├── Week2 ├── InformalLabExercises2-Template.ipynb ├── fig5-2.png ├── gauss.png ├── Fig5-1b.png ├── doubling.png ├── romberg.png ├── RecTrapSimp.png └── Lecture 2 Notebook Part1.ipynb ├── Week5 ├── desktop.ini ├── DIT-FFT-butterfly.png ├── InformalLabExercises5-Template.ipynb ├── FFT-WorkedExample.ipynb ├── pitch.txt └── L05-FourierTransforms.ipynb ├── Week4 ├── bisect.png ├── fig6-9.png ├── rescap.png ├── Cosine_fixed_point.png ├── NewtonIteration_Ani.gif └── Lab04Exercises-Template.ipynb ├── Week6 ├── fig8-2.png ├── fig8-9.png ├── fig8-10.png ├── L06-ExercisesTemplate.ipynb └── Simple-ODEs.ipynb ├── Week7 ├── fig9-2.png ├── Lab7ExercisesTemplate.ipynb └── SimplePDEs.ipynb ├── Formal Lab Report 3 ├── GraviteaTime.wav ├── PHY407_FormalLab3_Fall2024.pdf ├── times.txt └── lon.txt ├── Formal Lab Report 1 ├── PHY407_FormalLab1_F2024.pdf └── cdata.txt ├── Formal Lab Report 2 └── PHY407_FormalLab02_F2024.pdf ├── Formal Lab Report 4 └── PHY407_FormalReport4_F2024.pdf ├── README.md ├── Week0 ├── Informal0-Notebook.ipynb └── L00-PythonBasics.ipynb ├── Week1 ├── InformalExercises1-Template.ipynb └── Lecture1 Notebook.ipynb └── Week3 ├── InformalExercises3-Template.ipynb └── Lecture 4 Notebook - Derivatives.ipynb /Week2/InformalLabExercises2-Template.ipynb: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /Week5/desktop.ini: -------------------------------------------------------------------------------- 1 | [LocalizedFileNames] 2 | pitch.txt=@pitch.txt,0 3 | -------------------------------------------------------------------------------- /Week2/fig5-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mdiamon/PHY407-UofT/HEAD/Week2/fig5-2.png -------------------------------------------------------------------------------- /Week2/gauss.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mdiamon/PHY407-UofT/HEAD/Week2/gauss.png -------------------------------------------------------------------------------- /Week4/bisect.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mdiamon/PHY407-UofT/HEAD/Week4/bisect.png -------------------------------------------------------------------------------- /Week4/fig6-9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mdiamon/PHY407-UofT/HEAD/Week4/fig6-9.png -------------------------------------------------------------------------------- /Week4/rescap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mdiamon/PHY407-UofT/HEAD/Week4/rescap.png -------------------------------------------------------------------------------- /Week6/fig8-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mdiamon/PHY407-UofT/HEAD/Week6/fig8-2.png -------------------------------------------------------------------------------- /Week6/fig8-9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mdiamon/PHY407-UofT/HEAD/Week6/fig8-9.png -------------------------------------------------------------------------------- /Week7/fig9-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mdiamon/PHY407-UofT/HEAD/Week7/fig9-2.png -------------------------------------------------------------------------------- /Week2/Fig5-1b.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mdiamon/PHY407-UofT/HEAD/Week2/Fig5-1b.png -------------------------------------------------------------------------------- /Week2/doubling.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mdiamon/PHY407-UofT/HEAD/Week2/doubling.png -------------------------------------------------------------------------------- /Week2/romberg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mdiamon/PHY407-UofT/HEAD/Week2/romberg.png -------------------------------------------------------------------------------- /Week6/fig8-10.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mdiamon/PHY407-UofT/HEAD/Week6/fig8-10.png -------------------------------------------------------------------------------- /Week2/RecTrapSimp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mdiamon/PHY407-UofT/HEAD/Week2/RecTrapSimp.png -------------------------------------------------------------------------------- /Week4/Cosine_fixed_point.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mdiamon/PHY407-UofT/HEAD/Week4/Cosine_fixed_point.png -------------------------------------------------------------------------------- /Week4/NewtonIteration_Ani.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mdiamon/PHY407-UofT/HEAD/Week4/NewtonIteration_Ani.gif -------------------------------------------------------------------------------- /Week5/DIT-FFT-butterfly.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mdiamon/PHY407-UofT/HEAD/Week5/DIT-FFT-butterfly.png -------------------------------------------------------------------------------- /Formal Lab Report 3/GraviteaTime.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mdiamon/PHY407-UofT/HEAD/Formal Lab Report 3/GraviteaTime.wav -------------------------------------------------------------------------------- /Formal Lab Report 1/PHY407_FormalLab1_F2024.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mdiamon/PHY407-UofT/HEAD/Formal Lab Report 1/PHY407_FormalLab1_F2024.pdf -------------------------------------------------------------------------------- /Formal Lab Report 2/PHY407_FormalLab02_F2024.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mdiamon/PHY407-UofT/HEAD/Formal Lab Report 2/PHY407_FormalLab02_F2024.pdf -------------------------------------------------------------------------------- /Formal Lab Report 3/PHY407_FormalLab3_Fall2024.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mdiamon/PHY407-UofT/HEAD/Formal Lab Report 3/PHY407_FormalLab3_Fall2024.pdf -------------------------------------------------------------------------------- /Formal Lab Report 4/PHY407_FormalReport4_F2024.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mdiamon/PHY407-UofT/HEAD/Formal Lab Report 4/PHY407_FormalReport4_F2024.pdf -------------------------------------------------------------------------------- /Formal Lab Report 1/cdata.txt: -------------------------------------------------------------------------------- 1 | 299.85 2 | 299.74 3 | 299.9 4 | 300.07 5 | 299.93 6 | 299.85 7 | 299.95 8 | 299.98 9 | 299.98 10 | 299.88 11 | 300 12 | 299.98 13 | 299.93 14 | 299.65 15 | 299.76 16 | 299.81 17 | 300 18 | 300 19 | 299.96 20 | 299.96 21 | 299.96 22 | 299.94 23 | 299.96 24 | 299.94 25 | 299.88 26 | 299.8 27 | 299.85 28 | 299.88 29 | 299.9 30 | 299.84 31 | 299.83 32 | 299.79 33 | 299.81 34 | 299.88 35 | 299.88 36 | 299.83 37 | 299.8 38 | 299.79 39 | 299.76 40 | 299.8 41 | 299.88 42 | 299.88 43 | 299.88 44 | 299.86 45 | 299.72 46 | 299.72 47 | 299.62 48 | 299.86 49 | 299.97 50 | 299.95 51 | 299.88 52 | 299.91 53 | 299.85 54 | 299.87 55 | 299.84 56 | 299.84 57 | 299.85 58 | 299.84 59 | 299.84 60 | 299.84 61 | 299.89 62 | 299.81 63 | 299.81 64 | 299.82 65 | 299.8 66 | 299.77 67 | 299.76 68 | 299.74 69 | 299.75 70 | 299.76 71 | 299.91 72 | 299.92 73 | 299.89 74 | 299.86 75 | 299.88 76 | 299.72 77 | 299.84 78 | 299.85 79 | 299.85 80 | 299.78 81 | 299.89 82 | 299.84 83 | 299.78 84 | 299.81 85 | 299.76 86 | 299.81 87 | 299.79 88 | 299.81 89 | 299.82 90 | 299.85 91 | 299.87 92 | 299.87 93 | 299.81 94 | 299.74 95 | 299.81 96 | 299.94 97 | 299.95 98 | 299.8 99 | 299.81 100 | 299.87 101 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Instructions to use this repository with syzygy 2 | 3 | ## If first cloning into syzygy: 4 | 5 | 1. Go to https://utoronto.syzygy.ca, log in with your UTorID, and start the server. 6 | 2. Near the top-right-hand corner of the home menu, hit the drop-down menu "New", and click on Terminal. 7 | 3. in the terminal, create a PHY407 (or whatever) folder and go there: 8 | 9 | `mkdir PHY407` 10 | 11 | `cd PHY407` 12 | 13 | 4. In there, clone the repo: 14 | 15 | `git clone https://github.com/mdiamon/PHY407-UofT.git` 16 | 17 | This should create a new folder called `PHY407-UofT`. 18 | 19 | ## Refreshing the repo 20 | 21 | As the class progresses, I will add notes and code. 22 | 23 | 5. To get the latest updates, start by repeating steps 1-3 above. 24 | 6. If you just want to play around with the notes a little in-between two updates but want your repository to closely match what I have on GitHub, enter the command 25 | 26 | `git fetch --all` 27 | `git reset --hard origin` 28 | 29 | in the Terminal, where you left at the end of step 5, whenever you want to re-align your notes with mine. 30 | 31 | 7. If you want to be more fancy, modify the notebooks significantly and keep your modifications while keeping up-to-date with my content, I suggest you create your own "fork" and work on it. You can then sync your fork with my repository by following these instructions: https://help.github.com/en/articles/syncing-a-fork 32 | 33 | ## Opening a Jupyter notebook: 34 | 35 | 8. Repeat steps 1-3 above. 36 | 9. Repeat step 6 or 7 above, probably. 37 | 10. You can navigate to the Jupyter file, using the graphical interface of the home menu of syzygy. You are looking for a `.ipynb` file. 38 | -------------------------------------------------------------------------------- /Formal Lab Report 3/times.txt: -------------------------------------------------------------------------------- 1 | 0.000000000000000000e+00 2 | 1.000000000000000000e+00 3 | 2.000000000000000000e+00 4 | 3.000000000000000000e+00 5 | 4.000000000000000000e+00 6 | 5.000000000000000000e+00 7 | 6.000000000000000000e+00 8 | 7.000000000000000000e+00 9 | 8.000000000000000000e+00 10 | 9.000000000000000000e+00 11 | 1.000000000000000000e+01 12 | 1.100000000000000000e+01 13 | 1.200000000000000000e+01 14 | 1.300000000000000000e+01 15 | 1.400000000000000000e+01 16 | 1.500000000000000000e+01 17 | 1.600000000000000000e+01 18 | 1.700000000000000000e+01 19 | 1.800000000000000000e+01 20 | 1.900000000000000000e+01 21 | 2.000000000000000000e+01 22 | 2.100000000000000000e+01 23 | 2.200000000000000000e+01 24 | 2.300000000000000000e+01 25 | 2.400000000000000000e+01 26 | 2.500000000000000000e+01 27 | 2.600000000000000000e+01 28 | 2.700000000000000000e+01 29 | 2.800000000000000000e+01 30 | 2.900000000000000000e+01 31 | 3.000000000000000000e+01 32 | 3.100000000000000000e+01 33 | 3.200000000000000000e+01 34 | 3.300000000000000000e+01 35 | 3.400000000000000000e+01 36 | 3.500000000000000000e+01 37 | 3.600000000000000000e+01 38 | 3.700000000000000000e+01 39 | 3.800000000000000000e+01 40 | 3.900000000000000000e+01 41 | 4.000000000000000000e+01 42 | 4.100000000000000000e+01 43 | 4.200000000000000000e+01 44 | 4.300000000000000000e+01 45 | 4.400000000000000000e+01 46 | 4.500000000000000000e+01 47 | 4.600000000000000000e+01 48 | 4.700000000000000000e+01 49 | 4.800000000000000000e+01 50 | 4.900000000000000000e+01 51 | 5.000000000000000000e+01 52 | 5.100000000000000000e+01 53 | 5.200000000000000000e+01 54 | 5.300000000000000000e+01 55 | 5.400000000000000000e+01 56 | 5.500000000000000000e+01 57 | 5.600000000000000000e+01 58 | 5.700000000000000000e+01 59 | 5.800000000000000000e+01 60 | 5.900000000000000000e+01 61 | 6.000000000000000000e+01 62 | 6.100000000000000000e+01 63 | 6.200000000000000000e+01 64 | 6.300000000000000000e+01 65 | 6.400000000000000000e+01 66 | 6.500000000000000000e+01 67 | 6.600000000000000000e+01 68 | 6.700000000000000000e+01 69 | 6.800000000000000000e+01 70 | 6.900000000000000000e+01 71 | 7.000000000000000000e+01 72 | 7.100000000000000000e+01 73 | 7.200000000000000000e+01 74 | 7.300000000000000000e+01 75 | 7.400000000000000000e+01 76 | 7.500000000000000000e+01 77 | 7.600000000000000000e+01 78 | 7.700000000000000000e+01 79 | 7.800000000000000000e+01 80 | 7.900000000000000000e+01 81 | 8.000000000000000000e+01 82 | 8.100000000000000000e+01 83 | 8.200000000000000000e+01 84 | 8.300000000000000000e+01 85 | 8.400000000000000000e+01 86 | 8.500000000000000000e+01 87 | 8.600000000000000000e+01 88 | 8.700000000000000000e+01 89 | 8.800000000000000000e+01 90 | 8.900000000000000000e+01 91 | 9.000000000000000000e+01 92 | 9.100000000000000000e+01 93 | 9.200000000000000000e+01 94 | 9.300000000000000000e+01 95 | 9.400000000000000000e+01 96 | 9.500000000000000000e+01 97 | 9.600000000000000000e+01 98 | 9.700000000000000000e+01 99 | 9.800000000000000000e+01 100 | 9.900000000000000000e+01 101 | 1.000000000000000000e+02 102 | 1.010000000000000000e+02 103 | 1.020000000000000000e+02 104 | 1.030000000000000000e+02 105 | 1.040000000000000000e+02 106 | 1.050000000000000000e+02 107 | 1.060000000000000000e+02 108 | 1.070000000000000000e+02 109 | 1.080000000000000000e+02 110 | 1.090000000000000000e+02 111 | 1.100000000000000000e+02 112 | 1.110000000000000000e+02 113 | 1.120000000000000000e+02 114 | 1.130000000000000000e+02 115 | 1.140000000000000000e+02 116 | 1.150000000000000000e+02 117 | 1.160000000000000000e+02 118 | 1.170000000000000000e+02 119 | 1.180000000000000000e+02 120 | 1.190000000000000000e+02 121 | -------------------------------------------------------------------------------- /Formal Lab Report 3/lon.txt: -------------------------------------------------------------------------------- 1 | 0.000000000000000000e+00 2 | 2.500000000000000000e+00 3 | 5.000000000000000000e+00 4 | 7.500000000000000000e+00 5 | 1.000000000000000000e+01 6 | 1.250000000000000000e+01 7 | 1.500000000000000000e+01 8 | 1.750000000000000000e+01 9 | 2.000000000000000000e+01 10 | 2.250000000000000000e+01 11 | 2.500000000000000000e+01 12 | 2.750000000000000000e+01 13 | 3.000000000000000000e+01 14 | 3.250000000000000000e+01 15 | 3.500000000000000000e+01 16 | 3.750000000000000000e+01 17 | 4.000000000000000000e+01 18 | 4.250000000000000000e+01 19 | 4.500000000000000000e+01 20 | 4.750000000000000000e+01 21 | 5.000000000000000000e+01 22 | 5.250000000000000000e+01 23 | 5.500000000000000000e+01 24 | 5.750000000000000000e+01 25 | 6.000000000000000000e+01 26 | 6.250000000000000000e+01 27 | 6.500000000000000000e+01 28 | 6.750000000000000000e+01 29 | 7.000000000000000000e+01 30 | 7.250000000000000000e+01 31 | 7.500000000000000000e+01 32 | 7.750000000000000000e+01 33 | 8.000000000000000000e+01 34 | 8.250000000000000000e+01 35 | 8.500000000000000000e+01 36 | 8.750000000000000000e+01 37 | 9.000000000000000000e+01 38 | 9.250000000000000000e+01 39 | 9.500000000000000000e+01 40 | 9.750000000000000000e+01 41 | 1.000000000000000000e+02 42 | 1.025000000000000000e+02 43 | 1.050000000000000000e+02 44 | 1.075000000000000000e+02 45 | 1.100000000000000000e+02 46 | 1.125000000000000000e+02 47 | 1.150000000000000000e+02 48 | 1.175000000000000000e+02 49 | 1.200000000000000000e+02 50 | 1.225000000000000000e+02 51 | 1.250000000000000000e+02 52 | 1.275000000000000000e+02 53 | 1.300000000000000000e+02 54 | 1.325000000000000000e+02 55 | 1.350000000000000000e+02 56 | 1.375000000000000000e+02 57 | 1.400000000000000000e+02 58 | 1.425000000000000000e+02 59 | 1.450000000000000000e+02 60 | 1.475000000000000000e+02 61 | 1.500000000000000000e+02 62 | 1.525000000000000000e+02 63 | 1.550000000000000000e+02 64 | 1.575000000000000000e+02 65 | 1.600000000000000000e+02 66 | 1.625000000000000000e+02 67 | 1.650000000000000000e+02 68 | 1.675000000000000000e+02 69 | 1.700000000000000000e+02 70 | 1.725000000000000000e+02 71 | 1.750000000000000000e+02 72 | 1.775000000000000000e+02 73 | 1.800000000000000000e+02 74 | 1.825000000000000000e+02 75 | 1.850000000000000000e+02 76 | 1.875000000000000000e+02 77 | 1.900000000000000000e+02 78 | 1.925000000000000000e+02 79 | 1.950000000000000000e+02 80 | 1.975000000000000000e+02 81 | 2.000000000000000000e+02 82 | 2.025000000000000000e+02 83 | 2.050000000000000000e+02 84 | 2.075000000000000000e+02 85 | 2.100000000000000000e+02 86 | 2.125000000000000000e+02 87 | 2.150000000000000000e+02 88 | 2.175000000000000000e+02 89 | 2.200000000000000000e+02 90 | 2.225000000000000000e+02 91 | 2.250000000000000000e+02 92 | 2.275000000000000000e+02 93 | 2.300000000000000000e+02 94 | 2.325000000000000000e+02 95 | 2.350000000000000000e+02 96 | 2.375000000000000000e+02 97 | 2.400000000000000000e+02 98 | 2.425000000000000000e+02 99 | 2.450000000000000000e+02 100 | 2.475000000000000000e+02 101 | 2.500000000000000000e+02 102 | 2.525000000000000000e+02 103 | 2.550000000000000000e+02 104 | 2.575000000000000000e+02 105 | 2.600000000000000000e+02 106 | 2.625000000000000000e+02 107 | 2.650000000000000000e+02 108 | 2.675000000000000000e+02 109 | 2.700000000000000000e+02 110 | 2.725000000000000000e+02 111 | 2.750000000000000000e+02 112 | 2.775000000000000000e+02 113 | 2.800000000000000000e+02 114 | 2.825000000000000000e+02 115 | 2.850000000000000000e+02 116 | 2.875000000000000000e+02 117 | 2.900000000000000000e+02 118 | 2.925000000000000000e+02 119 | 2.950000000000000000e+02 120 | 2.975000000000000000e+02 121 | 3.000000000000000000e+02 122 | 3.025000000000000000e+02 123 | 3.050000000000000000e+02 124 | 3.075000000000000000e+02 125 | 3.100000000000000000e+02 126 | 3.125000000000000000e+02 127 | 3.150000000000000000e+02 128 | 3.175000000000000000e+02 129 | 3.200000000000000000e+02 130 | 3.225000000000000000e+02 131 | 3.250000000000000000e+02 132 | 3.275000000000000000e+02 133 | 3.300000000000000000e+02 134 | 3.325000000000000000e+02 135 | 3.350000000000000000e+02 136 | 3.375000000000000000e+02 137 | 3.400000000000000000e+02 138 | 3.425000000000000000e+02 139 | 3.450000000000000000e+02 140 | 3.475000000000000000e+02 141 | 3.500000000000000000e+02 142 | 3.525000000000000000e+02 143 | 3.550000000000000000e+02 144 | 3.575000000000000000e+02 145 | -------------------------------------------------------------------------------- /Week0/Informal0-Notebook.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": { 6 | "slideshow": { 7 | "slide_type": "skip" 8 | } 9 | }, 10 | "source": [ 11 | "*Supporting textbook chapters for week 1: 2, 3 and 4.3*" 12 | ] 13 | }, 14 | { 15 | "cell_type": "markdown", 16 | "metadata": { 17 | "slideshow": { 18 | "slide_type": "subslide" 19 | } 20 | }, 21 | "source": [ 22 | "# Example 1\n", 23 | "\n", 24 | "Suppose the problem is to find the number of radioactive atoms of uranium $N$ as a function of time given initial $N_0$ and\n", 25 | "$$\\frac{dN}{dt} = -\\frac{N}{\\tau}.$$\n", 26 | "($\\tau =$ decay constant), using numerical integration, for a duration of $5\\tau$." 27 | ] 28 | }, 29 | { 30 | "cell_type": "markdown", 31 | "metadata": { 32 | "slideshow": { 33 | "slide_type": "fragment" 34 | }, 35 | "tags": [] 36 | }, 37 | "source": [ 38 | "## Part (a) \n", 39 | "Write pseudocode for the solution, such that we end up with an array of values of time $t$ (between 0 and $N_0$) and an array of values of $N(t)$" 40 | ] 41 | }, 42 | { 43 | "cell_type": "markdown", 44 | "metadata": {}, 45 | "source": [ 46 | "## Part (b)\n", 47 | "Now write the actual code" 48 | ] 49 | }, 50 | { 51 | "cell_type": "markdown", 52 | "metadata": {}, 53 | "source": [ 54 | "## Part (c)\n", 55 | "Use the results of the previous part to make a plot of $N$ vs $t$, with the help of matplotlib.pyplot" 56 | ] 57 | }, 58 | { 59 | "cell_type": "markdown", 60 | "metadata": {}, 61 | "source": [ 62 | "# Example 2\n", 63 | "\n", 64 | "For a particle undergoing Simple Harmonic Oscillation with angular frequency 0.5 rad/s, initial position 3 m, and initial velocity 0 m/s, create a function to find its position as a function of time analytically. Then calculate the velocity and acceleration as a function of time, for 30 seconds, by taking derivatives numerically." 65 | ] 66 | }, 67 | { 68 | "cell_type": "markdown", 69 | "metadata": {}, 70 | "source": [ 71 | "## Part (a)\n", 72 | "Write the code for the analytical function" 73 | ] 74 | }, 75 | { 76 | "cell_type": "markdown", 77 | "metadata": {}, 78 | "source": [ 79 | "## Part (b)\n", 80 | "Write the pseuudocode for a program that gives us an array of values of time $t$ (between 0 and 30s), and 3 more arrays $x(t)$, $v(t)$, and $a(t)$" 81 | ] 82 | }, 83 | { 84 | "cell_type": "markdown", 85 | "metadata": {}, 86 | "source": [ 87 | "## Part (c)\n", 88 | "Now write the actual code for the previous part" 89 | ] 90 | }, 91 | { 92 | "cell_type": "markdown", 93 | "metadata": {}, 94 | "source": [ 95 | "## Part (d)\n", 96 | "Using the results of the previous part, make 3 plots: $x(t),v(t),a(t)$" 97 | ] 98 | }, 99 | { 100 | "cell_type": "markdown", 101 | "metadata": {}, 102 | "source": [ 103 | "## Example 3\n", 104 | "\n", 105 | "Read through Example 4.3 on pages 137-138 of the text, which show you that to multiply two matrices of size $O(N)$ on a side takes $O(N^3)$ operations.\n", 106 | "So multiplying matrices that are $N=1000$ on a side takes $O(10^9)$ operations (a ``GigaFLOP''). " 107 | ] 108 | }, 109 | { 110 | "cell_type": "markdown", 111 | "metadata": {}, 112 | "source": [ 113 | "## Part (a)\n", 114 | "Create two constant matrices $A$ and $B$, then time how long it takes to multiply the matrices to form a matrix $C$ (using the code fragment in the textbook), for a range of $N$ (from $N = 2$ to a few hundred). You should end up with an array of $N$ values and an array of time values. Hint: Use python's time module. " 115 | ] 116 | }, 117 | { 118 | "cell_type": "markdown", 119 | "metadata": {}, 120 | "source": [ 121 | "## Part (b)\n", 122 | "Using the results of the previous part, plot this time as a function of $N$ and as a function of $N^3$. What do you notice?" 123 | ] 124 | }, 125 | { 126 | "cell_type": "markdown", 127 | "metadata": {}, 128 | "source": [ 129 | "## Part (c)\n", 130 | "Compare your time results to the time it takes numpy.dot to carry out the same operation. What do you notice?\n", 131 | "See http://tinyurl.com/pythondot for more explanations." 132 | ] 133 | } 134 | ], 135 | "metadata": { 136 | "celltoolbar": "Slideshow", 137 | "kernelspec": { 138 | "display_name": "Python 3 (ipykernel)", 139 | "language": "python", 140 | "name": "python3" 141 | }, 142 | "language_info": { 143 | "codemirror_mode": { 144 | "name": "ipython", 145 | "version": 3 146 | }, 147 | "file_extension": ".py", 148 | "mimetype": "text/x-python", 149 | "name": "python", 150 | "nbconvert_exporter": "python", 151 | "pygments_lexer": "ipython3", 152 | "version": "3.11.9" 153 | }, 154 | "latex_metadata": { 155 | "affiliation": "PHY407, University of Toronto", 156 | "author": "Nico Grisouard", 157 | "title": "Lecture 1: python basics" 158 | } 159 | }, 160 | "nbformat": 4, 161 | "nbformat_minor": 4 162 | } 163 | -------------------------------------------------------------------------------- /Week1/InformalExercises1-Template.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "id": "a3ddacb7-c031-40cb-b93f-26a5eea72174", 6 | "metadata": {}, 7 | "source": [ 8 | "## 1. Numerical Error" 9 | ] 10 | }, 11 | { 12 | "cell_type": "markdown", 13 | "id": "ed4782c0-5d5e-4978-8330-31fed011a720", 14 | "metadata": {}, 15 | "source": [ 16 | "## Part (a)\n", 17 | "Write code to generate an array of length $N$, filled with random numbers from a Gaussian distribution (with mean $x$ and standard deviation $\\sigma$), and make a histogram (with $m$ bins) of the array values. (You are encouraged to import functions from numpy and matplotlib.pyplot) Test this code, performing at least two sanity checks to make sure it's working properly." 18 | ] 19 | }, 20 | { 21 | "cell_type": "markdown", 22 | "id": "5380753d-6433-4d15-8853-b620146f9d65", 23 | "metadata": {}, 24 | "source": [ 25 | "## Part (b)\n", 26 | "Now write a function to: generate two arrays as in the previous part (where the first Gaussian has $x_1,\\sigma_1$ and the second Gaussian has $x_2,\\sigma_2$), make a third array that is the element-wise sum of these two arrays, and make a histogram of the 'array values. The function should take $x_1,\\sigma_1, x_2, \\sigma_2$ as arguments." 27 | ] 28 | }, 29 | { 30 | "cell_type": "markdown", 31 | "id": "221b968d-e8fe-41b6-bbec-44e5750681b5", 32 | "metadata": {}, 33 | "source": [ 34 | "## Part (c)\n", 35 | "\n", 36 | "Recall from your physics labs, when you make repeated measurements of a quantity, the measurements follow a Gaussian distribution, where the mean (hopefully) represents the 'true' value of the quantity and the standard deviation represents the statistical uncertainty (error) on the measurement. \n", 37 | "If you make repeated measurements of two different quantities, and use these two quantities to calculate a third quantity, the error propagates, so you have to use error propagation formulas to figure out the uncertainty on the third quantity.\n", 38 | "\n", 39 | "Numerical errors in calculations propagate in a similar manner. We can represent the numerical error on stored value $x$ as $\\sigma$, and define our fractional error constant as $C$ such that $\\sigma = C |x|$.\n", 40 | "\n", 41 | "Plugging this into the previous part, we get $\\sigma_1 = C |x_1|, \\sigma_2 = C |x_2|$. Try this with: $N=10^6, m=100, C=10^{-14}, x_1 = 100, x_2 = -100$. (For the purposes of this exercise, we're using a much larger value of $C$ than the machine precision, so that we can see its effect.) Now try again with different mean values: $x_1 = 1.0, x_2 = -1.0$\n", 42 | "\n", 43 | "Hopefully, you now see visually why adding a positive number to a negative number, where both have large absolutely values, can produce large errors." 44 | ] 45 | }, 46 | { 47 | "cell_type": "markdown", 48 | "id": "4793fbbc-6e00-4728-ac93-53ac2ee40f71", 49 | "metadata": {}, 50 | "source": [ 51 | "# 2. Approximation Error" 52 | ] 53 | }, 54 | { 55 | "cell_type": "markdown", 56 | "id": "b05efffc-fe14-4a5e-a257-12f69f7122de", 57 | "metadata": { 58 | "slideshow": { 59 | "slide_type": "subslide" 60 | } 61 | }, 62 | "source": [ 63 | "Consider this system representing phasor rotation in the complex plane:\n", 64 | " $$\\dot Z = i\\omega Z, \\quad\\text{given}\\quad Z_0 = Z(t=0).$$\n", 65 | " \n", 66 | "The analytical solution is:\n", 67 | " $Z(t) = Z_0 \\exp(i\\omega t).$\n", 68 | "\n", 69 | "How can we solve it numerically? We could try using its Taylor expansion:\n", 70 | "$$\\dot Z(t) = \\frac{Z(t+\\Delta t)-Z(t)}{\\Delta t} + H.O.T. = i\\omega Z(t).$$\n", 71 | "And use a simple algorithm such as: \n", 72 | "* Start with $Z_0$ = $Z(t=0)=Z_{old}$,\n", 73 | "* $Z_{new} = (1+i\\omega \\Delta t)Z_{old}$,\n", 74 | "* repeat for a large number $n$ of timesteps until we complete a full rotation\n", 75 | "\n", 76 | "It turns out this simple algorithm is unstable, because of the accumulation of error." 77 | ] 78 | }, 79 | { 80 | "cell_type": "markdown", 81 | "id": "47cfb73c-d212-4f27-a113-1f80b452e009", 82 | "metadata": {}, 83 | "source": [ 84 | "## Part (a)\n", 85 | "Write the code to implement the above algorithm, with $\\omega, Z_0, n$ as parameters that can be set in the code. You should end up with an array of $t$ values and an array of $Z$ values covering a full rotation.\n", 86 | "\n", 87 | "You may want to make use of numpy.pi" 88 | ] 89 | }, 90 | { 91 | "cell_type": "markdown", 92 | "id": "1bd389de-5c8d-42ab-b9e1-619f0d6a786f", 93 | "metadata": {}, 94 | "source": [ 95 | "## Part (b)\n", 96 | "Using the code in the previous part, plot $|Z|(t)$ (absolute value of $Z$, as a function of $t$) for $n=200,Z_0=1,\\omega=1$Hz . Is the result what you expected? Why?" 97 | ] 98 | }, 99 | { 100 | "cell_type": "code", 101 | "execution_count": null, 102 | "id": "c4b92133-296d-47c7-8bfc-fda0c3d2f854", 103 | "metadata": {}, 104 | "outputs": [], 105 | "source": [] 106 | } 107 | ], 108 | "metadata": { 109 | "kernelspec": { 110 | "display_name": "Python 3 (ipykernel)", 111 | "language": "python", 112 | "name": "python3" 113 | }, 114 | "language_info": { 115 | "codemirror_mode": { 116 | "name": "ipython", 117 | "version": 3 118 | }, 119 | "file_extension": ".py", 120 | "mimetype": "text/x-python", 121 | "name": "python", 122 | "nbconvert_exporter": "python", 123 | "pygments_lexer": "ipython3", 124 | "version": "3.11.9" 125 | } 126 | }, 127 | "nbformat": 4, 128 | "nbformat_minor": 5 129 | } 130 | -------------------------------------------------------------------------------- /Week6/L06-ExercisesTemplate.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "id": "098ad455-0a0c-4770-b84d-25bc951d3466", 6 | "metadata": {}, 7 | "source": [ 8 | "# Comparing methods for a simple ODE" 9 | ] 10 | }, 11 | { 12 | "cell_type": "markdown", 13 | "id": "3dff43de-a9b4-4446-8c42-22dcc73112f3", 14 | "metadata": {}, 15 | "source": [ 16 | "## Exercise 1" 17 | ] 18 | }, 19 | { 20 | "cell_type": "markdown", 21 | "id": "91538571-0c7b-4503-820c-538ec76813f5", 22 | "metadata": { 23 | "slideshow": { 24 | "slide_type": "slide" 25 | } 26 | }, 27 | "source": [ 28 | "Use Euler's Method to solve for $x(t)$ given\n", 29 | "$$ \\frac{\\text d x}{\\text d t} = -x^3(t) + \\sin(t) $$\n", 30 | "from 0 to 10 seconds, with initial condition $x(t=0) = 0$.\n", 31 | "\n", 32 | "Try with 20 time-steps, and again with 1000 time-steps. Plot the results, on the same graph." 33 | ] 34 | }, 35 | { 36 | "cell_type": "markdown", 37 | "id": "f4ddfb90-1e81-4e64-b385-3aa6d605eb7b", 38 | "metadata": {}, 39 | "source": [ 40 | "## Exercise 2" 41 | ] 42 | }, 43 | { 44 | "cell_type": "markdown", 45 | "id": "ce56b40e-f115-4645-a600-96430a885b70", 46 | "metadata": {}, 47 | "source": [ 48 | "Repeat Exercise 1 using RK2." 49 | ] 50 | }, 51 | { 52 | "cell_type": "markdown", 53 | "id": "d3f07efc-15cf-48dd-b119-baee7bc60608", 54 | "metadata": { 55 | "tags": [] 56 | }, 57 | "source": [ 58 | "## Exercise 3" 59 | ] 60 | }, 61 | { 62 | "cell_type": "markdown", 63 | "id": "54eb163d-e205-49e6-a00e-5d4dcedd6082", 64 | "metadata": {}, 65 | "source": [ 66 | "Repeat Exercise 1 using RK4." 67 | ] 68 | }, 69 | { 70 | "cell_type": "markdown", 71 | "id": "c7562c37-0a7a-451c-b4e6-b74acdf436f2", 72 | "metadata": { 73 | "tags": [] 74 | }, 75 | "source": [ 76 | "## Exercise 4" 77 | ] 78 | }, 79 | { 80 | "cell_type": "markdown", 81 | "id": "7d52a6f2-5707-4fb0-91ba-0f4546510299", 82 | "metadata": { 83 | "tags": [] 84 | }, 85 | "source": [ 86 | "Repeat Exercise 1 using Bulirsch-Stoer, with error tolerance 1e-08. You may copy-and-paste code from the textbook's 'bulirsch.py' to help you." 87 | ] 88 | }, 89 | { 90 | "cell_type": "markdown", 91 | "id": "7c751cbc-9859-4336-b3fb-03a4c8f473ce", 92 | "metadata": { 93 | "tags": [] 94 | }, 95 | "source": [ 96 | "## Exercise 5" 97 | ] 98 | }, 99 | { 100 | "cell_type": "markdown", 101 | "id": "add76a6f-e35f-4e50-8c0f-b0c11a719e11", 102 | "metadata": {}, 103 | "source": [ 104 | "Repeat Exercise 1 using scipy.integrate.odeint" 105 | ] 106 | }, 107 | { 108 | "cell_type": "markdown", 109 | "id": "986b07a1-1a2a-406d-a44d-728a6d2e54f9", 110 | "metadata": { 111 | "tags": [] 112 | }, 113 | "source": [ 114 | "## Exercise 6" 115 | ] 116 | }, 117 | { 118 | "cell_type": "markdown", 119 | "id": "5828add8-0576-48ea-b629-faea055fc984", 120 | "metadata": {}, 121 | "source": [ 122 | "Plot your Exercise 1 through 5 results for $N=20$, on the same graph.\n", 123 | "\n", 124 | "Plot your Exercise 1 through 5 results for $N=1000$, on the same graph. \n", 125 | "\n", 126 | "(So you should have 2 graphs for this exercise.)" 127 | ] 128 | }, 129 | { 130 | "cell_type": "markdown", 131 | "id": "9525cfa1-b68a-46b4-a7aa-b150959277ad", 132 | "metadata": {}, 133 | "source": [ 134 | "# Stability of ODE Solutions" 135 | ] 136 | }, 137 | { 138 | "cell_type": "markdown", 139 | "id": "9403037b-c622-4909-8f5d-05bbf2265eff", 140 | "metadata": { 141 | "tags": [] 142 | }, 143 | "source": [ 144 | "* We have focused on accuracy and speed in investigating our solutions to ODEs.\n", 145 | "* But stability is also important!\n", 146 | "* The stability of solutions tells us how fast initially close solutions diverge from each other.\n", 147 | "* In other words, a stable solution tends to a finite number.\n", 148 | "* Some systems are inherently unstable and so will always be challenging to simulate. Physical stability or instability of a system can be determined from small perturbations to a solution of the ODE.\n", 149 | "* But even for physically stable systems, numerical methods can be unstable (i.e. give approximation and roundoff errors that grow)." 150 | ] 151 | }, 152 | { 153 | "cell_type": "markdown", 154 | "id": "360bd869-72d0-47ea-8bdd-2e76bb8edc34", 155 | "metadata": {}, 156 | "source": [ 157 | "## Exercise 7" 158 | ] 159 | }, 160 | { 161 | "cell_type": "markdown", 162 | "id": "992e0888-d0e9-4155-9e41-5fdb1e485dab", 163 | "metadata": { 164 | "slideshow": { 165 | "slide_type": "subslide" 166 | } 167 | }, 168 | "source": [ 169 | "Consider: $y'(t) = -2.3y(t), y(t=0) = 1$\n", 170 | "\n", 171 | "The analytical solution is:\n", 172 | "$y(t) = \\exp (-2.3 t)$ . This is a stable solution, i.e. it tends to a finite number: $y \\rightarrow 0$ as $t \\rightarrow \\infty$\n", 173 | "\n", 174 | "Demonstrate (by making 2 plots) that computationally, the Euler method for the interval $0 < t < 20$ is stable for $h=0.7$ but unstable for $h=1$." 175 | ] 176 | }, 177 | { 178 | "cell_type": "code", 179 | "execution_count": null, 180 | "id": "1581420b-d3f9-4859-99e6-4c6effcae150", 181 | "metadata": {}, 182 | "outputs": [], 183 | "source": [] 184 | } 185 | ], 186 | "metadata": { 187 | "kernelspec": { 188 | "display_name": "Python 3 (ipykernel)", 189 | "language": "python", 190 | "name": "python3" 191 | }, 192 | "language_info": { 193 | "codemirror_mode": { 194 | "name": "ipython", 195 | "version": 3 196 | }, 197 | "file_extension": ".py", 198 | "mimetype": "text/x-python", 199 | "name": "python", 200 | "nbconvert_exporter": "python", 201 | "pygments_lexer": "ipython3", 202 | "version": "3.10.9" 203 | } 204 | }, 205 | "nbformat": 4, 206 | "nbformat_minor": 5 207 | } 208 | -------------------------------------------------------------------------------- /Week3/InformalExercises3-Template.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# 1. Bessel Functions" 8 | ] 9 | }, 10 | { 11 | "cell_type": "markdown", 12 | "metadata": {}, 13 | "source": [ 14 | "These appear in various physics problems, and are given by:\n", 15 | "$$J_m(x) = \\frac{1}{\\pi} \\int_0^\\pi \\cos(m \\theta - x \\sin \\theta) d\\theta $$\n", 16 | "where $m$ is a whole number and $x$ is a non-negative real number." 17 | ] 18 | }, 19 | { 20 | "cell_type": "markdown", 21 | "metadata": {}, 22 | "source": [ 23 | "## Part (a) \n", 24 | "\n", 25 | "Write pseudocode for your own Python function that calculates the value of the Bessel function\n", 26 | "$J_m(x)$, taking $m$ and $x$ as inputs, using Gaussian Quadrature with $N$ points. $N$ is a parameter that you set in your code. You can use the gaussxw and gaussxwab functions from the previous lab exercises." 27 | ] 28 | }, 29 | { 30 | "cell_type": "markdown", 31 | "metadata": {}, 32 | "source": [ 33 | "## Part (b)\n", 34 | "Now write the actual code." 35 | ] 36 | }, 37 | { 38 | "cell_type": "markdown", 39 | "metadata": {}, 40 | "source": [ 41 | "## Part (c)\n", 42 | "Use the code from the previous part to make a plot, on a single graph, of the Bessel\n", 43 | "functions $J_0, J_1, J_2$ as a function of $x$ from $x = 0$ to $x = 20$." 44 | ] 45 | }, 46 | { 47 | "cell_type": "markdown", 48 | "metadata": {}, 49 | "source": [ 50 | "## Part (d)\n", 51 | "scipy.special includes some special functions that are a little too exotic to be part of NumPy. One of such special function is the Bessel function, called jv . Compare graphically the difference between the results of your Bessel function from the previous part, and the results of the SciPy version." 52 | ] 53 | }, 54 | { 55 | "cell_type": "code", 56 | "execution_count": 4, 57 | "metadata": {}, 58 | "outputs": [], 59 | "source": [ 60 | "from scipy.special import jv" 61 | ] 62 | }, 63 | { 64 | "cell_type": "markdown", 65 | "metadata": {}, 66 | "source": [ 67 | "# 2. Black Body Radiation" 68 | ] 69 | }, 70 | { 71 | "cell_type": "markdown", 72 | "metadata": { 73 | "jp-MarkdownHeadingCollapsed": true 74 | }, 75 | "source": [ 76 | "The black body function can be written as a function of wavenumber $\\nu$ and temperature $T$, using the Planck constant $h$, the speed of light $c$ and the Boltzmann constant $k$:\n", 77 | "$$B = \\frac{2h\\nu^3}{c^2(\\exp{\\frac{h\\nu}{kT}}-1)}$$\n", 78 | "The total energy per unit area emitted by a black body is then:\n", 79 | "$$W = \\pi\\int_0^\\infty B d\\nu $$\n", 80 | "It follows Stefan's law:\n", 81 | "$W = \\sigma T^4$,\n", 82 | "where $\\sigma$ is the Stefan-Boltzmann constant. Therefore\n", 83 | "$$\\sigma = \\frac{\\pi\\int_0^\\infty B d\\nu}{T^4} $$\n", 84 | "(Of course, you remember all this if I was your prof for PHY252.)" 85 | ] 86 | }, 87 | { 88 | "cell_type": "markdown", 89 | "metadata": {}, 90 | "source": [ 91 | "Equivalently, using the change of variables $x = \\frac{h \\nu}{kT}$:\n", 92 | "$$\\sigma = C_1 \\int_0^\\infty\\frac{x^3}{e^x-1} dx$$\n", 93 | "with $C_1 = \\frac{2 \\pi k^4}{c^2 h^3}$" 94 | ] 95 | }, 96 | { 97 | "cell_type": "markdown", 98 | "metadata": {}, 99 | "source": [ 100 | "We can convert the limits of integration to [0,1] by another variable transform\n", 101 | "$ z = \\frac{x}{1+x}$:\n", 102 | "$$\\int_0^\\infty f(x)dx = \\int_0^1 \\frac{1}{(1-z)^2}f\\left(\\frac{z}{1-z}\\right)dz = \\int_0^1 g(z) dz$$\n", 103 | "We want to perform a numerical integral over the domain [0,1] in order to calculate $\\sigma$. " 104 | ] 105 | }, 106 | { 107 | "cell_type": "markdown", 108 | "metadata": {}, 109 | "source": [ 110 | "## Part (a)\n", 111 | "Write code for the functions x(z), f(x), and g(z). " 112 | ] 113 | }, 114 | { 115 | "cell_type": "markdown", 116 | "metadata": {}, 117 | "source": [ 118 | "## Part (b)\n", 119 | "Incorporating your functions from the previous part, write code to calculate $$\\int_0^\\infty\\frac{x^3}{e^x-1} dx$$ using Gaussian quadrature. Figure out for yourself what value of $N$ you need, to obtain an accuracy of about 5 digits." 120 | ] 121 | }, 122 | { 123 | "cell_type": "markdown", 124 | "metadata": {}, 125 | "source": [ 126 | "## Part (c)\n", 127 | "Using the code from the previous part, calculate a value for the Stefan-Boltzmann constant in SI units, to three significant figures or more. Compare it to the value from NIST that is included in the scipy.constants package (https://docs.scipy.org/doc/scipy/reference/constants.html)" 128 | ] 129 | }, 130 | { 131 | "cell_type": "code", 132 | "execution_count": 5, 133 | "metadata": {}, 134 | "outputs": [], 135 | "source": [ 136 | "from scipy.constants import Stefan_Boltzmann" 137 | ] 138 | }, 139 | { 140 | "cell_type": "markdown", 141 | "metadata": {}, 142 | "source": [ 143 | "Note, the Scipy value is taken from the NIST database, and includes an uncertainty in the measurement, so it is the currently accepted value. Our integral doesn't need to be machine precision level because the actual constant is only known to 8 digits." 144 | ] 145 | }, 146 | { 147 | "cell_type": "markdown", 148 | "metadata": { 149 | "tags": [] 150 | }, 151 | "source": [ 152 | "# Fun with interpolation" 153 | ] 154 | }, 155 | { 156 | "cell_type": "markdown", 157 | "metadata": {}, 158 | "source": [ 159 | "Here, I perform interpolation for the function $$y = \\frac{1}{2+x^2}$$" 160 | ] 161 | }, 162 | { 163 | "cell_type": "code", 164 | "execution_count": null, 165 | "metadata": { 166 | "tags": [] 167 | }, 168 | "outputs": [], 169 | "source": [ 170 | "from scipy.interpolate import CubicSpline, interp1d\n", 171 | "import matplotlib.pyplot as pltm\n", 172 | "\n", 173 | "# fake data to interpolate from\n", 174 | "x = np.arange(-10,10)\n", 175 | "y = 1./(2.+x**2)\n", 176 | "\n", 177 | "# points at which we want to interpolate\n", 178 | "xs = np.arange(-9, 9, 0.1)\n", 179 | "\n", 180 | "# Apply Linear interpolation\n", 181 | "linear_int = interp1d(x,y)\n", 182 | "ys_lin = linear_int(xs)\n", 183 | "\n", 184 | "# apply cubic spline\n", 185 | "cs = CubicSpline(x, y)\n", 186 | "ys_cub = cs(xs)\n", 187 | " \n", 188 | "# plot linear interpolation\n", 189 | "pltm.plot(xs, ys_lin, 'o', label='linear')\n", 190 | "pltm.plot(xs, ys_cub, 'o', label='cubicspline')\n", 191 | "pltm.plot(x, y, '*', label='data')\n", 192 | "pltm.legend()\n", 193 | "pltm.show()" 194 | ] 195 | }, 196 | { 197 | "cell_type": "markdown", 198 | "metadata": {}, 199 | "source": [ 200 | "## Exercise 6" 201 | ] 202 | }, 203 | { 204 | "cell_type": "markdown", 205 | "metadata": {}, 206 | "source": [ 207 | "Replace the definition of y in the code above, to interpolate the following function:\n", 208 | "$$y = \\frac{1}{1+(2x)^2}$$" 209 | ] 210 | }, 211 | { 212 | "cell_type": "markdown", 213 | "metadata": { 214 | "tags": [] 215 | }, 216 | "source": [ 217 | "Show the plot. Is the cubic spline still nicer than the linear interpolation?" 218 | ] 219 | } 220 | ], 221 | "metadata": { 222 | "kernelspec": { 223 | "display_name": "Python 3 (ipykernel)", 224 | "language": "python", 225 | "name": "python3" 226 | }, 227 | "language_info": { 228 | "codemirror_mode": { 229 | "name": "ipython", 230 | "version": 3 231 | }, 232 | "file_extension": ".py", 233 | "mimetype": "text/x-python", 234 | "name": "python", 235 | "nbconvert_exporter": "python", 236 | "pygments_lexer": "ipython3", 237 | "version": "3.11.9" 238 | } 239 | }, 240 | "nbformat": 4, 241 | "nbformat_minor": 4 242 | } 243 | -------------------------------------------------------------------------------- /Week0/L00-PythonBasics.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": { 6 | "slideshow": { 7 | "slide_type": "skip" 8 | } 9 | }, 10 | "source": [ 11 | "*Supporting textbook chapters for week 1: 2, 3 and 4.3*\n", 12 | "\n", 13 | "This is an example of \"lecture notes\".\n", 14 | "As you will quickly find out, this course is by nature a lab course.\n", 15 | "Therefore, my \"lecture notes\" will not often follow the linear progression of regular lecture notes.\n", 16 | "This is particularly true for this first lecture, in which I merely want to give you pointers to do the first lab." 17 | ] 18 | }, 19 | { 20 | "cell_type": "markdown", 21 | "metadata": { 22 | "slideshow": { 23 | "slide_type": "subslide" 24 | } 25 | }, 26 | "source": [ 27 | "# General Problem-Solving Approach\n", 28 | "\n", 29 | "1. **Math model:** often, but not always, continuous.\n", 30 | "2. **Translate and discretize:** set up discrete arrays of independent variables (e.g., $x$, $t$), dependent variables (e.g. $v(t)$, $a(t)$), and define operators on these variables ($dv/dt$, $ma$...) according to the model.\n", 31 | "3. **Initialize** parameters and variables appropriately. \n", 32 | "4. **Evaluate:** run algorithms (using the operators) on these variables. \n", 33 | "5. **Debug:** bang your head against the wall for awhile to figure out why it didn't work properly, fix it, repeat as many times as necessary.\n", 34 | "6. **Analyze:** some extra processing of the raw results (figures, etc.)\n", 35 | "7. **Sanity check:** ask yourself whether your analysis outputs make sense. If not, go back to step 5." 36 | ] 37 | }, 38 | { 39 | "cell_type": "markdown", 40 | "metadata": {}, 41 | "source": [ 42 | "# Coding Tips" 43 | ] 44 | }, 45 | { 46 | "cell_type": "markdown", 47 | "metadata": {}, 48 | "source": [ 49 | "**Modularity** is the concept of breaking up your code into pieces that are as independent as possible from each other. That way, if anything goes wrong, you can test each piece independently.\n", 50 | "\n", 51 | "- Python scripts: define external functions for repetitive tasks. Place them in a separate file (called e.g. MyFunctions.py) in the same folder as your main code file. In your main file, import MyFunctions, and call and use the external functions.\n", 52 | "\n", 53 | "- Notebooks: define functions in dedicated cells. Run them before you run the cells containing your main code." 54 | ] 55 | }, 56 | { 57 | "cell_type": "markdown", 58 | "metadata": {}, 59 | "source": [ 60 | "**Give your parameters and variables meaningful names**\n", 61 | "- reserve i, j, k, etc. for loop counters\n", 62 | "- careful about capitalization\n", 63 | "\n", 64 | "**Avoid hard-coding** parameter values. It should be easy to find and set these values somewhere near the top of your code. \n", 65 | "\n", 66 | "**Test your code as you go**, not just when it is finished.\n", 67 | "\n", 68 | "**Debugging**: \n", 69 | "- carefully read any error messages produced\n", 70 | "- add print statements to see intermediate values of variables\n", 71 | "- before you try looping with several iterations, try just one iteration\n", 72 | "- you may want to use a built-in debugger in an IDE\n", 73 | "- just because the code runs without errors, doesn't mean it's bug-free. Remember, there's almost always another bug sitting somewhere in your code.\n", 74 | "\n", 75 | "**Sanity check**: \n", 76 | "- are all output arrays the length you expected? do all the plots have the number of points (or histograms the number of entries) you expected?\n", 77 | "- are all output values the correct order of magnitude? \n", 78 | "- are all curves in your plots the expected shapes?\n", 79 | "- if possible, check the result (maybe for a simple case) analytically or by-hand or using a different computational method" 80 | ] 81 | }, 82 | { 83 | "cell_type": "markdown", 84 | "metadata": { 85 | "slideshow": { 86 | "slide_type": "slide" 87 | }, 88 | "tags": [] 89 | }, 90 | "source": [ 91 | "# Pseudocode and Comments" 92 | ] 93 | }, 94 | { 95 | "cell_type": "markdown", 96 | "metadata": { 97 | "slideshow": { 98 | "slide_type": "skip" 99 | } 100 | }, 101 | "source": [ 102 | "The concept of \"Pseudocode\" is loosely defined, see for example https://en.wikipedia.org/wiki/Pseudocode. In this lecture, I will use a **very** loose definition for it, i.e., mostly plain English with bullet points.\n", 103 | "You are free to use your own version. Just make sure that it is understandable by someone who speaks English and is vaguely familiar with the problem you're trying to solve." 104 | ] 105 | }, 106 | { 107 | "cell_type": "markdown", 108 | "metadata": { 109 | "slideshow": { 110 | "slide_type": "fragment" 111 | } 112 | }, 113 | "source": [ 114 | "* Pseudocode is the planned version of your code. It describes your algorithm(s).\n", 115 | "* Writing pseudocode helps ensure that your planned logic for the algorithm is sound. Therefore you should write pseudocode before starting any actual code.\n", 116 | "* Pseudocode should be concise, logical, step-by-step.\n", 117 | "* Coding = turning your pseudocode $\\to$ specific programming language. You should be able to take your pseudocode and convert it into any typical programming language.\n", 118 | "* You may distribute your pseudocode throughout your real code, in the form of **comments**, as you go through the process of coding. Also **keep a copy of the pseudocode intact** so you can refer back to it, and ensure each block of code starts with a brief overview of what the block will do." 119 | ] 120 | }, 121 | { 122 | "cell_type": "markdown", 123 | "metadata": { 124 | "slideshow": { 125 | "slide_type": "subslide" 126 | } 127 | }, 128 | "source": [ 129 | "Examples for sequential stuff:\n", 130 | "* Input: `READ`, `OBTAIN`, `GET`\n", 131 | "* Initialize: `SET`, `DEFINE`\n", 132 | "* Compute: `COMPUTE`, `CALCULATE`, `DETERMINE`\n", 133 | "* Add one: `INCREMENT`, `BUMP`\n", 134 | "* Output: `PRINT`, `DISPLAY`, `PLOT`, `WRITE`" 135 | ] 136 | }, 137 | { 138 | "cell_type": "markdown", 139 | "metadata": { 140 | "slideshow": { 141 | "slide_type": "subslide" 142 | } 143 | }, 144 | "source": [ 145 | "Examples for conditions and loops:\n", 146 | "* `WHILE`, `IF-THEN-ELSE`, `REPEAT-UNTIL`, `CASE`, `FOR`\n", 147 | "\n", 148 | "Should also include calling functions:\n", 149 | "* `CALL`" 150 | ] 151 | }, 152 | { 153 | "cell_type": "markdown", 154 | "metadata": {}, 155 | "source": [ 156 | "# Learning (or Reviewing) the Basics" 157 | ] 158 | }, 159 | { 160 | "cell_type": "markdown", 161 | "metadata": {}, 162 | "source": [ 163 | "Ensure that you take the time this week to learn (or review) the material in Chapters 2 and 3 as this will be expected knowledge for all the future labs.\n", 164 | "Particularly useful review material includes:\n", 165 | "- Assigning variables: Sections 2.1, 2.2.1\n", 166 | "- Mathematical operations: Section 2.2.4\n", 167 | "- Loops: Sections 2.3 and 2.5\n", 168 | "- Lists and Arrays: Section 2.4\n", 169 | "- User defined functions: Section 2.6\n", 170 | "- Making basic graphs: Section 3.1" 171 | ] 172 | }, 173 | { 174 | "cell_type": "code", 175 | "execution_count": null, 176 | "metadata": {}, 177 | "outputs": [], 178 | "source": [] 179 | } 180 | ], 181 | "metadata": { 182 | "celltoolbar": "Slideshow", 183 | "kernelspec": { 184 | "display_name": "Python 3 (ipykernel)", 185 | "language": "python", 186 | "name": "python3" 187 | }, 188 | "language_info": { 189 | "codemirror_mode": { 190 | "name": "ipython", 191 | "version": 3 192 | }, 193 | "file_extension": ".py", 194 | "mimetype": "text/x-python", 195 | "name": "python", 196 | "nbconvert_exporter": "python", 197 | "pygments_lexer": "ipython3", 198 | "version": "3.10.9" 199 | }, 200 | "latex_metadata": { 201 | "affiliation": "PHY407, University of Toronto", 202 | "author": "Nico Grisouard", 203 | "title": "Lecture 1: python basics" 204 | } 205 | }, 206 | "nbformat": 4, 207 | "nbformat_minor": 4 208 | } 209 | -------------------------------------------------------------------------------- /Week3/Lecture 4 Notebook - Derivatives.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": { 6 | "slideshow": { 7 | "slide_type": "slide" 8 | } 9 | }, 10 | "source": [ 11 | "*Supporting textbook chapters: 5.10, 5.11*" 12 | ] 13 | }, 14 | { 15 | "cell_type": "markdown", 16 | "metadata": { 17 | "tags": [] 18 | }, 19 | "source": [ 20 | "# Numerical derivatives" 21 | ] 22 | }, 23 | { 24 | "cell_type": "markdown", 25 | "metadata": { 26 | "slideshow": { 27 | "slide_type": "slide" 28 | } 29 | }, 30 | "source": [ 31 | "* Simpler than numerical integration, in a way.\n", 32 | "* Computing errors is usually a doozey though.\n", 33 | "* Based on Taylor series approximations.\n", 34 | "\n", 35 | "\n", 36 | "1. Forward difference approximation: $\\displaystyle f'(x) \\approx \\frac{f(x+h) - f(x)}{h}$,\n", 37 | "2. Backward difference approximation: $\\displaystyle f'(x) \\approx \\frac{f(x) - f(x-h)}{h}$,\n" 38 | ] 39 | }, 40 | { 41 | "cell_type": "markdown", 42 | "metadata": { 43 | "slideshow": { 44 | "slide_type": "subslide" 45 | } 46 | }, 47 | "source": [ 48 | "## Estimation of approximation error\n", 49 | "\n", 50 | "Use Taylor series to find error in these approximations:\n", 51 | "$$ f(x+h) = f(x) + hf'(x) + \\frac{h^2}{2}f''(x) + h.o.t.$$\n", 52 | "Isolate for $f'(x)$:\n", 53 | "$$f'(x) = \\frac{f(x+h) - f(x)}{h} - \\frac{h}2 f''(x) + h.o.t.$$\n", 54 | "$\\Rightarrow$ error is 1st-order in $h$ (same is true for backward difference method)." 55 | ] 56 | }, 57 | { 58 | "cell_type": "markdown", 59 | "metadata": { 60 | "slideshow": { 61 | "slide_type": "subslide" 62 | } 63 | }, 64 | "source": [ 65 | "## Central differences\n", 66 | "\n", 67 | "* Using Taylor series to find sneaky improvements to finite difference (FD) schemes.\n", 68 | "* Example: central FD method:\n", 69 | "$$f'(x) \\approx \\frac{f(x+h) - f(x-h)}{2h}.$$\n", 70 | "* Notice it still only involves subtracting 2 points, it's just that the location of the 2 points is different." 71 | ] 72 | }, 73 | { 74 | "cell_type": "markdown", 75 | "metadata": { 76 | "slideshow": { 77 | "slide_type": "subslide" 78 | } 79 | }, 80 | "source": [ 81 | "* Error:\n", 82 | "$$f(x+h) = f(x) + hf'(x) + \\frac{h^2}{2}f''(x) + \\frac{h^3}{6}f'''(x) + h.o.t.$$\n", 83 | "$$f(x-h) = f(x) - hf'(x) + \\frac{h^2}{2}f''(x) - \\frac{h^3}{6}f'''(x) + h.o.t.$$" 84 | ] 85 | }, 86 | { 87 | "cell_type": "markdown", 88 | "metadata": {}, 89 | "source": [ 90 | "* Subtract:\n", 91 | "$$ f(x+h) - f(x-h) = 2hf'(x) + \\frac{h^3}{3}f'''(x) + h.o.t.$$\n", 92 | "* Isolate for $f'(x)$ and add:\n", 93 | "$$f'(x) = \\frac{f(x+h) - f(x-h)}{2h} - \\boxed{\\frac{h^2}{3}f'''(x)} + h.o.t.$$" 94 | ] 95 | }, 96 | { 97 | "cell_type": "markdown", 98 | "metadata": { 99 | "slideshow": { 100 | "slide_type": "subslide" 101 | } 102 | }, 103 | "source": [ 104 | "* So we see that this formula is accurate to 2nd order in $h$.\n", 105 | "* Can get higher-order methods by including more points (see table 5.1 on page 196).\n", 106 | "* Might have to do different things near the boundaries\n", 107 | "* Partial derivatives: similar techniques\n", 108 | "* Higher order derivatives (e.g. $f''$): also similar techniques." 109 | ] 110 | }, 111 | { 112 | "cell_type": "markdown", 113 | "metadata": { 114 | "slideshow": { 115 | "slide_type": "subslide" 116 | } 117 | }, 118 | "source": [ 119 | "## Roundoff error\n", 120 | "\n", 121 | "* Let’s take another look at this formula:\n", 122 | "$$f(x+h) = f(x) + hf'(x) + \\frac{h^2}{2}f''(x) + h.o.t.$$\n", 123 | "* What happens when we consider roundoff error? Recall that subtracting numbers that are close to each other in value, and yield an answer close to 0, is dangerous!\n", 124 | "* Each of the terms $f(x+h)$ and $f(x)$ have error $\\sim C|f(x)|$. Their difference will have approximate error $2Cf(x)$ (\"worst case\" error).\n", 125 | "* So in fact there are two sources of error and this leads to (eqn. (5.91) in book):\n", 126 | "$$\\epsilon = \\underbrace{\\frac{2C|f(x)|}{h}}_{\\text{round-off error}} + \\underbrace{\\frac{1}{2}h |f''(x)| + h.o.t.}_{\\text{approximation error}}$$" 127 | ] 128 | }, 129 | { 130 | "cell_type": "markdown", 131 | "metadata": { 132 | "slideshow": { 133 | "slide_type": "subslide" 134 | } 135 | }, 136 | "source": [ 137 | "* There's a limit to the improvement you can obtain by going to finer resolution\n", 138 | "* The precision expected on differentiation is orders of magnitude less than that of other operations we have discussed." 139 | ] 140 | }, 141 | { 142 | "cell_type": "markdown", 143 | "metadata": {}, 144 | "source": [ 145 | "# Interpolation" 146 | ] 147 | }, 148 | { 149 | "cell_type": "markdown", 150 | "metadata": { 151 | "tags": [] 152 | }, 153 | "source": [ 154 | "## Linear ##" 155 | ] 156 | }, 157 | { 158 | "cell_type": "markdown", 159 | "metadata": {}, 160 | "source": [ 161 | "Assume the function follows a straight line from $f(a)$ to $f(b)$. Estimate $f(x)$ for $a < x < b$." 162 | ] 163 | }, 164 | { 165 | "cell_type": "markdown", 166 | "metadata": {}, 167 | "source": [ 168 | "$f(x) \\approx y + z$ , $y = m(x-a)$\n", 169 | "\n", 170 | "Therefore:\n", 171 | "\n", 172 | "$f(x) \\approx \\frac{f(b) - f(a)}{b-a}(x-a) + f(a) $\n", 173 | "\n", 174 | "$~~~~~~~ = \\frac{(b-x)f(a) + (x-a)f(b)}{b-a} $ fundamental formula of linear interpolation\n", 175 | "\n", 176 | "* Can also use this to extrapolate the function to points $x > b$ or $x < a$\n", 177 | "* But the further you go, the less likely it is that the extrapolation will be accurate" 178 | ] 179 | }, 180 | { 181 | "cell_type": "markdown", 182 | "metadata": {}, 183 | "source": [ 184 | "### Error" 185 | ] 186 | }, 187 | { 188 | "cell_type": "markdown", 189 | "metadata": {}, 190 | "source": [ 191 | "* Leading-order term in approximation error: $(a-x)(b-x)f''(x)$\n", 192 | " * Vanishes as $x$ approaches $a$ or $b$\n", 193 | " * Largest in the middle of the interval, assuming $f''(x)$ varies slowly\n", 194 | " * Worst-case error is $O(h^2)$" 195 | ] 196 | }, 197 | { 198 | "cell_type": "markdown", 199 | "metadata": {}, 200 | "source": [ 201 | "* Rounding error not a big problem, since fundamental formula involves sum (not difference) of function values at two closely spaced points" 202 | ] 203 | }, 204 | { 205 | "cell_type": "markdown", 206 | "metadata": {}, 207 | "source": [ 208 | "## More complicated interpolations" 209 | ] 210 | }, 211 | { 212 | "cell_type": "markdown", 213 | "metadata": {}, 214 | "source": [ 215 | "* Lagrange interpolation methods: use quadratics or higher polynomials\n", 216 | "* To use polynomial of degree n, we need to know the value of the function at n+1 or more points\n", 217 | "\n", 218 | "* For large n, using (n-1)th order polynomial doesn't work very well\n", 219 | " * because very high order polynomials tend to have a lot of wiggles, therefore can deviate badly in the intervals between points\n", 220 | " * better to use many quadratics or cubics on smaller sets of adjacent points\n", 221 | " * but slope changes abruptly at the join-points between polynomials, resulting in uneven interpolation\n", 222 | " * even better to use splines: use the derivatives of the polynomials at the join-points, so the interpolation has smooth slope everywhere\n", 223 | " * cubic spline is most commonly used" 224 | ] 225 | }, 226 | { 227 | "cell_type": "code", 228 | "execution_count": null, 229 | "metadata": {}, 230 | "outputs": [], 231 | "source": [] 232 | } 233 | ], 234 | "metadata": { 235 | "celltoolbar": "Slideshow", 236 | "kernelspec": { 237 | "display_name": "Python 3 (ipykernel)", 238 | "language": "python", 239 | "name": "python3" 240 | }, 241 | "language_info": { 242 | "codemirror_mode": { 243 | "name": "ipython", 244 | "version": 3 245 | }, 246 | "file_extension": ".py", 247 | "mimetype": "text/x-python", 248 | "name": "python", 249 | "nbconvert_exporter": "python", 250 | "pygments_lexer": "ipython3", 251 | "version": "3.10.9" 252 | } 253 | }, 254 | "nbformat": 4, 255 | "nbformat_minor": 4 256 | } 257 | -------------------------------------------------------------------------------- /Week7/Lab7ExercisesTemplate.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "id": "95737d32-3c19-412f-a00c-4231bf759946", 6 | "metadata": {}, 7 | "source": [ 8 | "# Elliptic Example: 2D Laplacian" 9 | ] 10 | }, 11 | { 12 | "cell_type": "markdown", 13 | "id": "6225c56b-3947-4d1e-87fc-9f934be23c28", 14 | "metadata": {}, 15 | "source": [ 16 | "We want to model the electric potential for an empty 2D box, 10cm x 10cm in size, where the top wall is held at $V$ = 1.0V and the other walls at 0V.\n", 17 | "\n", 18 | "$$0 = \\nabla^2 \\phi = \\frac{\\partial^2 \\phi}{\\partial x^2} + \\frac{\\partial^2 \\phi}{\\partial y^2},$$\n", 19 | "$$ \\phi(y = 10) = 1.0 V$$\n", 20 | "$$ \\phi(y = 0) = \\phi(x = 0) = \\phi(x = 10) = 0$$" 21 | ] 22 | }, 23 | { 24 | "cell_type": "markdown", 25 | "id": "e22394b8-0aeb-4657-a1db-04c0d457e223", 26 | "metadata": {}, 27 | "source": [ 28 | "![](fig9-2.png)" 29 | ] 30 | }, 31 | { 32 | "cell_type": "markdown", 33 | "id": "22d1f834-6a10-4bfe-b3e0-8a4ed17123e6", 34 | "metadata": {}, 35 | "source": [ 36 | "## Exercise 1" 37 | ] 38 | }, 39 | { 40 | "cell_type": "markdown", 41 | "id": "520dbea0-42b7-4df4-9c2e-0aba281f3bb2", 42 | "metadata": {}, 43 | "source": [ 44 | "Setup up the problem: \n", 45 | "* discretize space in x and y, using an MxM grid\n", 46 | "* implement the boundary conditions\n", 47 | "\n", 48 | "Then use Jacobi Relaxation to solve it, with target accuracy 1e-04 and M=10. Print the number of iterations required to reach the target accuracy.\n", 49 | "\n", 50 | "You can consult the textbook's `laplace.py` for help." 51 | ] 52 | }, 53 | { 54 | "cell_type": "markdown", 55 | "id": "ed4643fb-384f-4958-9515-f7473cdc9ca6", 56 | "metadata": {}, 57 | "source": [ 58 | "## Exercise 2\n", 59 | "\n", 60 | "Plot the solution (you can use matplotlib.pyplot.imshow)" 61 | ] 62 | }, 63 | { 64 | "cell_type": "markdown", 65 | "id": "afc0c158-9b98-481d-89b1-d806f43701bb", 66 | "metadata": {}, 67 | "source": [ 68 | "## Exercise 3\n", 69 | "\n", 70 | "Now repeat Exercises 1 and 2 with M=100. Do you notice a difference in runtime?" 71 | ] 72 | }, 73 | { 74 | "cell_type": "markdown", 75 | "id": "fa30a1c8-ed5d-4794-94ab-9f826ca31a93", 76 | "metadata": {}, 77 | "source": [ 78 | "# Hyperbolic Example: Wave Equation" 79 | ] 80 | }, 81 | { 82 | "cell_type": "markdown", 83 | "id": "c14c96e4-c899-4980-955a-29ae1c2ecb40", 84 | "metadata": {}, 85 | "source": [ 86 | "Recall the 1D wave equation:\n", 87 | "$$ \\frac{\\partial^2\\phi}{\\partial t^2}=v^2\\frac{\\partial^2\\phi}{\\partial x^2} $$\n", 88 | "Consider a piano string of length $L$, initially at rest. At time $t=0$ the string is struck by the piano hammer a distance $d$ from the end of from the string. The string vibrates as a result of being struck, except at the ends, $x=0$, and $x=L$, where it is held fixed." 89 | ] 90 | }, 91 | { 92 | "cell_type": "markdown", 93 | "id": "3f09ccb8-3f2a-4de3-8307-2865e6514381", 94 | "metadata": {}, 95 | "source": [ 96 | "Consider the case $v=100\\mathrm{ms^{-1}}$, with the initial condition that $\\phi(x)=0$ everywhere but the velocity $\\psi(x)$ is nonzero, with profile\n", 97 | "\\begin{equation}\n", 98 | " \\psi(x) = C\\frac{x(L-x)}{L^2}\\exp\\left[-\\frac{(x-d)^2}{2\\sigma^2}\\right],\n", 99 | "\\end{equation}\n", 100 | "where $L=1$m, $d=10$cm, $C=1\\mathrm{ms^{-1}}$, and $\\sigma=0.3$m. " 101 | ] 102 | }, 103 | { 104 | "cell_type": "markdown", 105 | "id": "f88b1e47-83dd-4dab-ab70-4b87df21cfcf", 106 | "metadata": { 107 | "tags": [] 108 | }, 109 | "source": [ 110 | "## Exercise 4" 111 | ] 112 | }, 113 | { 114 | "cell_type": "markdown", 115 | "id": "f0cfe64d-33ae-401d-a739-23443dcf7648", 116 | "metadata": {}, 117 | "source": [ 118 | "Solve using the FTCS method, with grid spacing (in $x$) $a=5$ mm, from times 0 to 0.1s using time--step $h=10^{-6}$ s.\n", 119 | "Make a plot of $\\phi$ vs $x$ over the entire length of string, at each of the following times:\n", 120 | "* 0.006 s\n", 121 | "* 0.004 s\n", 122 | "* 0.002 s\n", 123 | "* 0.012 s\n", 124 | "* 0.100 s\n", 125 | "\n", 126 | "You'll see your first 4 plots look good, then the instability of the solution shows up!" 127 | ] 128 | }, 129 | { 130 | "cell_type": "markdown", 131 | "id": "d71158df-c9b4-4835-8bff-057faeeaaeff", 132 | "metadata": {}, 133 | "source": [ 134 | "## Exercise 5" 135 | ] 136 | }, 137 | { 138 | "cell_type": "markdown", 139 | "id": "74cd57b8-afc7-4eae-8263-901600991214", 140 | "metadata": {}, 141 | "source": [ 142 | "Repeat the previous exercise using the Crank--Nicolson method. Use a larger time--step, $h = 10^{-4}$ s.\n", 143 | "\n", 144 | "You'll see the solution is stable. It dies out to 0 at about 0.1 s, but this is how the physical system is supposed to behave!" 145 | ] 146 | }, 147 | { 148 | "cell_type": "markdown", 149 | "id": "d5e50a02-d8b3-47a9-8ac4-4218798e5def", 150 | "metadata": {}, 151 | "source": [ 152 | "the CN method involves a set of simultaneous equations, one for each grid point. We can solve using the methods for linear systems in Chapter 6 -- in particular, banded matrix. The following snippets of code will help you define the matrix, and the vector to use on the right-hand side, of the CK equations." 153 | ] 154 | }, 155 | { 156 | "cell_type": "code", 157 | "execution_count": 13, 158 | "id": "b06fab58-73ce-4bf8-a84c-c81a7463913e", 159 | "metadata": { 160 | "tags": [] 161 | }, 162 | "outputs": [], 163 | "source": [ 164 | "def matrix(N,alpha):\n", 165 | " \"\"\"Banded matrix for the Crank-Nicolson\n", 166 | " Args: \n", 167 | " N : number of elements\n", 168 | " alpha = 2*h*v**2/a**2 , h: timestep length, a: spatial grid spacing, v: wave speed\"\"\"\n", 169 | " bands = np.zeros((3,N+2))\n", 170 | " bands[0,:-2] = -alpha\n", 171 | " bands[2,1:-1] = -alpha\n", 172 | " bands[1,:] = 1+2*alpha\n", 173 | " return bands \n", 174 | "\n", 175 | "def banded(Aa,va,up=1,down=1):\n", 176 | "# from textbook online resources, to solve Ax = v\n", 177 | "# Aa is banded matrix A, va is vector v, up and down give band positions in matrix\n", 178 | "\n", 179 | " # Copy the inputs and determine the size of the system\n", 180 | " A = np.copy(Aa)\n", 181 | " v = np.copy(va)\n", 182 | " N = len(v)\n", 183 | "\n", 184 | " # Gaussian elimination\n", 185 | " for m in range(N):\n", 186 | "\n", 187 | " # Normalization factor\n", 188 | " div = A[up,m]\n", 189 | "\n", 190 | " # Update the vector first\n", 191 | " v[m] /= div\n", 192 | " for k in range(1,down+1):\n", 193 | " if m+k abs(A[ZeRow, m]):\n", 246 | " ZeRow = mm # I could swap everytime I find a hit, but that\n", 247 | " # would be a lot of operations. Instead, I just take note\n", 248 | " # of which row emerges as the winner\n", 249 | "\n", 250 | " if ZeRow != m: # now we know if and what to swap\n", 251 | " A[ZeRow, :], A[m, :] = np.copy(A[m, :]), np.copy(A[ZeRow, :])\n", 252 | " v[ZeRow], v[m] = np.copy(v[m]), np.copy(v[ZeRow])\n", 253 | "\n", 254 | " # Divide by the diagonal element\n", 255 | " div = A[m, m]\n", 256 | " A[m, :] /= div\n", 257 | " v[m] /= div\n", 258 | "\n", 259 | " # Now subtract from the lower rows\n", 260 | " for i in range(m+1, N):\n", 261 | " mult = A[i, m]\n", 262 | " A[i, :] -= mult*A[m, :]\n", 263 | " v[i] -= mult*v[m]\n", 264 | "\n", 265 | " # Backsubstitution\n", 266 | " # create an array of the same type as the input array\n", 267 | " x = np.empty(N, dtype=v.dtype)\n", 268 | " for m in range(N-1, -1, -1):\n", 269 | " x[m] = v[m]\n", 270 | " for i in range(m+1, N):\n", 271 | " x[m] -= A[m, i]*x[i]\n", 272 | " return x\n", 273 | "\n", 274 | "def PartialPivot(A_in, v_in):\n", 275 | " \"\"\" see textbook p. 222) \"\"\"\n", 276 | " return GaussElim(A_in, v_in, True)" 277 | ] 278 | } 279 | ], 280 | "metadata": { 281 | "kernelspec": { 282 | "display_name": "Python 3 (ipykernel)", 283 | "language": "python", 284 | "name": "python3" 285 | }, 286 | "language_info": { 287 | "codemirror_mode": { 288 | "name": "ipython", 289 | "version": 3 290 | }, 291 | "file_extension": ".py", 292 | "mimetype": "text/x-python", 293 | "name": "python", 294 | "nbconvert_exporter": "python", 295 | "pygments_lexer": "ipython3", 296 | "version": "3.11.9" 297 | } 298 | }, 299 | "nbformat": 4, 300 | "nbformat_minor": 5 301 | } 302 | -------------------------------------------------------------------------------- /Week5/FFT-WorkedExample.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "id": "b54cfca2-3517-47ab-b5bd-ff88d819def6", 6 | "metadata": { 7 | "slideshow": { 8 | "slide_type": "subslide" 9 | } 10 | }, 11 | "source": [ 12 | "Choose Your Own Adventure! Your options, from simplest to the hardest:\n", 13 | "\n", 14 | "* You are happy with what you've learned about the FFT and want to move on: ignore this notebook\n", 15 | "* You want to understand how FFT works a little deeper: watch the YouTube video at https://youtu.be/h7apO7q16V0 , where in 30 minutes, the narrator goes step-by-step with beautiful visuals. Grasping why FFT is so efficient is a great achievement :)\n", 16 | "* You want to fully work out an example: go through this notebook with $N=8$.\n", 17 | " * Won't do it in-class. Even with small $N$, it's tricky to follow how each step fits into the next. Actually implementing it in code is the easy part, usually a line or two.\n", 18 | " * But even if you perfectly understand the YouTube video, there is something to be said about doing it for yourself. One doesn't become a scientist only by watching videos, being able to do stuff is really what earns you a degree :)" 19 | ] 20 | }, 21 | { 22 | "cell_type": "markdown", 23 | "id": "e3fbddf1-8881-48e8-a85d-bff70c907bfa", 24 | "metadata": { 25 | "slideshow": { 26 | "slide_type": "slide" 27 | } 28 | }, 29 | "source": [ 30 | "## Example with $N=8$ \n", 31 | "\n", 32 | "We have $M=\\log_2(8) = 3$, as well as $k=0, 1, \\dots 7$ at every step. We also have\n", 33 | "$$\\omega = \\exp\\left(-\\frac{2i\\pi}8\\right) \\Rightarrow \\omega^{n+8} = \\omega^n,$$\n", 34 | "which we will use a lot." 35 | ] 36 | }, 37 | { 38 | "cell_type": "markdown", 39 | "id": "24d75686-6efd-42b4-8d9e-823353abd619", 40 | "metadata": { 41 | "slideshow": { 42 | "slide_type": "subslide" 43 | } 44 | }, 45 | "source": [ 46 | "### Writing the pairings\n", 47 | "\n", 48 | "The FFT algorithm actually starts from the real values and works its way up to the coefficients. Here, I will start from the coefficients and make my way down to the values, to better understand the notations.\n", 49 | "\n", 50 | "#### First bisection\n", 51 | "\n", 52 | "Second coefficient of the DFT is\n", 53 | "\\begin{align*}\n", 54 | " c_1 = E_1^{(0, 0)} & = \\sum_{n=0}^3y_n e^{-2i\\pi n/8} = \\sum_{n=0}^3y_n \\omega^{n} \\\\\n", 55 | " & = y_0 + \\omega y_1 + \\omega^2 y_2 + \\omega^3 y_3 + \\omega^4 y_4 + \\omega^5 y_5 + \\omega^6 y_6 + \\omega^7 y_7 \\\\\n", 56 | " & = \\underbrace{y_0 + \\omega^2 y_2 + \\omega^4 y_4 + \\omega^6 y_6}_{E_1^{(1, 0)}} \n", 57 | " + \\omega\\underbrace{\\left(y_1 + \\omega^2 y_3 + \\omega^4 y_5 + \\omega^6 y_7\\right)}_{E_1^{(1, 1)}} \n", 58 | "\\end{align*}" 59 | ] 60 | }, 61 | { 62 | "cell_type": "markdown", 63 | "id": "33cc2c0f-45a6-455a-ad9a-6baa18563ed2", 64 | "metadata": { 65 | "slideshow": { 66 | "slide_type": "subslide" 67 | } 68 | }, 69 | "source": [ 70 | "* Split and multiplication count as one operation for us.\n", 71 | "* Very hard to actually figure out the exact number of ops quickly. Let's call this \"unit operation\", one that also happens in DFT for comparison: \n", 72 | " **Bisection + multiplication = 1 operation**\n", 73 | "* Verify for yourself that eqn.\n", 74 | "$$E_k^{(m,j)} = \\sum_{p=0}^{N/2^m - 1}y_{2^m p + j}\\exp\\left(-i\\frac{2\\pi k p}{N/2^m}\\right), \\quad j \\in \\{0\\dots 2^m-1\\}$$\n", 75 | " works here." 76 | ] 77 | }, 78 | { 79 | "cell_type": "markdown", 80 | "id": "05b137a5-d6f3-46f9-9437-7e16214d9cea", 81 | "metadata": { 82 | "slideshow": { 83 | "slide_type": "subslide" 84 | } 85 | }, 86 | "source": [ 87 | "#### Second bisection\n", 88 | "\n", 89 | "Let's do a similar split for $E_1^{(1, 0)}$, for $j=0, 1$:\n", 90 | "\\begin{align*}\n", 91 | " E_1^{(1, 0)} & = y_{0} + \\omega^2 y_{2} + \\omega^4 y_{4} + \\omega^6 y_{6} \\\\\n", 92 | " & = \\underbrace{y_{0} + \\omega^4 y_{4}}_{E_1^{(2, 0)}} \n", 93 | " + \\omega^2\\underbrace{\\left(y_{2} + \\omega^4 y_{6}\\right)}_{E_1^{(2, 2)}} \n", 94 | "\\end{align*}\n", 95 | "Note how I \"skipped\" $E_1^{(2, 1)}$: that's because we reserve it for the split of $E_1^{(1, 1)}$.\n", 96 | "\\begin{align*}\n", 97 | " E_1^{(1, 1)} & = \\underbrace{y_{1} + \\omega^4 y_{5}}_{E_1^{(2, 1)}} \n", 98 | " + \\omega^2\\underbrace{\\left(y_{3} + \\omega^4 y_{7}\\right)}_{E_1^{(2, 3)}} \n", 99 | "\\end{align*}\n", 100 | "**Two bisections + multiplication = 2 operations**" 101 | ] 102 | }, 103 | { 104 | "cell_type": "markdown", 105 | "id": "34b94ad6-6a34-44ab-86d6-dcc3a2dc73ab", 106 | "metadata": { 107 | "slideshow": { 108 | "slide_type": "subslide" 109 | } 110 | }, 111 | "source": [ 112 | "Or, in a shorter fashion,\n", 113 | "$$E_1^{(1, j)} = \\underbrace{y_{j} + \\omega^4 y_{j+4}}_{E_1^{(2, j)}} + \\omega^2\\underbrace{\\left(y_{j+2} + \\omega^4 y_{j+6}\\right)}_{E_1^{(2, j+2)}}.$$\n", 114 | "I could plug index $m$ to make it more systematic, which would give me the general formulas of the previous sub-section." 115 | ] 116 | }, 117 | { 118 | "cell_type": "markdown", 119 | "id": "bf4bfc02-ec6c-4f5d-829f-2857f6a33948", 120 | "metadata": { 121 | "slideshow": { 122 | "slide_type": "subslide" 123 | } 124 | }, 125 | "source": [ 126 | "#### Third (and final) bisection\n", 127 | "Finally, we can split previous $E$'s in the last step:\n", 128 | "\\begin{align*}\n", 129 | " E_1^{(2, 0)} & = y_0 + \\omega^4 y_{4} = E^{(3, 0)} + \\omega^4 E^{(3, 4)}\\\\\n", 130 | " E_1^{(2, 1)} & = y_1 + \\omega^4 y_{5} = E^{(3, 1)} + \\omega^4 E^{(3, 5)}\\\\\n", 131 | " E_1^{(2, 2)} & = y_2 + \\omega^4 y_{6} = E^{(3, 2)} + \\omega^4 E^{(3, 6)}\\\\\n", 132 | " E_1^{(2, 3)} & = y_3 + \\omega^4 y_{7} = E^{(3, 3)} + \\omega^4 E^{(3, 7)},\n", 133 | "\\end{align*}\n", 134 | "\n", 135 | "* **Four bisections + multiplication = 4 operations**\n", 136 | "* More simply$E_1^{(3, j)} = y_j$.\n", 137 | "* We can use these 8 values of all $k$: actually connected to the $\\omega^{n+8}= \\omega^n$ property." 138 | ] 139 | }, 140 | { 141 | "cell_type": "markdown", 142 | "id": "17634bb2-127f-47d3-b261-e07fbf4bd91f", 143 | "metadata": { 144 | "slideshow": { 145 | "slide_type": "subslide" 146 | } 147 | }, 148 | "source": [ 149 | "At this point: \n", 150 | "* $1+2+4 = 7$ operations, for 8 coefficients. $8\\times7 = 56$ operations.\n", 151 | "* Recall: I promised $N\\log_2N = 8\\times3 = 24$ ops. What is going on?\n", 152 | "* Recall DFT: $\\sim64$ operations (a little less actually, but not much).\n", 153 | "* Answer: we do not have to compute all $E_k^{(m, j)}$ for all $k$'s, because $\\omega^{n+8}=\\omega^n$. Kind of like I did for the last bisection: I was going to re-use the $E^{3, j}$ for all $k$'s, but something similar happens at all stages.\n", 154 | "* It is easier to see by actually doing it." 155 | ] 156 | }, 157 | { 158 | "cell_type": "markdown", 159 | "id": "c69c7c4a-fd68-4d24-9f23-fcdfa86ce25f", 160 | "metadata": { 161 | "slideshow": { 162 | "slide_type": "subslide" 163 | } 164 | }, 165 | "source": [ 166 | "### Calculating the Fourier coefficients\n", 167 | "\n", 168 | "The FFT algorithm works backward from what I just showed.\n", 169 | "Which makes sense: we know the $y_j$'s, not the $\\gamma_k$'s.\n", 170 | "\n", 171 | "#### First step: start from the values\n", 172 | "* $m=M=3$,\n", 173 | "* $j=0, 1,\\dots 7$,\n", 174 | "* $\\forall k, \\quad E_k^{(3, j)} = E^{(3, j)} = y_j$ , \n", 175 | "* **Number of ops:** $(N/2^m = 1) \\times (2^m = 8) = 8 = N$.\n", 176 | "\n", 177 | "Note that these ar far less flop-consuming operations than the next steps, and they are common to both DFT and FFT. Therefore, we will not count these operations for simplicity." 178 | ] 179 | }, 180 | { 181 | "cell_type": "markdown", 182 | "id": "a244b91f-a341-4a88-8ae4-3d0825095807", 183 | "metadata": { 184 | "slideshow": { 185 | "slide_type": "subslide" 186 | } 187 | }, 188 | "source": [ 189 | "#### Second step\n", 190 | "* $m=2, j=0, 1, 2, 3$\n", 191 | "* Compute the \"partial\" DFTs:\n", 192 | " \\begin{align*}\n", 193 | " & E_0^{(2, j)} = E^{(3, j)} + \\omega^{0\\times4}E^{(3, j+4)}= E^{(3, j)} + E^{(3, j+4)},\\\\\n", 194 | " & E_1^{(2, j)} = E^{(3, j)} + \\omega^{1\\times4}E^{(3, j+4)} = E^{(3, j)} + \\omega^4 E^{(3, j+4)},\n", 195 | " \\end{align*}\n", 196 | " but we can actually stop after these $4\\times 2 = 8$ operations.\n", 197 | " This is where we use the property that $\\omega^{n+8}=\\omega^n$: \n", 198 | " \\begin{align*}\n", 199 | " & E_2^{(2, j)} = E^{(3, j)} + \\omega^{2\\times4}E^{(3, j+4)} = E^{(3, j)} + \\underbrace{\\omega^8}_{=1} E^{(3, j+4)} = E_0^{(2, j)},\\\\\n", 200 | " & E_3^{(2, j)} = E^{(3, j)} + \\omega^{3\\times4}E^{(3, j+4)} = E^{(3, j)} + \\underbrace{\\omega^{12}}_{=\\omega^4} E^{(3, j+4)} = E_1^{(2, j)},\n", 201 | " \\end{align*}\n", 202 | " and so on.\n", 203 | " Thanks to the periodicity of $\\omega^{2^mk}$, we do not have to compute the values for all $k$'s, but instead, compute eight of them (one for each $j$, twice) and re-use them in a clever fashion.\n", 204 | "* **Number of ops:** $(N/2^m = 2) \\times (2^m = 4) = 8= N$\n", 205 | " " 206 | ] 207 | }, 208 | { 209 | "cell_type": "markdown", 210 | "id": "26b47a16-0a1e-4ebb-a86e-1c4fbd788bc5", 211 | "metadata": { 212 | "slideshow": { 213 | "slide_type": "subslide" 214 | } 215 | }, 216 | "source": [ 217 | "#### Third step\n", 218 | "* $m=1$, \n", 219 | "* $j=0, 1$,\n", 220 | "* Compute the \"partial\" DFTs:\n", 221 | " \\begin{align*}\n", 222 | " & E_0^{(1, j)} = E_0^{(2, j)} + \\omega^{0\\times2}E_0^{(2, j+2)}= E_0^{(2, j)} + E_0^{(2, j+2)},\\\\\n", 223 | " & E_1^{(1, j)} = E_1^{(2, j)} + \\omega^{1\\times2}E_1^{(2, j+2)} = E_1^{(2, j)} + \\omega^2 E_1^{(2, j+4)},\\\\\n", 224 | " \\end{align*}\n", 225 | " and we can use the trick where we re-use the $E_0$'s and $E_1$'s of the previous step to compute:\n", 226 | " \\begin{align*}\n", 227 | " & E_2^{(1, j)} = E_0^{(2, j)} + \\omega^{2\\times2}E_0^{(2, j+2)}= E_0^{(2, j)} + \\omega^4 E_0^{(2, j+4)},\\\\\n", 228 | " & E_3^{(1, j)} = E_1^{(2, j)} + \\omega^{3\\times2}E_1^{(2, j+2)} = E_1^{(2, j)} + \\omega^6 E_1^{(2, j+4)}.\n", 229 | " \\end{align*}\n", 230 | " We had to compute for twice as many $k$ indices, but for half as many $j$ indices.\n", 231 | "* **Number of ops:** $(N/2^m = 4) \\times (2^m = 2) = N$" 232 | ] 233 | }, 234 | { 235 | "cell_type": "markdown", 236 | "id": "8ff14507-dd46-4140-83b6-13ed1d4c8843", 237 | "metadata": { 238 | "slideshow": { 239 | "slide_type": "subslide" 240 | } 241 | }, 242 | "source": [ 243 | "#### Last step: the coefficients\n", 244 | "* $m = 0$,\n", 245 | "* $j=0$,\n", 246 | "* We can now compute the DFT coefficients:\n", 247 | " \\begin{align*}\n", 248 | " & E_0^{(0, 0)} = c_0 = E_0^{(1, 0)} + \\omega^{0\\times1}E_0^{(1, 1)} = E_0^{(1, 0)} + E_0^{(1, 1)},\\\\\n", 249 | " & E_1^{(0, 0)} = c_1 = E_1^{(1, 0)} + \\omega^{1\\times1}E_1^{(1, 1)} = E_1^{(1, 0)} + \\omega E_1^{(1, 0)},\\\\\n", 250 | " & \\vdots\\\\\n", 251 | " & E_7^{(0, 0)} = c_7 = E_1^{(3, j)} + \\omega^7 E_1^{(1, 1)}.\n", 252 | " \\end{align*}\n", 253 | "* **Number of ops:** $(N/2^m = 8)\\times (2^m = 1) = N$.\n", 254 | " \n", 255 | "For each step ($m=2, 1, 0$, because we said we weren't counting $m=3$), we had 8 operations. $8\\times 3=24 = 8\\log_2(8)$, as promised!" 256 | ] 257 | } 258 | ], 259 | "metadata": { 260 | "kernelspec": { 261 | "display_name": "Python 3 (ipykernel)", 262 | "language": "python", 263 | "name": "python3" 264 | }, 265 | "language_info": { 266 | "codemirror_mode": { 267 | "name": "ipython", 268 | "version": 3 269 | }, 270 | "file_extension": ".py", 271 | "mimetype": "text/x-python", 272 | "name": "python", 273 | "nbconvert_exporter": "python", 274 | "pygments_lexer": "ipython3", 275 | "version": "3.10.9" 276 | } 277 | }, 278 | "nbformat": 4, 279 | "nbformat_minor": 5 280 | } 281 | -------------------------------------------------------------------------------- /Week2/Lecture 2 Notebook Part1.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": { 6 | "slideshow": { 7 | "slide_type": "slide" 8 | } 9 | }, 10 | "source": [ 11 | "# Simple Numerical Integration Techniques" 12 | ] 13 | }, 14 | { 15 | "cell_type": "markdown", 16 | "metadata": { 17 | "tags": [] 18 | }, 19 | "source": [ 20 | "## Basic Idea of Numerical integration" 21 | ] 22 | }, 23 | { 24 | "cell_type": "markdown", 25 | "metadata": { 26 | "jp-MarkdownHeadingCollapsed": true, 27 | "slideshow": { 28 | "slide_type": "slide" 29 | }, 30 | "tags": [] 31 | }, 32 | "source": [ 33 | "* Think of integrals as areas under curves.\n", 34 | "* Approximate these areas in terms of simple shapes (rectangles, trapezoids, rectangles with parabolic tops)\n", 35 | "* Simplest case: Riemann sum, $I \\approx \\sum_{k} f(x_k) h$, with $h=$ slice width (bottom-left panel below)\n", 36 | "\n", 37 | "![](RecTrapSimp.png)" 38 | ] 39 | }, 40 | { 41 | "cell_type": "markdown", 42 | "metadata": { 43 | "tags": [] 44 | }, 45 | "source": [ 46 | "## Trapezoidal rule" 47 | ] 48 | }, 49 | { 50 | "cell_type": "markdown", 51 | "metadata": { 52 | "cell_style": "split", 53 | "jp-MarkdownHeadingCollapsed": true, 54 | "slideshow": { 55 | "slide_type": "subslide" 56 | }, 57 | "tags": [] 58 | }, 59 | "source": [ 60 | "* Break up interval into $N$ slices,\n", 61 | "* Approximate function as segments on each slice.\n", 62 | "\n", 63 | "\n", 64 | "![From Newman: fig. 5.1b](Fig5-1b.png)" 65 | ] 66 | }, 67 | { 68 | "cell_type": "markdown", 69 | "metadata": { 70 | "cell_style": "split", 71 | "slideshow": { 72 | "slide_type": "fragment" 73 | } 74 | }, 75 | "source": [ 76 | "* N slices from $a$ to $b$ means that slice width: $$h = (b - a )/N$$\n", 77 | "* area of $k^\\text{th}$ slice’s trapezoid: (Rectangle + Triangle)\n", 78 | "$$A_k = f(x_k)h + \\frac{h[f(x_k+h)-f(x_k)]}2 \\\\ = \\frac{h[f(x_k) + f(x_k +h)]}2.$$\n", 79 | "* Total area (our approximation for the integral) (and using $x_k=a+kh$):\n", 80 | "$$ \\boxed{I(a, b) \\approx h\\left[\\frac12 f(a) + \\frac12f(b) + \\sum_{k=1}^{N-1} f(a+kh)\\right]}. $$\n", 81 | "* Note how it is almost a Riemann sum, except for beginning and end points.\n", 82 | " Yet, the differences will be significant!" 83 | ] 84 | }, 85 | { 86 | "cell_type": "markdown", 87 | "metadata": { 88 | "cell_style": "split", 89 | "slideshow": { 90 | "slide_type": "subslide" 91 | } 92 | }, 93 | "source": [ 94 | "$$\\text{Recall } I(a, b) \\approx h\\left[\\frac12 f(a) + \\frac12f(b) + \\sum_{k=1}^{N-1} f(a+kh)\\right].$$" 95 | ] 96 | }, 97 | { 98 | "cell_type": "markdown", 99 | "metadata": { 100 | "tags": [] 101 | }, 102 | "source": [ 103 | "## Simpson's rule" 104 | ] 105 | }, 106 | { 107 | "cell_type": "markdown", 108 | "metadata": { 109 | "cell_style": "split", 110 | "slideshow": { 111 | "slide_type": "subslide" 112 | }, 113 | "tags": [] 114 | }, 115 | "source": [ 116 | "* Break up interval into $N$ slices,\n", 117 | "* approximate function as a **quadratic** for every 2 slices\n", 118 | "* need 2 slices because you need 3 points to define a quadratic\n", 119 | "* more slices $\\Rightarrow$ better approximation to function\n", 120 | "* Number of slices need to be even! If uneven, either discard one, or use trapezoidal rule on one slice." 121 | ] 122 | }, 123 | { 124 | "cell_type": "markdown", 125 | "metadata": { 126 | "cell_style": "split", 127 | "slideshow": { 128 | "slide_type": "subslide" 129 | } 130 | }, 131 | "source": [ 132 | "![From Newman: fig. 5.2](fig5-2.png)" 133 | ] 134 | }, 135 | { 136 | "cell_type": "markdown", 137 | "metadata": { 138 | "cell_style": "center", 139 | "slideshow": { 140 | "slide_type": "subslide" 141 | } 142 | }, 143 | "source": [ 144 | "* Area of each 2-slice quadratic (see text for formula):\n", 145 | "$$A_k = \\frac{h}3\\left\\{f[a+(2k-2)h] + 4f[a+(2k-1)h] + f(a+2kh)\\right\\}.$$\n", 146 | "* Adding up the slices:\n", 147 | "$$\\boxed{I(a,b) \\approx \\frac{h}3\\left[f(a) + f(b) + 4\\sum_{\\substack{k\\ odd\\\\ 1\\dots{}N-1}}f(a+kh) + 2\\sum_{\\substack{k\\ even \\\\ 2\\dots{}N-2}}f(a+kh)\\right].}$$\n", 148 | "* In Python, you can easily sum over even and odd values:\n", 149 | " `for k in range(1, N, 2)` for the odd terms, and\n", 150 | " `for k in range(2, N, 2)` for the even terms." 151 | ] 152 | }, 153 | { 154 | "cell_type": "markdown", 155 | "metadata": { 156 | "tags": [] 157 | }, 158 | "source": [ 159 | "## Newton-Cotes formulas" 160 | ] 161 | }, 162 | { 163 | "cell_type": "markdown", 164 | "metadata": { 165 | "slideshow": { 166 | "slide_type": "fragment" 167 | } 168 | }, 169 | "source": [ 170 | "Trapezoid and Simpson's Rules are part of a more general set of integration rules:\n", 171 | "* Break your interval into small **equal** sub-intervals,\n", 172 | "* approximate your function by a polynomial of some degree, e.g. \n", 173 | " * 0 for Riemann Sum (mid-point rule, just summing all elements and multiplying by $h$)\n", 174 | " * 1 for Trapezoidal\n", 175 | " * 2 for Simpson\n", 176 | " \n", 177 | "on that sub-interval.\n", 178 | "* this class of methods leads to Newton-Cotes (N-C) formulas." 179 | ] 180 | }, 181 | { 182 | "cell_type": "markdown", 183 | "metadata": { 184 | "slideshow": { 185 | "slide_type": "subslide" 186 | } 187 | }, 188 | "source": [ 189 | "* All Newton-Cotes formulas can be written in the form:\n", 190 | "$$\\int_a^b f(x) dx \\approx \\sum_{k=1}^{N+1} w_k f(x_k).$$\n", 191 | "* $w_k$: \"weights\".\n", 192 | "* $x_k$: \"sample points\". Notice above we are using $N+1$ points ($N$ slices) to sample.\n", 193 | "* N-C formulas of degree $N$: exact for polynomials of degree $N$ (which require $N+1$ points to determine)\n", 194 | "* For N-C formulas, the sample points are **evenly spaced**." 195 | ] 196 | }, 197 | { 198 | "cell_type": "markdown", 199 | "metadata": { 200 | "slideshow": { 201 | "slide_type": "subslide" 202 | } 203 | }, 204 | "source": [ 205 | "### Generalization\n", 206 | "\n", 207 | "Degree | Polynomial | Coefficients\n", 208 | "- | - | -\n", 209 | "1 (trapezoidal) | Straight line | $\\frac{1}{2}, 1, 1,\\dots, 1, \\frac{1}{2}$\n", 210 | "2 (Simpson) | Parabola | $\\frac13, \\frac43, \\frac23, \\frac43,\\dots, \\frac23, \\frac43, \\frac13$\n", 211 | "3 | Cubic | $\\frac38, \\frac98, \\frac98, \\frac34, \\frac98, \\frac98, \\frac34, \\dots, \\frac98, \\frac 38$\n", 212 | "4 | Quartic | $\\frac{14}{45}, \\frac{64}{45}, \\frac{8}{15}, \\frac{64}{45}, \\frac{28}{45}, \\frac{64}{45},\\dots, \\frac{64}{45}, \\frac{14}{45}$" 213 | ] 214 | }, 215 | { 216 | "cell_type": "markdown", 217 | "metadata": { 218 | "tags": [] 219 | }, 220 | "source": [ 221 | "## Error estimation" 222 | ] 223 | }, 224 | { 225 | "cell_type": "markdown", 226 | "metadata": { 227 | "slideshow": { 228 | "slide_type": "subslide" 229 | }, 230 | "tags": [] 231 | }, 232 | "source": [ 233 | "* If you tried the trapezoidal integration routine, you noticed that the error (difference between true value of the integral and computed value) goes down as $N$ increases.\n", 234 | "* How fast?" 235 | ] 236 | }, 237 | { 238 | "cell_type": "markdown", 239 | "metadata": { 240 | "slideshow": { 241 | "slide_type": "subslide" 242 | } 243 | }, 244 | "source": [ 245 | "### Euler-MacLaurin formulas for error\n", 246 | "\n", 247 | "Based on Taylor expansions.\n", 248 | "\n", 249 | "Example, for the trapezoidal rule:\n", 250 | "$$ I(a, b) = \\int_a^b f(x)dx \\underbrace{=}_{look!} \\underbrace{h\\left[\\frac12 f(a) + \\frac12f(b) + \\sum_{k=1}^{N-1} f(a+kh)\\right]}_{\\text{the method}} + \\underbrace{\\epsilon}_{\\text{the error}}$$" 251 | ] 252 | }, 253 | { 254 | "cell_type": "markdown", 255 | "metadata": { 256 | "slideshow": { 257 | "slide_type": "subslide" 258 | } 259 | }, 260 | "source": [ 261 | "* Trapezoidal rule is a \"1$^{\\text{st}}$-order\" integration rule, i.e. accurate up to and including terms proportional to $h$. Leading order approximation error is of order $h^2$:\n", 262 | "$$\\boxed{\\epsilon = \\frac{h^2[f'(a) - f'(b)]}{12} + h.o.t.}$$\n", 263 | "\n", 264 | "* Simpson’s rule is a \"3$^{\\text{rd}}$-order\" integration rule, i.e., accurate up to and including terms proportional to $h^3$. Leading order approximation error is of order $h^4$ (even though we go from segments to quadratics!)\n", 265 | "$$\\boxed{\\epsilon = \\frac{h^4[f'''(a) - f'''(b)]}{180} + h.o.t.}$$\n", 266 | "(we won't worry about deriving these)" 267 | ] 268 | }, 269 | { 270 | "cell_type": "markdown", 271 | "metadata": { 272 | "slideshow": { 273 | "slide_type": "subslide" 274 | } 275 | }, 276 | "source": [ 277 | "### Adaptive methods\n", 278 | "\n", 279 | "What if you don't know $f'$, $f'''$, etc.? If you know the order of the error, there is another way: compute the integral using $N$ intervals, then double $N$ and compute the integral again. Based on the order, a formula can be derived relating the error $\\epsilon$ on the latter result to the difference between the results.\n", 280 | "* e.g. for trapezoidal rule, we can (but won't) derive the following formula: $\\epsilon = (I_{2N} - I_N)/3$\n", 281 | "* for Simpson's, $\\epsilon = (I_{2N} - I_N)/15$\n", 282 | "* when you're re-computing the integral with $N$ doubled: re-use some of your results from the previous computation (since the sample points for the previous estimate are nested inside the points for the new estimate)\n", 283 | "* you can add $\\epsilon$ (as calculated above) to $I_{2N}$, to obtain a new estimate that is accurate to two more orders!" 284 | ] 285 | }, 286 | { 287 | "cell_type": "markdown", 288 | "metadata": {}, 289 | "source": [ 290 | "Often we want to calculate the value of an integral to a given accuracy (e.g. 4 decimal places), and don't know beforehand what value of $N$ will be required to achieve this. We could just start with a really huge $N$, but this is computationally expensive. Instead use an adaptive method:\n", 291 | "1. Evaluate integral using a small $N$\n", 292 | "2. Double $N$, evaluate again, and calculate error using formula above\n", 293 | "3. If error doesn't satisfy our accuracy criterion, repeat step 2. Keep repeating until accuracy is achieved." 294 | ] 295 | }, 296 | { 297 | "cell_type": "markdown", 298 | "metadata": { 299 | "tags": [] 300 | }, 301 | "source": [ 302 | "## Romberg Integration" 303 | ] 304 | }, 305 | { 306 | "cell_type": "markdown", 307 | "metadata": {}, 308 | "source": [ 309 | "An example of \"Richardson extrapolation\", a technique in which higher-order estimates of quantities are calculated iteratively or recursively from lower-order ones\n", 310 | "\n", 311 | "*If you're unfamiliar with recursion: that's OK, I will try to implement algorithms iteratively rather than recursively in the code in this course, even though recursion is sometimes more computationally efficient.*" 312 | ] 313 | }, 314 | { 315 | "cell_type": "markdown", 316 | "metadata": {}, 317 | "source": [ 318 | "Define $R_{i,m}$ as the estimate calculated at the ith round of the doubling procedure (in the adaptive method above), with an error of order $h^{2m}$. Recursion relation:\n", 319 | "$$R_{i,m+1} = R_{i,m} + \\frac{R_{i,m} - R_{i-1,m}}{4^m - 1} $$" 320 | ] 321 | }, 322 | { 323 | "cell_type": "markdown", 324 | "metadata": {}, 325 | "source": [ 326 | "1. Calculate $R_{1,1}$ and $R_{2,1}$ using the trapezoidal rule.\n", 327 | "2. Calculate $R_{2,2}$ by recursion relation using the results from step 1\n", 328 | "3. Calculate $R_{3,1}$ using trapezoidal rule\n", 329 | "4. Calculate $R_{3,2}$ by recursion relation using the results from steps 1 and 3, then calculate $R_{3,3}$ by recursion relation using $R_{3,2}$ and the result from step 2.\n", 330 | "5. At each successive step i, compute one more trapezoidal rule estimate $R_{i,1}$. Then compute $R_{i,2}$ through $R_{i,i}$ by recursion relation using results of previous steps. Also compute the error on $R_{i,i}$ and stop when it's accurate enough." 331 | ] 332 | }, 333 | { 334 | "cell_type": "markdown", 335 | "metadata": { 336 | "tags": [] 337 | }, 338 | "source": [ 339 | "![From Newman: chap5](doubling.PNG)" 340 | ] 341 | }, 342 | { 343 | "cell_type": "markdown", 344 | "metadata": { 345 | "tags": [] 346 | }, 347 | "source": [ 348 | "![From Newman: chap5](romberg.PNG)" 349 | ] 350 | }, 351 | { 352 | "cell_type": "markdown", 353 | "metadata": {}, 354 | "source": [ 355 | "* Note, we are essentially calculating the integral by doing a series expansion in powers of h. \n", 356 | "* This works best in cases where the power series converges rapidly\n", 357 | "* This doesn't work so well -- so we should use adaptive trapezoidal method instead -- if the integrand is poorly behaved, e.g.\n", 358 | " * has large rapid fluctuations\n", 359 | " * has singularities" 360 | ] 361 | }, 362 | { 363 | "cell_type": "code", 364 | "execution_count": null, 365 | "metadata": {}, 366 | "outputs": [], 367 | "source": [] 368 | } 369 | ], 370 | "metadata": { 371 | "celltoolbar": "Slideshow", 372 | "kernelspec": { 373 | "display_name": "Python 3 (ipykernel)", 374 | "language": "python", 375 | "name": "python3" 376 | }, 377 | "language_info": { 378 | "codemirror_mode": { 379 | "name": "ipython", 380 | "version": 3 381 | }, 382 | "file_extension": ".py", 383 | "mimetype": "text/x-python", 384 | "name": "python", 385 | "nbconvert_exporter": "python", 386 | "pygments_lexer": "ipython3", 387 | "version": "3.11.9" 388 | }, 389 | "latex_metadata": { 390 | "affiliation": "PHY407, University of Toronto", 391 | "author": "Nico Grisouard", 392 | "title": "Lecture 1: python basics" 393 | } 394 | }, 395 | "nbformat": 4, 396 | "nbformat_minor": 4 397 | } 398 | -------------------------------------------------------------------------------- /Week5/pitch.txt: -------------------------------------------------------------------------------- 1 | 0.455179 2 | 0.345091 3 | 0.0404799 4 | 0.152787 5 | 0.424501 6 | 0.363668 7 | 0.650339 8 | 1.30631 9 | 0.489474 10 | 1.02014 11 | 1.29756 12 | 1.06208 13 | 0.563859 14 | 0.730633 15 | 1.21874 16 | 1.10176 17 | 1.16789 18 | 1.21348 19 | 1.48197 20 | 0.832756 21 | 0.839688 22 | 1.3807 23 | 0.579161 24 | 1.26428 25 | 0.92053 26 | 1.06147 27 | 0.942171 28 | 0.768963 29 | 0.569169 30 | 0.883681 31 | 0.923655 32 | 0.074685 33 | 0.092567 34 | -0.435916 35 | -0.057262 36 | -0.951968 37 | -0.649733 38 | -0.44462 39 | -1.14561 40 | -0.728712 41 | -0.736087 42 | -1.13832 43 | -0.455724 44 | -0.99128 45 | -0.536699 46 | -0.814002 47 | -0.645586 48 | -0.617427 49 | -0.690382 50 | -0.802721 51 | -0.860645 52 | -1.05548 53 | -0.51968 54 | -1.35497 55 | -1.26597 56 | -0.70812 57 | -1.18836 58 | -0.524089 59 | -0.319178 60 | -0.927603 61 | -0.913662 62 | -0.12262 63 | -0.977707 64 | -0.704571 65 | 0.059006 66 | 0.808674 67 | 0.630308 68 | 0.198205 69 | 0.614853 70 | 0.338069 71 | 1.23206 72 | 0.380252 73 | 0.827153 74 | 1.15143 75 | 1.08733 76 | 1.25514 77 | 0.884045 78 | 0.861073 79 | 1.09851 80 | 0.685813 81 | 1.09712 82 | 0.567606 83 | 0.942769 84 | 0.489823 85 | 0.986075 86 | 1.35433 87 | 0.739755 88 | 0.695101 89 | 1.05061 90 | 0.59422 91 | 1.21426 92 | 1.18934 93 | 0.852377 94 | 0.667628 95 | 0.837226 96 | 0.297754 97 | 0.162805 98 | 0.082989 99 | -0.848629 100 | -0.286408 101 | -0.176092 102 | -0.716731 103 | -0.74744 104 | -1.20031 105 | -1.22048 106 | -0.83138 107 | -0.936128 108 | -0.715551 109 | -0.741243 110 | -1.16013 111 | -0.882363 112 | -1.46019 113 | -0.721042 114 | -1.25521 115 | -0.616825 116 | -1.1655 117 | -1.41007 118 | -1.22871 119 | -0.786792 120 | -0.614636 121 | -0.967878 122 | -0.902159 123 | -0.460601 124 | -0.509993 125 | -0.672589 126 | -1.03776 127 | -0.693258 128 | 0.11636 129 | 0.062451 130 | 0.335279 131 | 0.456839 132 | 0.126282 133 | 0.929797 134 | 0.896511 135 | 1.27161 136 | 0.887222 137 | 1.28261 138 | 1.14384 139 | 0.771979 140 | 1.1999 141 | 0.805016 142 | 0.710672 143 | 0.93108 144 | 0.582222 145 | 1.25745 146 | 0.616343 147 | 1.20827 148 | 0.854276 149 | 1.378 150 | 1.35206 151 | 0.498617 152 | 0.54519 153 | 0.868378 154 | 0.623044 155 | 0.308487 156 | 0.704239 157 | 0.427693 158 | 0.364264 159 | 0.118175 160 | 0.601726 161 | 0.019956 162 | -0.675955 163 | -0.137172 164 | -0.841153 165 | -0.205305 166 | -0.880882 167 | -1.10076 168 | -0.398618 169 | -1.22457 170 | -0.73722 171 | -1.0087 172 | -1.2312 173 | -1.04057 174 | -1.02353 175 | -0.521379 176 | -1.2495 177 | -1.08485 178 | -1.00076 179 | -1.33022 180 | -0.778918 181 | -0.581822 182 | -0.486723 183 | -0.622606 184 | -0.63752 185 | -0.943231 186 | -0.805948 187 | -0.901969 188 | -0.819706 189 | -0.232712 190 | -0.210333 191 | -0.412569 192 | -0.414784 193 | 0.323544 194 | 0.273357 195 | 0.096778 196 | 0.335495 197 | 0.679396 198 | 0.28476 199 | 0.915505 200 | 0.768808 201 | 1.1032 202 | 1.212 203 | 0.847404 204 | 0.986746 205 | 0.729727 206 | 1.23816 207 | 0.647476 208 | 0.930769 209 | 0.566467 210 | 0.822855 211 | 0.559904 212 | 0.804291 213 | 0.923918 214 | 1.03764 215 | 1.11294 216 | 0.963927 217 | 0.411199 218 | 1.15434 219 | 0.288703 220 | 0.323457 221 | 0.804031 222 | 0.854041 223 | 0.959495 224 | 0.420574 225 | 0.294976 226 | -0.422677 227 | -0.119332 228 | -0.504781 229 | -0.807993 230 | -0.358867 231 | -0.368384 232 | -0.381341 233 | -0.512188 234 | -0.704816 235 | -1.10505 236 | -0.51522 237 | -0.502969 238 | -1.21111 239 | -1.22242 240 | -0.824005 241 | -1.11215 242 | -0.594716 243 | -1.23253 244 | -1.03622 245 | -0.634653 246 | -1.22408 247 | -0.657855 248 | -0.459457 249 | -0.864296 250 | -0.638833 251 | -0.665095 252 | -1.09279 253 | -0.551507 254 | -0.42632 255 | -0.47603 256 | -0.082255 257 | 0.400821 258 | 0.005794 259 | 0.444018 260 | 1.02766 261 | 0.397391 262 | 0.337375 263 | 1.03045 264 | 0.378627 265 | 1.30414 266 | 0.457557 267 | 0.580985 268 | 0.885155 269 | 1.11455 270 | 0.638035 271 | 1.40636 272 | 1.2473 273 | 0.602164 274 | 0.689129 275 | 1.2842 276 | 0.892904 277 | 1.08756 278 | 1.05574 279 | 0.880759 280 | 1.2961 281 | 1.2091 282 | 0.564525 283 | 1.27311 284 | 0.782768 285 | 0.809463 286 | 0.903967 287 | 0.277751 288 | 0.455873 289 | 0.457782 290 | -0.05065 291 | -0.293773 292 | -1.02161 293 | -0.694105 294 | -0.504073 295 | -0.774931 296 | -1.2886 297 | -0.384127 298 | -1.28027 299 | -0.546986 300 | -0.954927 301 | -0.678897 302 | -1.25778 303 | -0.584826 304 | -0.797301 305 | -0.708285 306 | -1.19066 307 | -1.19539 308 | -1.17812 309 | -0.706062 310 | -1.16941 311 | -0.997881 312 | -0.486514 313 | -1.17161 314 | -0.485185 315 | -0.36812 316 | -0.374691 317 | -0.377849 318 | -0.399666 319 | -0.92534 320 | -0.491559 321 | -0.480347 322 | 0.555322 323 | 0.418596 324 | 0.759727 325 | 1.10059 326 | 0.459469 327 | 0.311324 328 | 1.31362 329 | 1.25026 330 | 1.37504 331 | 1.13198 332 | 0.807009 333 | 1.12066 334 | 0.50973 335 | 0.943159 336 | 0.772226 337 | 1.07365 338 | 0.643162 339 | 1.25952 340 | 1.44724 341 | 0.954777 342 | 1.04263 343 | 1.42657 344 | 1.21147 345 | 0.603865 346 | 1.15605 347 | 1.15644 348 | 1.1861 349 | 0.864353 350 | 0.920829 351 | 0.546746 352 | 0.690592 353 | 0.013715 354 | -0.786414 355 | -0.486681 356 | -1.01642 357 | -1.01173 358 | -1.14912 359 | -1.0601 360 | -0.451247 361 | -0.4459 362 | -0.857202 363 | -0.559402 364 | -1.30445 365 | -0.752623 366 | -1.08069 367 | -0.758481 368 | -0.691706 369 | -1.03968 370 | -1.41166 371 | -0.956823 372 | -1.40129 373 | -0.691626 374 | -0.897374 375 | -0.654001 376 | -0.703893 377 | -0.612797 378 | -0.417706 379 | -1.01376 380 | -1.20351 381 | -0.299532 382 | -0.965393 383 | -0.057288 384 | -0.551211 385 | 0.423366 386 | -0.0887855 387 | 0.0190352 388 | 0.308726 389 | 1.01313 390 | 0.373125 391 | 0.352514 392 | 1.09984 393 | 1.2152 394 | 0.832587 395 | 0.8357 396 | 1.32073 397 | 0.886489 398 | 0.848997 399 | 0.765194 400 | 1.38941 401 | 1.22692 402 | 1.45431 403 | 1.24752 404 | 0.609336 405 | 0.651485 406 | 0.590718 407 | 1.00975 408 | 0.549547 409 | 1.2502 410 | 0.791818 411 | 0.777073 412 | 0.827849 413 | 1.01089 414 | 0.236198 415 | 0.914128 416 | 0.212415 417 | 0.437889 418 | -0.214048 419 | -0.558177 420 | -0.36892 421 | -1.08325 422 | -1.13028 423 | -0.813278 424 | -0.489739 425 | -0.540415 426 | -0.892381 427 | -0.647558 428 | -1.07749 429 | -1.35506 430 | -0.600313 431 | -1.1215 432 | -1.48902 433 | -1.37165 434 | -0.729557 435 | -0.616896 436 | -0.492073 437 | -0.574565 438 | -0.655314 439 | -1.14894 440 | -0.678461 441 | -0.784672 442 | -0.857207 443 | -0.649268 444 | -1.22077 445 | -0.1802 446 | -0.990957 447 | -0.117867 448 | -0.657184 449 | 0.44436 450 | 0.574518 451 | 0.947497 452 | 1.04101 453 | 1.13276 454 | 0.929168 455 | 0.672353 456 | 0.608977 457 | 0.843832 458 | 0.922923 459 | 0.550785 460 | 1.31502 461 | 1.43758 462 | 0.757483 463 | 0.645096 464 | 0.498192 465 | 1.01234 466 | 1.43033 467 | 1.25365 468 | 0.748514 469 | 0.614582 470 | 1.10283 471 | 1.41584 472 | 0.632137 473 | 0.582347 474 | 1.31906 475 | 0.664919 476 | 0.325375 477 | 1.07379 478 | 0.105117 479 | 0.441347 480 | 0.497486 481 | -0.485513 482 | -0.316166 483 | -0.501545 484 | -0.404064 485 | -0.755298 486 | -0.824467 487 | -0.706035 488 | -0.385643 489 | -0.666581 490 | -1.20929 491 | -1.36343 492 | -1.20676 493 | -1.15778 494 | -1.03301 495 | -1.11234 496 | -1.38518 497 | -1.15884 498 | -1.09348 499 | -1.23787 500 | -1.12946 501 | -1.32782 502 | -0.910221 503 | -1.32623 504 | -0.489889 505 | -1.04588 506 | -0.564011 507 | -1.23657 508 | -1.03791 509 | -0.896671 510 | -0.66212 511 | -0.951943 512 | -0.796473 513 | -0.393534 514 | 0.719505 515 | 0.498238 516 | 0.67666 517 | 0.185692 518 | 1.01303 519 | 0.704779 520 | 0.973572 521 | 0.874126 522 | 0.923726 523 | 1.31907 524 | 1.22284 525 | 0.650928 526 | 0.488503 527 | 0.656457 528 | 0.676365 529 | 0.789811 530 | 0.744476 531 | 0.752422 532 | 1.47197 533 | 1.16667 534 | 0.930812 535 | 0.429632 536 | 1.25717 537 | 0.610778 538 | 0.56166 539 | 0.447164 540 | 1.0527 541 | 0.285743 542 | 0.120466 543 | 0.376505 544 | 0.754375 545 | 0.425381 546 | -0.408274 547 | -0.057678 548 | -0.74825 549 | -0.803621 550 | -0.961612 551 | -0.327949 552 | -1.13205 553 | -0.429464 554 | -1.26852 555 | -0.981635 556 | -0.594175 557 | -1.43064 558 | -0.838603 559 | -1.11414 560 | -0.921358 561 | -0.553131 562 | -0.531068 563 | -1.28895 564 | -1.32256 565 | -0.976294 566 | -1.22891 567 | -0.677141 568 | -0.773139 569 | -1.14122 570 | -1.09493 571 | -0.957668 572 | -0.40632 573 | -0.458474 574 | -0.770515 575 | -0.513535 576 | -0.371773 577 | -0.290754 578 | 0.166017 579 | 0.18705 580 | 0.634651 581 | 1.13134 582 | 1.11609 583 | 1.10791 584 | 1.021 585 | 1.26367 586 | 1.16362 587 | 1.21206 588 | 1.19479 589 | 0.964222 590 | 1.43557 591 | 1.01869 592 | 0.813717 593 | 1.32723 594 | 1.3887 595 | 0.593998 596 | 0.702387 597 | 0.820617 598 | 0.504425 599 | 0.529334 600 | 0.628891 601 | 1.2662 602 | 0.489099 603 | 0.576338 604 | 0.29881 605 | 0.418441 606 | 1.0067 607 | 0.236739 608 | 0.024108 609 | -0.145891 610 | -0.345326 611 | -0.223981 612 | -0.844271 613 | -0.809427 614 | -0.528329 615 | -0.856629 616 | -1.31644 617 | -1.021 618 | -0.771794 619 | -1.13703 620 | -1.1383 621 | -1.21927 622 | -0.504123 623 | -0.682023 624 | -1.06503 625 | -1.2229 626 | -0.851628 627 | -0.591722 628 | -0.841764 629 | -0.65524 630 | -1.20768 631 | -0.884263 632 | -0.690129 633 | -1.0749 634 | -0.856774 635 | -0.721374 636 | -1.15279 637 | -0.844721 638 | -0.218654 639 | -0.101562 640 | -0.697491 641 | 0.147645 642 | -0.126267 643 | 0.233994 644 | 0.873575 645 | 0.569431 646 | 0.27186 647 | 1.11818 648 | 1.07035 649 | 0.881944 650 | 1.09841 651 | 0.745646 652 | 1.3816 653 | 0.709039 654 | 1.33432 655 | 0.64477 656 | 0.769477 657 | 0.901014 658 | 0.745603 659 | 0.63734 660 | 0.877734 661 | 0.764873 662 | 0.842817 663 | 1.32356 664 | 0.695622 665 | 0.600196 666 | 0.595966 667 | 0.781576 668 | 0.666702 669 | 1.01133 670 | 0.856762 671 | 0.492747 672 | 0.015277 673 | 0.159536 674 | 0.138116 675 | -0.651395 676 | -0.684743 677 | -0.350877 678 | -0.391004 679 | -0.961167 680 | -0.979106 681 | -1.06197 682 | -1.01969 683 | -1.35686 684 | -1.09764 685 | -0.785907 686 | -1.24143 687 | -0.969745 688 | -1.27113 689 | -0.677283 690 | -1.43576 691 | -1.41006 692 | -1.25471 693 | -0.880096 694 | -1.38694 695 | -1.12469 696 | -0.863922 697 | -0.637167 698 | -0.382642 699 | -0.668691 700 | -0.580907 701 | -0.32033 702 | -0.694582 703 | -0.870976 704 | -0.835839 705 | -0.295608 706 | 0.13451 707 | 0.482794 708 | 0.146941 709 | 0.307923 710 | 1.00809 711 | 0.323773 712 | 0.655356 713 | 0.944512 714 | 0.899687 715 | 0.563858 716 | 0.502301 717 | 0.874405 718 | 1.38873 719 | 0.878339 720 | 1.41901 721 | 1.39672 722 | 1.34964 723 | 1.25878 724 | 0.580269 725 | 1.35618 726 | 0.816799 727 | 1.01418 728 | 1.26091 729 | 0.629247 730 | 0.884695 731 | 0.313158 732 | 0.469116 733 | 0.954225 734 | 0.40605 735 | 0.622095 736 | 0.681391 737 | 0.392155 738 | -0.438125 739 | -0.35381 740 | -0.175238 741 | -0.607731 742 | -0.934199 743 | -0.405219 744 | -0.880207 745 | -0.554884 746 | -0.836844 747 | -0.843403 748 | -0.562512 749 | -1.45499 750 | -0.561657 751 | -1.47569 752 | -0.528915 753 | -1.09657 754 | -1.43495 755 | -1.18278 756 | -1.39136 757 | -0.641131 758 | -0.670317 759 | -0.650254 760 | -0.727478 761 | -0.395772 762 | -1.32054 763 | -0.504187 764 | -0.447527 765 | -0.715735 766 | -1.07524 767 | -0.082585 768 | -0.475012 769 | 0.243855 770 | -0.00558 771 | 0.669051 772 | 0.224499 773 | 0.40448 774 | 1.11479 775 | 1.09175 776 | 0.927179 777 | 1.24785 778 | 1.3603 779 | 0.946085 780 | 0.851013 781 | 1.32768 782 | 0.67826 783 | 0.808154 784 | 0.921592 785 | 1.35273 786 | 0.862748 787 | 0.669503 788 | 1.26938 789 | 1.13343 790 | 1.14628 791 | 0.88465 792 | 1.07382 793 | 0.952266 794 | 0.356884 795 | 0.379952 796 | 0.861732 797 | 0.851959 798 | 1.04045 799 | 0.644076 800 | 0.349128 801 | 0.048726 802 | -0.430317 803 | -0.720501 804 | -1.00684 805 | -1.11138 806 | -0.800046 807 | -0.727888 808 | -1.10715 809 | -0.735255 810 | -0.813949 811 | -0.734401 812 | -0.495497 813 | -1.45405 814 | -1.07233 815 | -0.565062 816 | -1.34513 817 | -0.597808 818 | -1.4889 819 | -0.547228 820 | -1.06692 821 | -1.02028 822 | -0.855689 823 | -0.520119 824 | -0.506998 825 | -0.849678 826 | -1.07089 827 | -1.13932 828 | -0.488364 829 | -0.99742 830 | -0.737558 831 | -0.66397 832 | -0.723891 833 | 0.449347 834 | -0.083138 835 | 0.545934 836 | 0.713614 837 | 0.336976 838 | 0.933231 839 | 1.05881 840 | 0.511073 841 | 0.478128 842 | 1.20076 843 | 1.3661 844 | 0.98202 845 | 0.921174 846 | 0.781613 847 | 1.35422 848 | 0.880321 849 | 0.649888 850 | 0.972128 851 | 0.745902 852 | 1.21584 853 | 1.16164 854 | 1.28314 855 | 1.11921 856 | 0.796954 857 | 1.27388 858 | 0.987887 859 | 0.787697 860 | 0.41581 861 | 0.406297 862 | 0.98721 863 | 0.263622 864 | 0.284675 865 | 0.25896 866 | 0.060459 867 | -0.185623 868 | -0.667796 869 | -0.822454 870 | -0.936838 871 | -0.694988 872 | -0.377307 873 | -0.871758 874 | -0.954109 875 | -0.512172 876 | -1.297 877 | -0.821246 878 | -1.33294 879 | -1.04888 880 | -1.23987 881 | -1.43897 882 | -1.14759 883 | -0.806371 884 | -1.38483 885 | -1.23131 886 | -0.946723 887 | -1.02576 888 | -0.934665 889 | -0.837968 890 | -0.422583 891 | -1.06021 892 | -0.741981 893 | -0.788164 894 | -0.827368 895 | -0.187407 896 | -0.838248 897 | -0.015182 898 | 0.614042 899 | 0.940218 900 | 0.811133 901 | 0.771681 902 | 0.24003 903 | 0.653798 904 | 0.71309 905 | 1.13153 906 | 1.15066 907 | 0.598875 908 | 0.478568 909 | 0.928108 910 | 0.596909 911 | 1.38173 912 | 0.525605 913 | 1.31881 914 | 0.8001 915 | 1.08611 916 | 1.02336 917 | 1.43426 918 | 0.860147 919 | 0.865068 920 | 0.503529 921 | 0.424646 922 | 1.23715 923 | 1.23851 924 | 0.922754 925 | 0.614679 926 | 1.07469 927 | 0.538755 928 | 0.739233 929 | -0.312754 930 | -0.655788 931 | -0.307625 932 | -0.759885 933 | -1.01748 934 | -0.927299 935 | -0.980951 936 | -0.700189 937 | -0.802216 938 | -0.602511 939 | -0.468653 940 | -1.1766 941 | -1.27127 942 | -1.17499 943 | -1.0772 944 | -1.23692 945 | -0.587243 946 | -1.44352 947 | -0.982172 948 | -0.498761 949 | -1.40946 950 | -0.779627 951 | -0.954917 952 | -1.25016 953 | -0.951611 954 | -1.1634 955 | -0.28604 956 | -0.628658 957 | -0.458014 958 | -0.166269 959 | -0.66482 960 | -0.704412 961 | -0.010243 962 | 0.480184 963 | 0.239785 964 | 0.641409 965 | 0.665074 966 | 0.95352 967 | 0.651742 968 | 0.435365 969 | 0.37543 970 | 0.696144 971 | 0.649362 972 | 1.42055 973 | 1.20891 974 | 0.5817 975 | 1.28275 976 | 1.25971 977 | 1.21914 978 | 1.07591 979 | 0.695721 980 | 0.519992 981 | 1.18566 982 | 0.5666 983 | 0.624344 984 | 1.12816 985 | 0.616671 986 | 0.95954 987 | 0.878063 988 | 0.435142 989 | 1.05332 990 | 0.801281 991 | 0.498638 992 | 0.380153 993 | 0.369007 994 | -0.618642 995 | -0.233806 996 | -0.626709 997 | -0.194532 998 | -1.20156 999 | -0.59755 1000 | -0.351324 1001 | -0.716795 1002 | -0.688482 1003 | -1.39195 1004 | -0.657205 1005 | -1.21353 1006 | -0.902874 1007 | -1.2816 1008 | -0.916986 1009 | -1.16347 1010 | -0.670522 1011 | -0.700612 1012 | -0.963138 1013 | -0.90528 1014 | -0.644119 1015 | -1.33772 1016 | -0.482115 1017 | -1.1983 1018 | -0.414568 1019 | -1.03781 1020 | -1.12596 1021 | -1.0211 1022 | -0.629429 1023 | -0.796315 1024 | -0.728081 1025 | -------------------------------------------------------------------------------- /Week1/Lecture1 Notebook.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": { 6 | "slideshow": { 7 | "slide_type": "slide" 8 | } 9 | }, 10 | "source": [ 11 | "This week's lecture topic: numerical errors (from textbook, chapter 4)\n", 12 | "\n", 13 | "In lab, we'll also do some Linux command-line basics" 14 | ] 15 | }, 16 | { 17 | "cell_type": "markdown", 18 | "metadata": { 19 | "slideshow": { 20 | "slide_type": "slide" 21 | }, 22 | "tags": [] 23 | }, 24 | "source": [ 25 | "# Numerical errors\n", 26 | "\n", 27 | "Aside from errors in programming or discretizing the physical model, there are 2 types of errors in computations:\n", 28 | "1. Rounding errors: errors in how the computer stores or manipulates numbers.\n", 29 | "2. Approximation errors (sometimes called truncation errors): errors in approximations to various functions or methods.\n", 30 | "\n", 31 | "Reason: computers are machines, with inherent limitations." 32 | ] 33 | }, 34 | { 35 | "cell_type": "markdown", 36 | "metadata": { 37 | "slideshow": { 38 | "slide_type": "subslide" 39 | }, 40 | "tags": [] 41 | }, 42 | "source": [ 43 | "## Integers and floats\n", 44 | "\n", 45 | "How does a computer represent a number?\n", 46 | "\n", 47 | "* Variables `a`, `b` and `c` below are all equal to 1000.\n", 48 | "* Yet, `a` is treated very differently by da Python than `b` and `c`:" 49 | ] 50 | }, 51 | { 52 | "cell_type": "code", 53 | "execution_count": 15, 54 | "metadata": { 55 | "scrolled": true, 56 | "tags": [] 57 | }, 58 | "outputs": [ 59 | { 60 | "name": "stdout", 61 | "output_type": "stream", 62 | "text": [ 63 | "The type of a is \n", 64 | "The type of a is \n", 65 | "The type of a is \n" 66 | ] 67 | } 68 | ], 69 | "source": [ 70 | "a = 1000\n", 71 | "b = 1000.\n", 72 | "c = 1e3\n", 73 | "print('The type of a is', type(a))\n", 74 | "print('The type of a is', type(b))\n", 75 | "print('The type of a is', type(c))\n" 76 | ] 77 | }, 78 | { 79 | "cell_type": "markdown", 80 | "metadata": { 81 | "tags": [] 82 | }, 83 | "source": [ 84 | "## Error #1: rounding errors" 85 | ] 86 | }, 87 | { 88 | "cell_type": "markdown", 89 | "metadata": { 90 | "slideshow": { 91 | "slide_type": "subslide" 92 | }, 93 | "tags": [] 94 | }, 95 | "source": [ 96 | "### General principle\n", 97 | "\n", 98 | "* Integer numbers: python does not limit the number of digits stored. Can cause problems!" 99 | ] 100 | }, 101 | { 102 | "cell_type": "code", 103 | "execution_count": 13, 104 | "metadata": { 105 | "scrolled": true, 106 | "slideshow": { 107 | "slide_type": "subslide" 108 | } 109 | }, 110 | "outputs": [ 111 | { 112 | "name": "stdout", 113 | "output_type": "stream", 114 | "text": [ 115 | "10715086071862673209484250490600018105614048117055336074437503883703510511249361224931983788156958581275946729175531468251871452856923140435984577574698574803934567774824230985421074605062371141877954182153046474983581941267398767559165543946077062914571196477686542167660429831652624386837205668069376\n" 116 | ] 117 | } 118 | ], 119 | "source": [ 120 | "from numpy import iinfo, int32, int64\n", 121 | "#iinfo(int32).max\n", 122 | "print(2**1000)\n" 123 | ] 124 | }, 125 | { 126 | "cell_type": "markdown", 127 | "metadata": { 128 | "slideshow": { 129 | "slide_type": "subslide" 130 | } 131 | }, 132 | "source": [ 133 | "* Non-integer numbers with more than 16 significant figures: **rounding** after 16 figures.\n", 134 | "\n", 135 | "* Each mathematical operation (FLOP, Floating-point Operation) introduces errors in the 16th digit. You can’t assume \n", 136 | "\n", 137 | " `1.3 + 2.4 = 3.7`, \n", 138 | "\n", 139 | " it might be \n", 140 | "\n", 141 | " `3.699999999999999` \n", 142 | " \n", 143 | " even though the 2 numbers you are adding only have 2 significant figures.\n", 144 | "\n", 145 | "* Know this, accept it, work with it. You are better at adding 1.3 and 2.4 than your computer, but you are much slower. This is why we use computers." 146 | ] 147 | }, 148 | { 149 | "cell_type": "markdown", 150 | "metadata": { 151 | "slideshow": { 152 | "slide_type": "subslide" 153 | } 154 | }, 155 | "source": [ 156 | "There are also limitations as to the largest number representable in Python, the closest to zero, etc.\n", 157 | "If you want to know what they are on your machine, you can run the code below:" 158 | ] 159 | }, 160 | { 161 | "cell_type": "code", 162 | "execution_count": 14, 163 | "metadata": { 164 | "slideshow": { 165 | "slide_type": "subslide" 166 | } 167 | }, 168 | "outputs": [ 169 | { 170 | "name": "stdout", 171 | "output_type": "stream", 172 | "text": [ 173 | "attributes you can access in finfo(float64) ['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', '_finfo_cache', '_init', '_machar', '_str_eps', '_str_epsneg', '_str_max', '_str_resolution', '_str_smallest_normal', '_str_smallest_subnormal', '_str_tiny', 'bits', 'dtype', 'eps', 'epsneg', 'iexp', 'machar', 'machep', 'max', 'maxexp', 'min', 'minexp', 'negep', 'nexp', 'nmant', 'precision', 'resolution', 'smallest_normal', 'smallest_subnormal', 'tiny']\n" 174 | ] 175 | } 176 | ], 177 | "source": [ 178 | "from numpy import finfo, float64, float32\n", 179 | "# float64 contains double-precision floats, float32 is single-precision\n", 180 | "print(\"attributes you can access in finfo(float64) \", dir(finfo(float64)))\n" 181 | ] 182 | }, 183 | { 184 | "cell_type": "code", 185 | "execution_count": 28, 186 | "metadata": { 187 | "slideshow": { 188 | "slide_type": "subslide" 189 | } 190 | }, 191 | "outputs": [ 192 | { 193 | "name": "stdout", 194 | "output_type": "stream", 195 | "text": [ 196 | "maximum numbers in 64 bit and 32 bit precision: 1.7976931348623157e+308 3.4028235e+38\n" 197 | ] 198 | } 199 | ], 200 | "source": [ 201 | "print( \"maximum numbers in 64 bit and 32 bit precision: \",\n", 202 | " finfo(float64).max, finfo(float32).max)\n" 203 | ] 204 | }, 205 | { 206 | "cell_type": "code", 207 | "execution_count": 29, 208 | "metadata": { 209 | "slideshow": { 210 | "slide_type": "fragment" 211 | } 212 | }, 213 | "outputs": [ 214 | { 215 | "name": "stdout", 216 | "output_type": "stream", 217 | "text": [ 218 | "minimum numbers in 64 bit and 32 bit precision: -1.7976931348623157e+308 -3.4028235e+38\n" 219 | ] 220 | } 221 | ], 222 | "source": [ 223 | "print( \"minimum numbers in 64 bit and 32 bit precision: \",\n", 224 | " finfo(float64).min, finfo(float32).min)\n" 225 | ] 226 | }, 227 | { 228 | "cell_type": "code", 229 | "execution_count": 30, 230 | "metadata": { 231 | "slideshow": { 232 | "slide_type": "fragment" 233 | } 234 | }, 235 | "outputs": [ 236 | { 237 | "name": "stdout", 238 | "output_type": "stream", 239 | "text": [ 240 | "epsilon for 64 bit and 32 bit: 2.220446049250313e-16 1.1920929e-07\n" 241 | ] 242 | } 243 | ], 244 | "source": [ 245 | "print( \"epsilon for 64 bit and 32 bit: \",\n", 246 | " finfo(float64).eps, finfo(float32).eps)\n" 247 | ] 248 | }, 249 | { 250 | "cell_type": "code", 251 | "execution_count": 15, 252 | "metadata": { 253 | "slideshow": { 254 | "slide_type": "subslide" 255 | } 256 | }, 257 | "outputs": [ 258 | { 259 | "name": "stdout", 260 | "output_type": "stream", 261 | "text": [ 262 | "Should be epsilon for this machine if it's 64 bit: 2.220446049250313e-16\n" 263 | ] 264 | } 265 | ], 266 | "source": [ 267 | "print( \"Should be epsilon for this machine if it's 64 bit:\",\n", 268 | " float64(1)+finfo(float64).eps-float64(1))\n" 269 | ] 270 | }, 271 | { 272 | "cell_type": "markdown", 273 | "metadata": { 274 | "slideshow": { 275 | "slide_type": "fragment" 276 | } 277 | }, 278 | "source": [ 279 | "This tells us that the error made on a given evaluation (here `float64(1)`) is always the same for a given software and machine.\n", 280 | "\n", 281 | "It may vary across software and hardware configurations." 282 | ] 283 | }, 284 | { 285 | "cell_type": "code", 286 | "execution_count": 16, 287 | "metadata": { 288 | "slideshow": { 289 | "slide_type": "subslide" 290 | } 291 | }, 292 | "outputs": [ 293 | { 294 | "name": "stdout", 295 | "output_type": "stream", 296 | "text": [ 297 | "Should be zero, and it is 0.0\n" 298 | ] 299 | } 300 | ], 301 | "source": [ 302 | "print( \"Should be zero, and it is\",\n", 303 | " float64(1)+finfo(float64).eps/2.0-float64(1))\n" 304 | ] 305 | }, 306 | { 307 | "cell_type": "markdown", 308 | "metadata": { 309 | "slideshow": { 310 | "slide_type": "fragment" 311 | } 312 | }, 313 | "source": [ 314 | "Here, adding 1/2-of the roundoff error to one didn't register, it was too small of Python to \"notice\"." 315 | ] 316 | }, 317 | { 318 | "cell_type": "markdown", 319 | "metadata": { 320 | "slideshow": { 321 | "slide_type": "subslide" 322 | }, 323 | "tags": [] 324 | }, 325 | "source": [ 326 | "### Simple examples" 327 | ] 328 | }, 329 | { 330 | "cell_type": "markdown", 331 | "metadata": { 332 | "slideshow": { 333 | "slide_type": "subslide" 334 | } 335 | }, 336 | "source": [ 337 | "7/3 - 4/3 - 1 = 0 , right? Well..." 338 | ] 339 | }, 340 | { 341 | "cell_type": "code", 342 | "execution_count": 17, 343 | "metadata": {}, 344 | "outputs": [ 345 | { 346 | "data": { 347 | "text/plain": [ 348 | "2.220446049250313e-16" 349 | ] 350 | }, 351 | "execution_count": 17, 352 | "metadata": {}, 353 | "output_type": "execute_result" 354 | } 355 | ], 356 | "source": [ 357 | "7./3. - 4./3 - 1.\n" 358 | ] 359 | }, 360 | { 361 | "cell_type": "markdown", 362 | "metadata": { 363 | "cell_style": "split", 364 | "slideshow": { 365 | "slide_type": "subslide" 366 | }, 367 | "tags": [] 368 | }, 369 | "source": [ 370 | "And how about this one: under what circumstances is the following possible?\n", 371 | "$$(x+y)+z \\neq x + (y + z)$$" 372 | ] 373 | }, 374 | { 375 | "cell_type": "code", 376 | "execution_count": 7, 377 | "metadata": { 378 | "cell_style": "split", 379 | "slideshow": { 380 | "slide_type": "subslide" 381 | } 382 | }, 383 | "outputs": [ 384 | { 385 | "name": "stdout", 386 | "output_type": "stream", 387 | "text": [ 388 | "(x+y)+z = 1.0\n", 389 | "x+(y+z) = 0.0\n" 390 | ] 391 | } 392 | ], 393 | "source": [ 394 | "x = 1.e16\n", 395 | "y = -1.e16\n", 396 | "z = 1.\n", 397 | "\n", 398 | "print(\"(x+y)+z = {}\".format((x+y)+z))\n", 399 | "print(\"x+(y+z) = {}\".format(x+(y+z)))\n" 400 | ] 401 | }, 402 | { 403 | "cell_type": "markdown", 404 | "metadata": { 405 | "cell_style": "split", 406 | "slideshow": { 407 | "slide_type": "subslide" 408 | }, 409 | "tags": [] 410 | }, 411 | "source": [ 412 | "### Error constant\n", 413 | "\n", 414 | "* Newman defines $\\sigma = C |x|$.\n", 415 | " * $x =$ number you want to represent.\n", 416 | " * $\\sigma =$ standard deviation of error \n", 417 | " * $C =$ fractional error for a single floating point number" 418 | ] 419 | }, 420 | { 421 | "cell_type": "markdown", 422 | "metadata": { 423 | "cell_style": "split", 424 | "slideshow": { 425 | "slide_type": "-" 426 | } 427 | }, 428 | "source": [ 429 | "* For 64 bit float: $C = O(10^{-16}) \\sim$ machine precision $\\epsilon_M$.\n", 430 | "* We cannot know a number better than this on the computer (otherwise it wouldn't be a limit on the precision).\n", 431 | "* This fractional error is different on different computers but should not depend on $x$.\n", 432 | "\n", 433 | "Note: **Fractional error** of a calculated value is not the same as **relative error** of a calculated value compared to a true value. Relative error of calculated value $x$ compared to true value $y$ is $(x-y)/y$." 434 | ] 435 | }, 436 | { 437 | "cell_type": "markdown", 438 | "metadata": { 439 | "slideshow": { 440 | "slide_type": "subslide" 441 | }, 442 | "tags": [] 443 | }, 444 | "source": [ 445 | "### Propagation of errors\n", 446 | "\n", 447 | "Errors propagate statistically like they do in experimental physics." 448 | ] 449 | }, 450 | { 451 | "cell_type": "markdown", 452 | "metadata": { 453 | "slideshow": { 454 | "slide_type": "subslide" 455 | } 456 | }, 457 | "source": [ 458 | "These errors in differences become **very important** when taking numerical derivatives:\n", 459 | "$$\\frac{df}{dt} \\approx \\frac{f_{i+1} - f_i}{\\Delta t} = \\text{danger zone}$$\n", 460 | "as we will see later." 461 | ] 462 | }, 463 | { 464 | "cell_type": "markdown", 465 | "metadata": { 466 | "slideshow": { 467 | "slide_type": "subslide" 468 | }, 469 | "tags": [] 470 | }, 471 | "source": [ 472 | "### One important rule\n", 473 | "\n", 474 | "Never, ever. Never ever ever. Do something like this:" 475 | ] 476 | }, 477 | { 478 | "cell_type": "code", 479 | "execution_count": 43, 480 | "metadata": { 481 | "slideshow": { 482 | "slide_type": "-" 483 | } 484 | }, 485 | "outputs": [ 486 | { 487 | "name": "stdout", 488 | "output_type": "stream", 489 | "text": [ 490 | "7/3 - 4/3 - 1 != 0\n" 491 | ] 492 | } 493 | ], 494 | "source": [ 495 | "if 7./3. - 4./3. - 1. == 0:\n", 496 | " print('7/3 - 4/3 - 1 == 0')\n", 497 | "else:\n", 498 | " print('7/3 - 4/3 - 1 != 0')\n", 499 | " " 500 | ] 501 | }, 502 | { 503 | "cell_type": "markdown", 504 | "metadata": { 505 | "slideshow": { 506 | "slide_type": "subslide" 507 | } 508 | }, 509 | "source": [ 510 | "Instead:" 511 | ] 512 | }, 513 | { 514 | "cell_type": "code", 515 | "execution_count": 44, 516 | "metadata": { 517 | "slideshow": { 518 | "slide_type": "fragment" 519 | } 520 | }, 521 | "outputs": [ 522 | { 523 | "name": "stdout", 524 | "output_type": "stream", 525 | "text": [ 526 | "7/3 - 4/3 - 1 == 0 (or close enough anyway...)\n" 527 | ] 528 | } 529 | ], 530 | "source": [ 531 | "delta = 1e-15\n", 532 | "if abs(7./3. - 4./3. - 1.) < delta:\n", 533 | " print('7/3 - 4/3 - 1 == 0 (or close enough anyway...)')\n", 534 | "else:\n", 535 | " print('7/3 - 4/3 - 1 != 0')\n", 536 | " " 537 | ] 538 | }, 539 | { 540 | "cell_type": "markdown", 541 | "metadata": { 542 | "slideshow": { 543 | "slide_type": "subslide" 544 | }, 545 | "tags": [] 546 | }, 547 | "source": [ 548 | "## Error #2: Approximation errors\n", 549 | "\n", 550 | "* errors introduced in functions due to approximations\n", 551 | "* very important to consider for **integration** & **differentiation** algorithms\n", 552 | "* we approximate these operations and there is an error in that approximation\n", 553 | "* Most integration/differentiation algorithms are somehow based on Taylor series expansions, so you can usually figure out the approximation error by looking at the terms in the Taylor expansion that you ignore.\n", 554 | "\n", 555 | "This should become clearer once we illustrate it with numerical integrations. We'll also do a simple illustration in the informal lab exercises." 556 | ] 557 | }, 558 | { 559 | "cell_type": "code", 560 | "execution_count": null, 561 | "metadata": {}, 562 | "outputs": [], 563 | "source": [] 564 | } 565 | ], 566 | "metadata": { 567 | "celltoolbar": "Slideshow", 568 | "kernelspec": { 569 | "display_name": "Python 3 (ipykernel)", 570 | "language": "python", 571 | "name": "python3" 572 | }, 573 | "language_info": { 574 | "codemirror_mode": { 575 | "name": "ipython", 576 | "version": 3 577 | }, 578 | "file_extension": ".py", 579 | "mimetype": "text/x-python", 580 | "name": "python", 581 | "nbconvert_exporter": "python", 582 | "pygments_lexer": "ipython3", 583 | "version": "3.11.9" 584 | }, 585 | "latex_metadata": { 586 | "affiliation": "PHY407, University of Toronto", 587 | "author": "Nico Grisouard", 588 | "title": "Lecture 1: python basics" 589 | } 590 | }, 591 | "nbformat": 4, 592 | "nbformat_minor": 4 593 | } 594 | -------------------------------------------------------------------------------- /Week7/SimplePDEs.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": { 6 | "slideshow": { 7 | "slide_type": "slide" 8 | } 9 | }, 10 | "source": [ 11 | "*Supporting textbook chapters for week 8: Chapters 9.1 - 9.3* \n", 12 | "\n", 13 | "Topics:\n", 14 | "* Classifying PDEs\n", 15 | "* Elliptic equation solvers: Jacobi, Gauss-Seidel, overrelaxation\n", 16 | "* Parabolic equation solver: Explicit FTCS (Forward Time, Centered Space)\n", 17 | "* Parabolic and hyperbolic equation soliver: Implicit FTCS, Crank-Nicolson" 18 | ] 19 | }, 20 | { 21 | "cell_type": "markdown", 22 | "metadata": {}, 23 | "source": [ 24 | "# Classification and General Approach" 25 | ] 26 | }, 27 | { 28 | "cell_type": "markdown", 29 | "metadata": { 30 | "slideshow": { 31 | "slide_type": "subslide" 32 | } 33 | }, 34 | "source": [ 35 | "* Solving partial differential equations is one of the pinnacles of computational physics, bringing together many methods.\n", 36 | "* Each type comes with design decisions on how to discretize and implement numerical methods,\n", 37 | "* Stability is crucial.\n", 38 | "* Accuracy is crucial too." 39 | ] 40 | }, 41 | { 42 | "cell_type": "markdown", 43 | "metadata": { 44 | "cell_style": "split", 45 | "jp-MarkdownHeadingCollapsed": true, 46 | "slideshow": { 47 | "slide_type": "slide" 48 | }, 49 | "tags": [] 50 | }, 51 | "source": [ 52 | "Recall conical equations in geometry:\n", 53 | "$$ \\alpha x^2 + \\beta xy + \\gamma y^2 + \\delta x + \\varepsilon y = f, $$\n", 54 | "classified using $ \\Delta = \\beta^2 - 4\\alpha\\gamma.$\n", 55 | "\n", 56 | "1. $\\Delta = 0$: equation for a parabola,\n", 57 | "3. $\\Delta < 0$: equation for an ellipse,\n", 58 | "2. $\\Delta > 0$: equation for a hyperbola." 59 | ] 60 | }, 61 | { 62 | "cell_type": "markdown", 63 | "metadata": { 64 | "slideshow": { 65 | "slide_type": "subslide" 66 | } 67 | }, 68 | "source": [ 69 | "Similar for PDEs:\n", 70 | "$$ \\alpha\\frac{\\partial^2 \\phi}{\\partial x^2} + \\beta\\frac{\\partial^2 \\phi}{\\partial x\\partial y} + \\gamma\\frac{\\partial^2 \\phi}{\\partial y^2} + \\delta \\frac{\\partial\\phi}{\\partial x} + \\varepsilon\\frac{\\partial\\phi}{\\partial y} = f.$$\n" 71 | ] 72 | }, 73 | { 74 | "cell_type": "markdown", 75 | "metadata": { 76 | "cell_style": "split", 77 | "slideshow": { 78 | "slide_type": "subslide" 79 | } 80 | }, 81 | "source": [ 82 | "With $\\Delta = \\beta^2 - 4\\alpha\\gamma$,\n", 83 | "1. $\\Delta = 0$: parabolic PDE,\n", 84 | "3. $\\Delta < 0$: elliptic PDE,\n", 85 | "2. $\\Delta > 0$: hyperbolic PDE." 86 | ] 87 | }, 88 | { 89 | "cell_type": "markdown", 90 | "metadata": {}, 91 | "source": [ 92 | "## Physics Examples" 93 | ] 94 | }, 95 | { 96 | "cell_type": "markdown", 97 | "metadata": { 98 | "slideshow": { 99 | "slide_type": "subslide" 100 | } 101 | }, 102 | "source": [ 103 | "1. Canonical parabolic PDE: the diffusion equation, $ \\kappa \\frac{\\partial ^2 T}{\\partial x^2} - \\frac{\\partial T}{\\partial t} = 0$,\n", 104 | " $$x\\to x,\\quad y\\to t,\\quad \\alpha \\to \\kappa,\\quad \\varepsilon \\to - 1,\\quad \\beta, \\gamma, \\delta, f \\to 0 \\quad\\Rightarrow\\quad \\beta^2 - 4\\alpha\\gamma = 0.$$\n", 105 | "2. Canonical elliptic PDE: the Poisson equation, $\\ \\nabla^2 \\phi = \\rho$,\n", 106 | " $$x\\to x,\\quad y\\to y,\\quad\\alpha, \\gamma \\to 1, f \\to \\rho, \\beta, \\delta, \\varepsilon \\to 0 \\quad \\Rightarrow \\quad \\beta^2 - 4\\alpha\\gamma = -4<0.$$\n", 107 | " * e.g. 2D electrostatics, with electric potential $\\phi$ s.t. $\\vec E = \\nabla \\phi$, in the absence of charges $(\\rho \\equiv 0)$, have Gauss' law:\n", 108 | "$\\frac{\\partial^2 \\phi}{\\partial x^2} + \\frac{\\partial^2 \\phi}{\\partial y^2} = 0$\n", 109 | "3. Canonical hyperbolic PDE: the wave equation, $\\ \\displaystyle c^2 \\frac{\\partial ^2 \\phi}{\\partial x^2} - \\frac{\\partial^2 \\phi}{\\partial t^2} = 0.$\n", 110 | " $$x\\to x,\\quad y\\to t,\\quad \\alpha \\to c^2,\\quad \\gamma \\to - 1,\\quad \\beta, \\delta, \\varepsilon, f \\to 0 \\quad\\Rightarrow\\quad \\beta^2 - 4\\alpha\\gamma = 4c^2 >0.$$" 111 | ] 112 | }, 113 | { 114 | "cell_type": "markdown", 115 | "metadata": { 116 | "slideshow": { 117 | "slide_type": "subslide" 118 | } 119 | }, 120 | "source": [ 121 | "Note: we use these categorizations even when the spatial operator is $\\nabla^2 = \\partial_x^2 + \\partial_y^2 + \\partial_z^2$, i.e., for 4D PDEs." 122 | ] 123 | }, 124 | { 125 | "cell_type": "markdown", 126 | "metadata": { 127 | "slideshow": { 128 | "slide_type": "subslide" 129 | } 130 | }, 131 | "source": [ 132 | "## General approach\n", 133 | "\n", 134 | "* Discretize system spatially and temporally: can use finite difference, spectral coefficients, etc.\n", 135 | "* $\\Rightarrow$ set of coupled ODEs that you need to solve in an efficient way.\n", 136 | "* Spatial derivatives bring information in from neighbouring points $\\Rightarrow$ coupling,\n", 137 | "* $\\Rightarrow$ errors depend on space and time and can get wave-like characteristics.\n", 138 | "* For 2nd derivatives, recall central difference calculation (§5.10.5, p.197):\n", 139 | "$$f''(x) = \\frac{f(x+h) - 2f(x)+ f(x-h)}{h^2} - \\frac{1}{12}h^2 f^{(4)}(x) + \\dots{}$$" 140 | ] 141 | }, 142 | { 143 | "cell_type": "markdown", 144 | "metadata": {}, 145 | "source": [ 146 | "# Elliptic Equations" 147 | ] 148 | }, 149 | { 150 | "cell_type": "markdown", 151 | "metadata": { 152 | "slideshow": { 153 | "slide_type": "subslide" 154 | } 155 | }, 156 | "source": [ 157 | "Start with simplest case of Gauss's Law with 2D Laplacian:\n", 158 | "$$0 = \\nabla^2 \\phi = \\frac{\\partial^2 \\phi}{\\partial x^2} + \\frac{\\partial^2 \\phi}{\\partial y^2},$$\n", 159 | "\n", 160 | "On regular square grid of cell side length $a$, finite difference form is\n", 161 | "\\begin{align}\n", 162 | "\\frac{\\partial^2 \\phi}{\\partial x^2} & \\approx \\frac{\\phi(x+a, y) - 2\\phi(x, y)+ \\phi(x-a, y)}{a^2},\\\\\n", 163 | "\\frac{\\partial^2 \\phi}{\\partial y^2} & \\approx \\frac{\\phi(x, y+a) - 2\\phi(x, y)+ \\phi(x, y-a)}{a^2}.\n", 164 | "\\end{align}\n", 165 | "Gauss's law then becomes:\n", 166 | "$$0 \\approx \\phi(x+a, y) + \\phi(x-a, y) + \\phi(x, y+a) + \\phi(x, y-a) - 4\\phi(x, y)$$\n", 167 | "at each location $(x, y)$." 168 | ] 169 | }, 170 | { 171 | "cell_type": "markdown", 172 | "metadata": { 173 | "slideshow": { 174 | "slide_type": "subslide" 175 | } 176 | }, 177 | "source": [ 178 | "* Put together a series of equations of the form\n", 179 | " $$\\phi(x+a, y) + \\phi(x-a, y) + \\phi(x, y+a) + \\phi(x, y-a) - 4\\phi(x) = 0$$\n", 180 | " for each $x$ and $y$, subject to boundary conditions.\n", 181 | "* $\\phi$ or derivative $\\partial\\phi/\\partial \\xi$ ($\\xi = x,\\ y,$ or both) given on boundary.\n", 182 | " * If $\\phi$ given, use this value for adjacent points.\n", 183 | " * If $\\partial\\phi/\\partial \\xi$ given, find algebraic relationship between points near to boundary using finite difference.\n", 184 | "* Could solve using matrix methods $\\mathbf L \\phi = \\mathbf R \\phi$, but a simpler method is possible.\n" 185 | ] 186 | }, 187 | { 188 | "cell_type": "markdown", 189 | "metadata": {}, 190 | "source": [ 191 | "## Jacobi relaxation method" 192 | ] 193 | }, 194 | { 195 | "cell_type": "markdown", 196 | "metadata": { 197 | "slideshow": { 198 | "slide_type": "slide" 199 | } 200 | }, 201 | "source": [ 202 | "$$\\phi(x+a, y) + \\phi(x-a, y) + \\phi(x, y+a) + \\phi(x, y-a) - 4\\phi(x) = 0$$\n", 203 | "* Iterate the rule\n", 204 | "$\\phi_{new}(x, y) = \\frac14\\left[\\phi(x+a, y) + \\phi(x-a, y) + \\phi(x, y+a) + \\phi(x, y-a)\\right].$\n", 205 | "* Much like the relaxation method for finding solutions of $f(x) = x$,\n", 206 | "* For this problem it turns out that Jacobi Relaxation is always stable and so always gives a solution!" 207 | ] 208 | }, 209 | { 210 | "cell_type": "markdown", 211 | "metadata": { 212 | "slideshow": { 213 | "slide_type": "subslide" 214 | } 215 | }, 216 | "source": [ 217 | "## Overrelaxation method\n", 218 | "\n", 219 | "$\\phi_{new}(x,y) = $\n", 220 | "$$(1+\\omega)\\left[\\frac{\\phi(x+a, y) + \\phi(x-a, y) + \\phi(x, y+a) + \\phi(x, y-a)}4\\right] - \\omega \\phi (x,y)$$\n", 221 | "* When it works, it usually speeds up the calculation.\n", 222 | "* Not always stable! How to choose $\\omega$ is not always reproducible.\n", 223 | "* see Newman's exercise 6.11 for a similar problem for finding $f(x)=x$." 224 | ] 225 | }, 226 | { 227 | "cell_type": "markdown", 228 | "metadata": { 229 | "slideshow": { 230 | "slide_type": "slide" 231 | } 232 | }, 233 | "source": [ 234 | "## Gauss-Seidel method\n", 235 | "\n", 236 | "* Replace function on the fly as in\n", 237 | "$$\\phi(x, y) \\leftarrow \\frac{\\phi(x+a, y) + \\phi(x-a, y) + \\phi(x, y+a) + \\phi(x, y-a)}4.$$\n", 238 | "* Crucial difference: the LHS is $\\phi$, not $\\phi_{new}$: we use newer values as they are being computed (Jacobi used only old values to compute new one).\n", 239 | "* This can be shown to run faster.\n", 240 | "* Can be combined with overrelaxation." 241 | ] 242 | }, 243 | { 244 | "cell_type": "markdown", 245 | "metadata": {}, 246 | "source": [ 247 | "# Parabolic PDEs" 248 | ] 249 | }, 250 | { 251 | "cell_type": "markdown", 252 | "metadata": {}, 253 | "source": [ 254 | "* Consider the 1D heat equation:\n", 255 | "$$\\frac{\\partial T}{\\partial t} = \\kappa\\frac{\\partial^2 T}{\\partial x^2},$$\n", 256 | "* B.Cs.:\n", 257 | " $$T(x=0, t) = T_0, \\quad T(x=L, t) = T_L.$$\n", 258 | "* I.C.:\n", 259 | " $$T(x, t=0) = T_0 + (T_L - T_0)\\left(\\frac{f(x) - f(0)}{f(L) - f(0)}\\right)$$" 260 | ] 261 | }, 262 | { 263 | "cell_type": "markdown", 264 | "metadata": { 265 | "cell_style": "center", 266 | "slideshow": { 267 | "slide_type": "slide" 268 | } 269 | }, 270 | "source": [ 271 | "## Explicit Forward Time Centred Space method" 272 | ] 273 | }, 274 | { 275 | "cell_type": "markdown", 276 | "metadata": { 277 | "cell_style": "center", 278 | "slideshow": { 279 | "slide_type": "subslide" 280 | } 281 | }, 282 | "source": [ 283 | "**Step 1: Discretize in space**\n", 284 | "\n", 285 | "$\\displaystyle x_m = \\frac{m}{M}L = am, \\quad m=0\\dots{}M, \\quad a = \\frac{L}M$,\n", 286 | "\n", 287 | "$T_m(t) = \\left[T_0(t), \\dots{}, T_{M}(t)\\right]$\n", 288 | "\n", 289 | "$\\displaystyle \\left.\\frac{\\partial^2 T}{\\partial x^2}\\right|_{x=x_m, t} \\approx \\frac{T_{m+1} - 2 T_m + T_{m-1}}{a^2}\\quad$ (\"centred space\", CS)" 290 | ] 291 | }, 292 | { 293 | "cell_type": "markdown", 294 | "metadata": { 295 | "slideshow": { 296 | "slide_type": "subslide" 297 | } 298 | }, 299 | "source": [ 300 | "**Step 2: Discretize in time**\n", 301 | "\n", 302 | "$\\displaystyle \\frac{d T_m}{d t} \\approx \\kappa\\frac{T_{m+1} - 2 T_m + T_{m-1}}{a^2},\\quad m = 1 \\dots{}, M-1$\n", 303 | "\n", 304 | "Let $t_n = nh$, $h$ the time step. Let $T_m(t_n) \\equiv T_m^n$.\n", 305 | "\n", 306 | "$\\displaystyle \\Rightarrow \\left.\\frac{\\partial T}{\\partial t}\\right|_{x=ma, t=nh} \\approx \\frac{T_{m}^{n+1} - T_m^n}{h} \\equiv \\kappa\\frac{T_{m+1}^n - 2 T_m^n + T_{m-1}^n}{a^2}$ (\"Forward Time\", FT).\n", 307 | "\n", 308 | "$\\Rightarrow$ **Explicit FTCS method:**\n", 309 | "$$\\boxed{T_m^{n+1} = T_m^n + \\frac{\\kappa h}{a^2}\\left(T_{m+1}^n - 2 T_m^n + T_{m-1}^n\\right)}.$$" 310 | ] 311 | }, 312 | { 313 | "cell_type": "markdown", 314 | "metadata": { 315 | "slideshow": { 316 | "slide_type": "slide" 317 | } 318 | }, 319 | "source": [ 320 | "It may be easier to understand by writing the problem as a set of ODEs\n", 321 | "$$\\frac{\\partial \\phi_m}{\\partial t} = \\psi_m, \\quad \\text{and}\\quad \\frac{\\partial \\psi_m}{\\partial t} = \\frac{c^2}{a^2}\\left(\\phi_{m+1} - 2\\phi_m + \\phi_{m-1}\\right)$$\n", 322 | "\n", 323 | "and the discretization in time as:\n", 324 | "$$\n", 325 | " \\begin{bmatrix}\n", 326 | " \\phi_m^{n+1} \\\\\n", 327 | " \\psi_m^{n+1}\n", 328 | " \\end{bmatrix}\n", 329 | " = \n", 330 | " \\begin{bmatrix}\n", 331 | " 1 & +h \\\\\n", 332 | " -\\frac{2hc^2}{a^2} & 1\n", 333 | " \\end{bmatrix}\n", 334 | " \\begin{bmatrix}\n", 335 | " \\phi_m^{n} \\\\\n", 336 | " \\psi_m^{n}\n", 337 | " \\end{bmatrix}\n", 338 | " +\n", 339 | " \\begin{bmatrix}\n", 340 | " 0 \\\\\n", 341 | " \\frac{c^2 h}{a^2}\\left(\\phi_{m+1}^n + \\phi_{m-1}^n\\right)\n", 342 | " \\end{bmatrix}\n", 343 | "$$" 344 | ] 345 | }, 346 | { 347 | "cell_type": "markdown", 348 | "metadata": {}, 349 | "source": [ 350 | "## Implicit FTCS Method" 351 | ] 352 | }, 353 | { 354 | "cell_type": "markdown", 355 | "metadata": {}, 356 | "source": [ 357 | "Evaluate the RHS of the above at time $t+h$ instead of $t$" 358 | ] 359 | }, 360 | { 361 | "cell_type": "markdown", 362 | "metadata": { 363 | "slideshow": { 364 | "slide_type": "subslide" 365 | } 366 | }, 367 | "source": [ 368 | "* first do $h\\to -h$ (from the current time step, compute the *previous* one):\n", 369 | " \\begin{align*}\n", 370 | " \\phi_m^{n-1} & = \\phi_m^{n} - h\\psi_m^{n},\\\\\n", 371 | " \\psi_m^{n-1} & = \\psi_m^{n} - h\\frac{c^2}{a^2}\\left(\\phi_{m-1}^{n} + \\phi_{m+1}^{n} - 2\\phi_m^{n}\\right),\n", 372 | " \\end{align*}\n", 373 | "* Then, $n \\to n+1$ (one shift forward in time):\n", 374 | " \\begin{align*}\n", 375 | " \\phi_m^{n} & = \\phi_m^{n+1} - h\\psi_m^{n+1},\\\\\n", 376 | " \\psi_m^{n} & = \\psi_m^{n+1} - h\\frac{c^2}{a^2}\\left(\\phi_{m-1}^{n+1} + \\phi_{m+1}^{n+1} - 2\\phi_m^{n+1}\\right),\n", 377 | " \\end{align*}\n", 378 | " or \n", 379 | " $$\n", 380 | " \\begin{bmatrix}\n", 381 | " \\phi_m^n \\\\\n", 382 | " \\psi_m^n\n", 383 | " \\end{bmatrix}\n", 384 | " = \n", 385 | " \\begin{bmatrix}\n", 386 | " 1 & -h \\\\\n", 387 | " +\\frac{2hc^2}{a^2} & 1\n", 388 | " \\end{bmatrix}\n", 389 | " \\begin{bmatrix}\n", 390 | " \\phi_m^{n+1} \\\\\n", 391 | " \\psi_m^{n+1}\n", 392 | " \\end{bmatrix}\n", 393 | " -\n", 394 | " \\begin{bmatrix}\n", 395 | " 0 \\\\\n", 396 | " \\frac{c^2 h}{a^2}\\left(\\phi_{m+1}^{n+1} + \\phi_{m-1}^{n+1}\\right)\n", 397 | " \\end{bmatrix}\n", 398 | " $$" 399 | ] 400 | }, 401 | { 402 | "cell_type": "markdown", 403 | "metadata": { 404 | "slideshow": { 405 | "slide_type": "subslide" 406 | } 407 | }, 408 | "source": [ 409 | "\"Implicit\": we now have a set of simultaneous equations relating the values of $\\phi,~\\psi$ at $t$ to their values at $t+h$.\n", 410 | "\n", 411 | "Why bother solving these simultaneous equations, rather than using an \"explicit\" expression for the values of $\\phi,~\\psi$ at $t+h$ given their values at $t$ ?\n", 412 | "\n", 413 | "Because in certain cases, this is numerically stable while the explicit FTCS is not! (More about this later)\n", 414 | "\n", 415 | "Note, it does often suffer from accuracy issues, where solutions decay to 0 over time." 416 | ] 417 | }, 418 | { 419 | "cell_type": "markdown", 420 | "metadata": {}, 421 | "source": [ 422 | "## Crank-Nicolson" 423 | ] 424 | }, 425 | { 426 | "cell_type": "markdown", 427 | "metadata": { 428 | "slideshow": { 429 | "slide_type": "slide" 430 | } 431 | }, 432 | "source": [ 433 | "Average of explicit and implicit methods.\n", 434 | "\n", 435 | "Explicit ('forward'):\n", 436 | "$$ \n", 437 | "\\begin{align}\n", 438 | "\\phi_m^{n+1} &= \\phi_m^{n} + h\\psi_m^{n}, & \\psi_m^{n+1} = \\psi_m^{n} + h\\frac{c^2}{a^2}\\left(\\phi_{m-1}^{n} + \\phi_{m+1}^{n} - 2\\phi_m^{n}\\right).\n", 439 | "\\end{align}\n", 440 | "$$\n", 441 | "Implicit ('backward'):\n", 442 | "$$ \n", 443 | "\\begin{align}\n", 444 | "\\phi_m^{n+1} - h\\psi_m^{n+1} &= \\phi_m^n, &\\psi_m^{n} = \\psi_m^{n+1} - h\\frac{c^2}{a^2}\\left(\\phi_{m-1}^{n+1} + \\phi_{m+1}^{n+1} - 2\\phi_m^{n+1}\\right).\n", 445 | "\\end{align}\n", 446 | "$$\n", 447 | "Crank-Nicholson (C-N):\n", 448 | "$$ \n", 449 | "\\phi_m^{n+1} - \\frac{h}2\\psi_m^{n+1} = \\phi_m^{n} + \\frac{h}2\\psi_m^{n}\n", 450 | "$$\n", 451 | "$$ \\psi_m^{n+1} - \\frac{h}2\\frac{c^2}{a^2}\\left(\\phi_{m-1}^{n+1} + \\phi_{m+1}^{n+1} - 2\\phi_m^{n+1}\\right) = \\psi_m^{n} + \\frac{h}2\\frac{c^2}{a^2}\\left(\\phi_{m-1}^{n} + \\phi_{m+1}^{n} - 2\\phi_m^{n}\\right).\n", 452 | "$$" 453 | ] 454 | }, 455 | { 456 | "cell_type": "markdown", 457 | "metadata": {}, 458 | "source": [ 459 | "C-N is 2nd-order accurate in time, while both explicit and implicit methods are 1st-order accurate. So, C-N often solves the 'decaying to 0' issues encountered with the implicit method." 460 | ] 461 | }, 462 | { 463 | "cell_type": "markdown", 464 | "metadata": {}, 465 | "source": [ 466 | "# Hyperbolic PDEs" 467 | ] 468 | }, 469 | { 470 | "cell_type": "markdown", 471 | "metadata": {}, 472 | "source": [ 473 | "Explicit FTCS is always unstable. Use C-N, or spectral methods (next time)" 474 | ] 475 | } 476 | ], 477 | "metadata": { 478 | "celltoolbar": "Slideshow", 479 | "kernelspec": { 480 | "display_name": "Python 3 (ipykernel)", 481 | "language": "python", 482 | "name": "python3" 483 | }, 484 | "language_info": { 485 | "codemirror_mode": { 486 | "name": "ipython", 487 | "version": 3 488 | }, 489 | "file_extension": ".py", 490 | "mimetype": "text/x-python", 491 | "name": "python", 492 | "nbconvert_exporter": "python", 493 | "pygments_lexer": "ipython3", 494 | "version": "3.11.9" 495 | } 496 | }, 497 | "nbformat": 4, 498 | "nbformat_minor": 4 499 | } 500 | -------------------------------------------------------------------------------- /Week6/Simple-ODEs.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": { 6 | "slideshow": { 7 | "slide_type": "slide" 8 | } 9 | }, 10 | "source": [ 11 | "*Supporting textbook chapters for week 6: Chapters 8.1, 8.2, 8.5.1 to 8.5.3*\n", 12 | "\n", 13 | "* Euler method\n", 14 | "* Runge-Kutta methods\n", 15 | "* Leapfrog Methods\n", 16 | " * Bulirsch-Stoer" 17 | ] 18 | }, 19 | { 20 | "cell_type": "markdown", 21 | "metadata": { 22 | "slideshow": { 23 | "slide_type": "subslide" 24 | } 25 | }, 26 | "source": [ 27 | "Consider a first-order ODE that is a function of only one variable, with an initial condition(s).\n", 28 | " $\\frac{\\text d x}{\\text d t} = f(x, t) \\quad \\text{with} \\quad x(t=0)=x_0.$\n", 29 | "These equations can be impossible to solve analytically, but easy to solve on a computer.\n", 30 | "\n", 31 | "Later in the course (not this week), we'll do:\n", 32 | " \n", 33 | "* $n$D: $\\quad\\displaystyle \\frac{\\text d x_i}{\\text d t} = f_i(x_1,\\dots{},x_n, t)\\quad \\text{with}\\quad x_i(t=0)=x_{i0}.$\n", 34 | "* higher order, e.g.: \n", 35 | "$$\\frac{\\text d^3 x}{\\text d t^3} =f(x, t)\\quad \\Leftrightarrow \\quad \\frac{\\text d x}{\\text d t} = v,\\ \\frac{\\text d v}{\\text d t} = a,\\ \\frac{\\text d a}{\\text d t}=f.$$\n", 36 | "* coupled sets of DEs" 37 | ] 38 | }, 39 | { 40 | "cell_type": "markdown", 41 | "metadata": { 42 | "tags": [] 43 | }, 44 | "source": [ 45 | "# SciPy Built-in Solvers" 46 | ] 47 | }, 48 | { 49 | "cell_type": "markdown", 50 | "metadata": { 51 | "slideshow": { 52 | "slide_type": "subslide" 53 | } 54 | }, 55 | "source": [ 56 | "* SciPy has built-in ODE solvers called `odeint` (older) and `solve_ivp` (preferred) located in the `scipy.integrate` module.\n", 57 | "https://docs.scipy.org/doc/scipy/tutorial/integrate.html\n", 58 | "\n", 59 | "* `odeint` is very powerful, but lots of hidden automatic black-box workings make it difficult to track what is actually going on (e.g. to get an error estimate).\n", 60 | " \n", 61 | "* `solve_ivp` allows more user control and tunable knobs.\n", 62 | "\n", 63 | "* If you don't need an error estimate for your specific application, then just use `odeint` or `solve_ivp` and hope the computation time is acceptable.\n", 64 | " However, if it does matter, then you can write your own ODE solver" 65 | ] 66 | }, 67 | { 68 | "cell_type": "markdown", 69 | "metadata": { 70 | "tags": [] 71 | }, 72 | "source": [ 73 | "# Euler Method" 74 | ] 75 | }, 76 | { 77 | "cell_type": "markdown", 78 | "metadata": {}, 79 | "source": [ 80 | "* Start at $t = t_0,~ x = x_0$\n", 81 | "* Discretize into time-steps $t_i$ with constant spacing $h$\n", 82 | "* At each time-step, find $x$, using $x$ at the previus time-step and $f$:\n", 83 | "$\\quad x_{i} = x_{i-1} + hf(x_i) $" 84 | ] 85 | }, 86 | { 87 | "cell_type": "markdown", 88 | "metadata": { 89 | "slideshow": { 90 | "slide_type": "subslide" 91 | } 92 | }, 93 | "source": [ 94 | "* The Euler method has error $O(h^2)$ at each step\n", 95 | "* integrating across the whole interval: global error is $O(h)$ (see eqn 8.8, p. 331 in textbook):\n", 96 | " $$\\text{Taylor expansion} \\Rightarrow x(t+h) = x(t) + h\\frac{dx}{dt} + \\overbrace{ \\frac{h^2}{2} \\frac{d^2x}{dt^2} } ^{\\epsilon} + O(h^3)$$\n", 97 | " $$\\sum\\epsilon = \\sum_{k=0}^{N-1}\\frac{h^2}{2}\\left. \\frac{d^2x}{dt^2} \\right|_{x_k, t_k} = \\frac{h}{2}\\sum_{k=0}^{N-1}h\\left.\\frac{df}{dt}\\right|_{x_k, t_k}\\\\\n", 98 | " \\approx \\frac{h}2\\int_a^b\\frac{df}{dt}d t = \\frac{h}{2}\\left[f_b - f_a\\right]$$\n", 99 | "* For some applications, this is good enough. But for others, we need to do better!" 100 | ] 101 | }, 102 | { 103 | "cell_type": "markdown", 104 | "metadata": {}, 105 | "source": [ 106 | "# Runge-Kutta (RK) Methods" 107 | ] 108 | }, 109 | { 110 | "cell_type": "markdown", 111 | "metadata": {}, 112 | "source": [ 113 | "## 2nd-order (RK2)" 114 | ] 115 | }, 116 | { 117 | "cell_type": "markdown", 118 | "metadata": { 119 | "slideshow": { 120 | "slide_type": "slide" 121 | } 122 | }, 123 | "source": [ 124 | "* Use the middle point $t+h/2$ and evaluate with Euler's method, \n", 125 | "\n", 126 | "$\\quad\\displaystyle x\\left(t + \\frac{h}2\\right) \\approx x(t) + \\frac{h}2 f[x(t), t]$\n", 127 | "\n", 128 | "* Slope at $t + \\frac{h}2 \\approx f \\! \\left[ x(t) + \\frac{h}2 f\\left[x(t), t\\right], t + \\frac{h}2\\right]$\n", 129 | "\n", 130 | "$\\displaystyle \\Rightarrow \\boxed{x(t+h) = x(t) + h f\\! \\left[x(t) + \\frac{h}2 f[x(t), t], t+\\frac{h}2\\right]}$" 131 | ] 132 | }, 133 | { 134 | "cell_type": "markdown", 135 | "metadata": { 136 | "cell_style": "split", 137 | "slideshow": { 138 | "slide_type": "subslide" 139 | } 140 | }, 141 | "source": [ 142 | "![Newman Fig. 8.2](fig8-2.png)" 143 | ] 144 | }, 145 | { 146 | "cell_type": "markdown", 147 | "metadata": { 148 | "slideshow": { 149 | "slide_type": "subslide" 150 | } 151 | }, 152 | "source": [ 153 | "RK2 usually coded by defining intermediate quantities:\n", 154 | "\n", 155 | "* $k_1 = hf(x, t)$ as preliminary step before $x(t+h/2)$,\n", 156 | "* $\\displaystyle k_2 = hf\\left(x + \\frac{k_1}{2}, t+\\frac{h}2\\right)$,\n", 157 | "* $x(t+h) = x(t) + k_2$.\n", 158 | "\n", 159 | "**Note: $O(h^3)$ error at each step, usually $O(h^2)$ global error.**" 160 | ] 161 | }, 162 | { 163 | "cell_type": "markdown", 164 | "metadata": { 165 | "slideshow": { 166 | "slide_type": "subslide" 167 | } 168 | }, 169 | "source": [ 170 | "Coding Euler:" 171 | ] 172 | }, 173 | { 174 | "cell_type": "code", 175 | "execution_count": null, 176 | "metadata": { 177 | "slideshow": { 178 | "slide_type": "fragment" 179 | } 180 | }, 181 | "outputs": [], 182 | "source": [ 183 | "for t in tpoints:\n", 184 | " x += h*f(x, t)" 185 | ] 186 | }, 187 | { 188 | "cell_type": "markdown", 189 | "metadata": { 190 | "slideshow": { 191 | "slide_type": "fragment" 192 | } 193 | }, 194 | "source": [ 195 | "Coding RK2:" 196 | ] 197 | }, 198 | { 199 | "cell_type": "code", 200 | "execution_count": null, 201 | "metadata": { 202 | "slideshow": { 203 | "slide_type": "fragment" 204 | } 205 | }, 206 | "outputs": [], 207 | "source": [ 208 | "for t in tpoints:\n", 209 | " k1 = h*f(x, t)\n", 210 | " k2 = h*f(x + 0.5*k1, t+0.5*h)\n", 211 | " x += k2" 212 | ] 213 | }, 214 | { 215 | "cell_type": "markdown", 216 | "metadata": {}, 217 | "source": [ 218 | "## 4th-order (RK4)" 219 | ] 220 | }, 221 | { 222 | "cell_type": "markdown", 223 | "metadata": { 224 | "slideshow": { 225 | "slide_type": "subslide" 226 | } 227 | }, 228 | "source": [ 229 | "* Various Taylor expansions at various points in the interval $\\Rightarrow$ higher-order RK's.\n", 230 | "* RK4 is reasonable to code yourself. Higher-order methods $\\Rightarrow$ use canned routines.\n", 231 | "* End result, after tedious algebra:\n", 232 | " 1. $k_1 = hf(x, t)$,\n", 233 | " 2. $k_2 = hf\\left(x + \\frac{k_1}{2}, t+\\frac{h}2\\right)$,\n", 234 | " 3. $k_3 = hf\\left(x + \\frac{k_2}{2}, t+\\frac{h}2\\right)$,\n", 235 | " 4. $k_4 = hf\\left(x + k_3, t + h \\right)$,\n", 236 | " 5. $x(t+h) = x(t) + \\frac{1}{6}(k_1 + 2 k_2 + 2k_3 + k_4)$." 237 | ] 238 | }, 239 | { 240 | "cell_type": "markdown", 241 | "metadata": { 242 | "cell_style": "center", 243 | "slideshow": { 244 | "slide_type": "subslide" 245 | } 246 | }, 247 | "source": [ 248 | "Coding RK4:" 249 | ] 250 | }, 251 | { 252 | "cell_type": "code", 253 | "execution_count": null, 254 | "metadata": { 255 | "cell_style": "center", 256 | "slideshow": { 257 | "slide_type": "fragment" 258 | } 259 | }, 260 | "outputs": [], 261 | "source": [ 262 | "for t in tpoints:\n", 263 | " k1 = h*f(x, t)\n", 264 | " k2 = h*f(x+0.5*k1, t+0.5*h)\n", 265 | " k3 = h*f(x+0.5*k2, t+0.5*h)\n", 266 | " k4 = h*f(x+k3, t+h)\n", 267 | " x += (k1 + 2*k2 + 2*k3 + k4)/6" 268 | ] 269 | }, 270 | { 271 | "cell_type": "markdown", 272 | "metadata": {}, 273 | "source": [ 274 | "### Error Estimation" 275 | ] 276 | }, 277 | { 278 | "cell_type": "markdown", 279 | "metadata": { 280 | "tags": [] 281 | }, 282 | "source": [ 283 | "* Very accurate method: error is $\\epsilon = ch^5$ at each time step $h$, $c$ constant (order $h^4$ globally).\n", 284 | "* Error after 2 time steps: $\\approx 2ch^5$.\n", 285 | "* Error after 1 time step of $2h$:$~ \\approx c(2h)^5 = 32 ch^5 \\gg 2 ch^5$\n", 286 | "* The difference is $(32-2)ch^5=30 ch^5 = 30\\epsilon$.\n", 287 | "* To estimate error: run ODE solver twice with $h$ (to get $x_1$), once with $2h$ (to get $x_2$), divide difference by 30.\n", 288 | "$$\\epsilon = ch^5 = \\frac{1}{30}(x_1 - x_2).$$" 289 | ] 290 | }, 291 | { 292 | "cell_type": "markdown", 293 | "metadata": {}, 294 | "source": [ 295 | "### Adaptive time stepping" 296 | ] 297 | }, 298 | { 299 | "cell_type": "markdown", 300 | "metadata": { 301 | "slideshow": { 302 | "slide_type": "subslide" 303 | }, 304 | "tags": [] 305 | }, 306 | "source": [ 307 | "* Suppose target error is $\\delta$ *per unit time* (physical time $t$, not computational step). \n", 308 | "* If\n", 309 | "$$\\rho = \\frac{h\\delta}{\\epsilon} = \\frac{30h\\delta}{|x_1 - x_2|} = \\frac{30 \\delta}{ch^4} > 1,$$\n", 310 | "then $h$ is too small (as in, could be bigger, saving computational resources while still reaching target accuracy) and can be adjusted to $h' = h\\rho^{1/4}$ to get $\\rho'=1$.\n", 311 | " * Still achieves target error, which is $h' \\delta$ for step of size $h'$.\n", 312 | " * Saves calculation time." 313 | ] 314 | }, 315 | { 316 | "cell_type": "markdown", 317 | "metadata": { 318 | "slideshow": { 319 | "slide_type": "subslide" 320 | } 321 | }, 322 | "source": [ 323 | "* If $\\rho < 1$, the time step is too large and needs to be adjusted down by the same factor.\n", 324 | " * We also need to repeat our calculation to get the desired accuracy.\n", 325 | " * This will guarantee meeting error target.\n", 326 | "* We test if we need to adjust by performing the calculation twice (we retrieve $x_1$ and $x_2$), testing if we met the target, and adjusting $h$.\n", 327 | "* Overall, despite extra work (up to 3 RK4 steps per time step), program often faster because resolution focused where it's needed." 328 | ] 329 | }, 330 | { 331 | "cell_type": "markdown", 332 | "metadata": { 333 | "cell_style": "split", 334 | "slideshow": { 335 | "slide_type": "slide" 336 | } 337 | }, 338 | "source": [ 339 | "# Leapfrog methods\n", 340 | "* RK2: Use mid-point location to jump to $t+h$\n", 341 | "$$ x(t+h) = x(t) + h f \\left[ x + \\frac{h}2 f(x, t), t+ \\frac{h}2 \\right] $$\n", 342 | "* Leapfrog: use each point as a mid-point.\n", 343 | "$$ x(t+h) = x(t) + h f \\! \\left[ x + \\frac{h}2 f(x, t), t + \\frac{h}2 \\right] ,$$\n", 344 | "$$ x\\left(t+\\frac{3}{2}h\\right) = x\\left(t + \\frac{h}2\\right) + hf[x(t+h), t+h].$$\n", 345 | " * Requires Euler for the first half-step, from $t$ to $t + h/2$" 346 | ] 347 | }, 348 | { 349 | "cell_type": "markdown", 350 | "metadata": { 351 | "cell_style": "split", 352 | "slideshow": { 353 | "slide_type": "subslide" 354 | } 355 | }, 356 | "source": [ 357 | "![Newman fig. 8.9](fig8-9.png)" 358 | ] 359 | }, 360 | { 361 | "cell_type": "markdown", 362 | "metadata": {}, 363 | "source": [ 364 | "* It is **time-reversible**! Just take the 'reverse path' to 'retrace your steps' backwards\n", 365 | " * But the time-step has to be constant" 366 | ] 367 | }, 368 | { 369 | "cell_type": "markdown", 370 | "metadata": {}, 371 | "source": [ 372 | "## Error Estimation" 373 | ] 374 | }, 375 | { 376 | "cell_type": "markdown", 377 | "metadata": {}, 378 | "source": [ 379 | "* Time-reversible $\\Rightarrow$ error $\\epsilon$ is an **odd** function of $h$:\n", 380 | "$$\\epsilon(-h) = -\\epsilon(h)$$\n", 381 | "$\\Rightarrow$ Taylor expansion is made of **odd** powers of $h$,\n", 382 | " $$\\epsilon(h) = c_3 h^3 + c_5 h^5 + \\dots$$\n", 383 | "$\\Rightarrow$ cumulative error is **even** in $h$.\n", 384 | "* Simplest implementation, based on RK2, has $O(h^2)$ global error\n", 385 | "* Each improvement we apply would gain two orders of accuracy, if we could first eliminate all even powers in $\\epsilon$ due to first 1/2 step." 386 | ] 387 | }, 388 | { 389 | "cell_type": "markdown", 390 | "metadata": {}, 391 | "source": [ 392 | "## Modified Midpoint Method" 393 | ] 394 | }, 395 | { 396 | "cell_type": "markdown", 397 | "metadata": { 398 | "slideshow": { 399 | "slide_type": "subslide" 400 | } 401 | }, 402 | "source": [ 403 | "Do the Leapfrog method, then do **both** the whole integer **and** the forward Euler 1/2-step.\n", 404 | " \\begin{align*}\n", 405 | " x_{n-1/2} & = x_{n-3/2} + hf(x_{n-1}, t + H - h),\\\\\n", 406 | " x_{n} & = x_{n-1} + hf(x_{n-1/2}, t+H-h/2)\\approx x(t+H) \\\\\n", 407 | " x_n ' & = x_{n-1/2} + hf(x_n, t+H)\\approx x(t+H)\n", 408 | " \\end{align*}\n", 409 | " \n", 410 | "Then do the following adjustment:\n", 411 | " $$x(t+H)_{final} = \\frac{x_n + x_n'}2$$ \n", 412 | " ... and you have canceled the even powers (MMP method).\n", 413 | "\n", 414 | "This is not a trivial result (cf. Gragg 1965 for proof; https://doi.org/10.1137/0702030 PDF posted on Quercus if you're curious)." 415 | ] 416 | }, 417 | { 418 | "cell_type": "markdown", 419 | "metadata": {}, 420 | "source": [ 421 | "## Bulirsch-Stoer method" 422 | ] 423 | }, 424 | { 425 | "cell_type": "markdown", 426 | "metadata": { 427 | "slideshow": { 428 | "slide_type": "subslide" 429 | } 430 | }, 431 | "source": [ 432 | "MMP method rarely used by itself, but is the basis for the powerful Bulirsch-Stoer (don't abbreviate this...) method:\n", 433 | " * Take 1 single MMP step of size $h_1 = H$ to get estimate $$x(t+H) = R_{1, 1}.$$ \n", 434 | " * $R$ stands for \"Richardson extrapolation\", remember this from Romberg integration?\n", 435 | " * Now take 2 MMP steps of size $h_2 = H/2$ to get second estimate of $$x(t+H) = R_{2,1}.$$" 436 | ] 437 | }, 438 | { 439 | "cell_type": "markdown", 440 | "metadata": { 441 | "slideshow": { 442 | "slide_type": "subslide" 443 | } 444 | }, 445 | "source": [ 446 | "* Since we know the MMP has 2nd order and even total error, we can write both of these estimates as\n", 447 | "\\begin{align*}\n", 448 | "x(t+H) & = R_{1,1} + c_1 h_1^2 + O(h_1^4)\\quad \\text{and}\\\\\n", 449 | "x(t+H) & = R_{2,1} + c_1 h_2^2 + O(h_2^4).\n", 450 | "\\end{align*}\n", 451 | "\n", 452 | "* Using the relationship between the step sizes: $h_1 = 2 h_2$, we can equate these expressions to get\n", 453 | "$$R_{1,1} + 4 c_1 h_2^2 + O(h_2^4) = R_{2,1} + c_1 h_2^2 + O(h_2^4)$$\n", 454 | "$$\\Rightarrow\\ c_1 h_2^2 = \\frac{1}{3}(R_{2,1} - R_{1,1}) + O(h_2^4).$$\n", 455 | "* If we plug this back in to the expression for $x(t+H)$ above we get a new estimate called $R_{2,2}$:\n", 456 | " $$x(t+H) \\approx R_{2,2} + \\boxed{O(h^4_2)}$$" 457 | ] 458 | }, 459 | { 460 | "cell_type": "markdown", 461 | "metadata": { 462 | "slideshow": { 463 | "slide_type": "subslide" 464 | }, 465 | "tags": [] 466 | }, 467 | "source": [ 468 | "$$x(t+H) = \\underbrace{R_{2,1} + \\frac{1}{3}(R_{2,1} - R_{1,1})}_{R_{2,2}} + \\boxed{O(h^4_2)}.$$\n", 469 | "* 2 different grid spacings ($H$ and $H/2$) $\\to$ expression for the leading error term $\\to$ replace it with our estimates for these grid spacings, i.e., $R_{1,1}$ and $R_{2,1}$.\n", 470 | "* We have reduced the error in our estimate by 2 orders! *(which was possible because the errors were even)*" 471 | ] 472 | }, 473 | { 474 | "cell_type": "markdown", 475 | "metadata": { 476 | "slideshow": { 477 | "slide_type": "subslide" 478 | } 479 | }, 480 | "source": [ 481 | "You can keep going! The power in this method is that you keep cancelling 2 powers in the error for every new grid spacing you consider.\n", 482 | "* Continue to refine grid to find new estimates and error estimates, and stop when error is acceptable\n", 483 | "\n", 484 | "* Error:\n", 485 | "$$ x(t+H) = R_{n, m+1} + O(h_n^{2m+2})$$\n", 486 | "* Recursion relation:\n", 487 | "$$ R_{n, m+1} = R_{n,m} + \\frac{R_{n,m} - R_{n-1, m}}{[n/(n-1)]^{2m} - 1}\\quad \\text{and}\\quad h_n = \\left(\\frac{n-1}{n}\\right)h_{n-1}. $$" 488 | ] 489 | }, 490 | { 491 | "cell_type": "markdown", 492 | "metadata": { 493 | "slideshow": { 494 | "slide_type": "subslide" 495 | } 496 | }, 497 | "source": [ 498 | "Can make a similar extrapolation table as for Romberg Integration, so you only need to use MMP (for calculating $R_{m,1}$) and the recursion relation:\n", 499 | "\\begin{align*}\n", 500 | "n = 1:\\ & R_{1,1}\\hspace{-20pt} & & & & & & \\\\\n", 501 | " & & \\searrow & & & & & \\\\\n", 502 | "n = 2:\\ & R_{2,1}\\hspace{-20pt} & \\to & R_{2,2}\\hspace{-20pt} & & & & \\\\\n", 503 | " & & \\searrow & & \\searrow & & & \\\\\n", 504 | "n = 3:\\ & R_{3,1}\\hspace{-20pt} & \\to & R_{3,2}\\hspace{-20pt} & \\to & R_{3,3}\\hspace{-20pt} & & \\\\\n", 505 | " & & \\searrow & & \\searrow & & \\searrow & \\\\\n", 506 | "n = 4:\\ & \\underbrace{R_{4,1}}_{MMP}\\hspace{-20pt} & \\to & R_{4,2}\\hspace{-20pt} & \\to & R_{4,3}\\hspace{-20pt} & \\to & R_{4,4}\\hspace{-20pt}\n", 507 | "\\end{align*}" 508 | ] 509 | }, 510 | { 511 | "cell_type": "markdown", 512 | "metadata": {}, 513 | "source": [ 514 | "## More on time-reversal" 515 | ] 516 | }, 517 | { 518 | "cell_type": "markdown", 519 | "metadata": { 520 | "slideshow": { 521 | "slide_type": "subslide" 522 | } 523 | }, 524 | "source": [ 525 | "Many physics problems involve systems that are symmetric or invariant in time (related to conservation of energy). So when modeling these systems, often want a solution that can be run 'backwards in time'" 526 | ] 527 | }, 528 | { 529 | "cell_type": "markdown", 530 | "metadata": { 531 | "cell_style": "split", 532 | "slideshow": { 533 | "slide_type": "subslide" 534 | } 535 | }, 536 | "source": [ 537 | "**Time shift/reversal doesn't work with RK**\n", 538 | "\n", 539 | "* e.g. for RK2, everything \"resets\" at $t+h$, so the info at the mid-point is lost and the reverse path is not a \"retracing of the steps\".\n", 540 | "* Graphically, reverse is not just taking the same points and drawing the arrows backwards. The positions of the points change, depending if you start at the beginning and work forwards or start at the end and work backwards!\n", 541 | "* A manifestation of this: solution 'drifts' with time, see example below (from model of a non-linear pendulum system)" 542 | ] 543 | }, 544 | { 545 | "cell_type": "markdown", 546 | "metadata": { 547 | "cell_style": "split", 548 | "slideshow": { 549 | "slide_type": "subslide" 550 | } 551 | }, 552 | "source": [ 553 | "\n", 554 | "\n", 555 | "![Newman Fig. 8.10](fig8-10.png)" 556 | ] 557 | }, 558 | { 559 | "cell_type": "markdown", 560 | "metadata": {}, 561 | "source": [ 562 | "# Pros and Cons of each method" 563 | ] 564 | }, 565 | { 566 | "cell_type": "markdown", 567 | "metadata": { 568 | "slideshow": { 569 | "slide_type": "slide" 570 | } 571 | }, 572 | "source": [ 573 | "RK2:\n", 574 | "* $\\oplus$ Easily extended to RK4\n", 575 | "* $\\oplus$ Possible to use adaptive time step\n", 576 | "* $\\ominus$ not time-reversible\n", 577 | "* $\\ominus$ not great accuracy" 578 | ] 579 | }, 580 | { 581 | "cell_type": "markdown", 582 | "metadata": { 583 | "cell_style": "split", 584 | "slideshow": { 585 | "slide_type": "subslide" 586 | } 587 | }, 588 | "source": [ 589 | "RK4:\n", 590 | "* $\\oplus$ good accuracy\n", 591 | "* $\\oplus$ Possible to use adaptive time step\n", 592 | "* $\\ominus$ not time-reversible" 593 | ] 594 | }, 595 | { 596 | "cell_type": "markdown", 597 | "metadata": { 598 | "cell_style": "split", 599 | "slideshow": { 600 | "slide_type": "subslide" 601 | } 602 | }, 603 | "source": [ 604 | "Leapfrog:\n", 605 | "\n", 606 | "* $\\oplus$ time-reversible\n", 607 | "* $\\oplus$ can be extended to higher-order methods\n", 608 | "* $\\ominus$ not great accuracy\n", 609 | "* $\\ominus$ time step has to be constant (not adaptive)\n", 610 | "\n", 611 | "Bulirsch-Stoer:\n", 612 | "\n", 613 | "* $\\oplus$ time-reversible\n", 614 | "* $\\oplus$ accuracy can be improved until desired level if reached\n", 615 | "* $\\ominus$ time step has to be constant (not adaptive)" 616 | ] 617 | }, 618 | { 619 | "cell_type": "code", 620 | "execution_count": null, 621 | "metadata": {}, 622 | "outputs": [], 623 | "source": [] 624 | } 625 | ], 626 | "metadata": { 627 | "celltoolbar": "Slideshow", 628 | "kernelspec": { 629 | "display_name": "Python 3 (ipykernel)", 630 | "language": "python", 631 | "name": "python3" 632 | }, 633 | "language_info": { 634 | "codemirror_mode": { 635 | "name": "ipython", 636 | "version": 3 637 | }, 638 | "file_extension": ".py", 639 | "mimetype": "text/x-python", 640 | "name": "python", 641 | "nbconvert_exporter": "python", 642 | "pygments_lexer": "ipython3", 643 | "version": "3.11.9" 644 | } 645 | }, 646 | "nbformat": 4, 647 | "nbformat_minor": 4 648 | } 649 | -------------------------------------------------------------------------------- /Week5/L05-FourierTransforms.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": { 6 | "slideshow": { 7 | "slide_type": "slide" 8 | } 9 | }, 10 | "source": [ 11 | "Supporting textbook chapters for week 5: Chapter 7" 12 | ] 13 | }, 14 | { 15 | "cell_type": "markdown", 16 | "metadata": {}, 17 | "source": [ 18 | "## Brief Review (hopefully)" 19 | ] 20 | }, 21 | { 22 | "cell_type": "markdown", 23 | "metadata": { 24 | "tags": [] 25 | }, 26 | "source": [ 27 | "If you need a more detailed refresher on what Fourier series are, the YouTube Channel 3Blue1Brown by Grant Sanderson has this video: https://youtu.be/r6sGWTCMz2k \n", 28 | "\n", 29 | "In computational physics, we often compute Fourier series as a way to compute Fourier transforms.\n", 30 | "For a refresher or introduction about Fourier transforms, the same channel also has a video about it: https://youtu.be/spUNpyF58BY" 31 | ] 32 | }, 33 | { 34 | "cell_type": "markdown", 35 | "metadata": {}, 36 | "source": [ 37 | "### Fourier Series" 38 | ] 39 | }, 40 | { 41 | "cell_type": "markdown", 42 | "metadata": { 43 | "slideshow": { 44 | "slide_type": "slide" 45 | } 46 | }, 47 | "source": [ 48 | "In 1822, Joseph Fourier wanted to compute temperature distributions in objects, based on the heat flux equation $\\vec \\phi = \\kappa \\vec\\nabla T$, where $\\vec \\phi$ is the flux vector, $\\kappa$ the heat diffusivity and $T$ is the temperature.\n", 49 | "He did so by finding a way to express periodic functions as linear combinations of sines and cosines.\n", 50 | "\n", 51 | "We can write any periodic function $f$ with period $L$ on the interval $[0, L]$ as a \"Fourier series\"." 52 | ] 53 | }, 54 | { 55 | "cell_type": "markdown", 56 | "metadata": { 57 | "slideshow": { 58 | "slide_type": "subslide" 59 | } 60 | }, 61 | "source": [ 62 | "$$f(x) = \\sum_{k=0}^{\\infty}\\left[\\alpha_k \\cos\\left(\\frac{2\\pi k x}{L}\\right) + \\beta_k\\sin\\left(\\frac{2\\pi k x}{L}\\right)\\right]$$\n", 63 | "$$= \\sum_{k=-\\infty}^{\\infty}\\gamma_k \\exp\\left(i\\frac{2\\pi k x}{L}\\right),\\qquad\\qquad\\quad $$\n", 64 | "with \n", 65 | "\\begin{align*}\n", 66 | "\\gamma_k & = \\frac{\\alpha_{-k} + i\\beta_{-k}}2 & \\text{if}\\quad k<0,\\\\\n", 67 | "\\gamma_k & = \\alpha_0 & \\text{if}\\quad k=0,\\\\\n", 68 | "\\gamma_k & = \\frac{\\alpha_{k} - i\\beta_{k}}2 & \\text{if}\\quad k>0,\n", 69 | "\\end{align*}\n", 70 | "and\n", 71 | "$$\\forall k, \\quad \\gamma_k = \\frac1L\\int_0^L f(x)\\exp\\left(-i\\frac{2\\pi k x}{L}\\right)dx\\quad \\text{from orthogonality of sin/cos functions}.$$" 72 | ] 73 | }, 74 | { 75 | "cell_type": "markdown", 76 | "metadata": { 77 | "slideshow": { 78 | "slide_type": "subslide" 79 | } 80 | }, 81 | "source": [ 82 | "Orthogonality of the sine functions:\n", 83 | "\\begin{align*}\n", 84 | "& \\int_0^L\\sin\\left(\\frac{\\pi n x}{L}\\right)\\sin\\left(\\frac{\\pi m x}{L}\\right)dx = \\frac{L}2 \\delta_{nm}, \\\\\n", 85 | "& \\int_0^L\\cos\\left(\\frac{\\pi n x}{L}\\right)\\cos\\left(\\frac{\\pi m x}{L}\\right)dx = \\frac{L}2 \\delta_{nm},\\\\\n", 86 | "& \\int_0^L\\sin\\left(\\frac{\\pi n x}{L}\\right)\\cos\\left(\\frac{\\pi m x}{L}\\right)dx = 0\n", 87 | "\\end{align*}" 88 | ] 89 | }, 90 | { 91 | "cell_type": "markdown", 92 | "metadata": { 93 | "slideshow": { 94 | "slide_type": "subslide" 95 | } 96 | }, 97 | "source": [ 98 | "For non-periodic functions, we can repeat the function over the portion of interest and discard the rest\n", 99 | "![Newman's fig. 7.1](fig7-1.png)\n", 100 | "Solid grey line = actual function, Solid black line = function bracketed to the interval $[0, L]$\n", 101 | "\n", 102 | "Dashed lines = bracketed function replicated over other intervals to make it periodic." 103 | ] 104 | }, 105 | { 106 | "cell_type": "markdown", 107 | "metadata": {}, 108 | "source": [ 109 | "### Fourier Transforms" 110 | ] 111 | }, 112 | { 113 | "cell_type": "markdown", 114 | "metadata": { 115 | "slideshow": { 116 | "slide_type": "slide" 117 | } 118 | }, 119 | "source": [ 120 | "What if $L \\to \\infty$? Then, separation between wave numbers or frequencies tend to zero:\n", 121 | " $$\\frac{2\\pi (k+1) x}{L} - \\frac{2\\pi k x}{L} = \\frac{2\\pi x}{L} \\to 0.$$\n", 122 | "And the discrete sums turn into integrals:\n", 123 | " $$f(x) = \\sum_{k=-\\infty}^\\infty \\gamma_k\\text e^{2\\pi i \\nu_k x} \\to \\int_{-\\infty}^\\infty \\hat f(\\nu)\\text e^{-2\\pi i \\nu x} \\text d \\nu,$$\n", 124 | " with $\\nu_k = k/L$ (discrete) or $\\nu$ (continuous) the frequency of each Fourier component, $\\hat f$ the **Fourier transform** of $f$.\n", 125 | " \n", 126 | "Just like we could retrieve the $\\gamma_k$'s from $f$ with the integral formulas above, we can invert the Fourier transform:\n", 127 | " $$\\hat f(\\nu) = \\int_{-\\infty}^\\infty f(x)\\text e^{2\\pi i \\nu x} \\text d x.$$" 128 | ] 129 | }, 130 | { 131 | "cell_type": "markdown", 132 | "metadata": {}, 133 | "source": [ 134 | "## Discrete Fourier Transform" 135 | ] 136 | }, 137 | { 138 | "cell_type": "markdown", 139 | "metadata": { 140 | "slideshow": { 141 | "slide_type": "slide" 142 | } 143 | }, 144 | "source": [ 145 | "\n", 146 | "Discretizing the Fourier transform operation: $\\text d x \\to \\Delta_x$ finite, $x \\to x_k = x_0 + k\\Delta_x$ with $0\\leq k < N$\n", 147 | "$$\\hat f(\\nu) = \\int_{-\\infty}^\\infty f(x)\\text e^{2\\pi i \\nu x} \\text d x \\approx \\sum_{k = 0}^{N-1}f(x_k)\\text e^{2 i\\pi\\nu x_k} \\Delta_x.$$\n", 148 | "\n", 149 | "* Note how discretizing also requires bounding the interval.\n", 150 | "* Only a Riemann sum but it illustrates the properties well enough. And it is actually how we compute FTs numerically.\n", 151 | "* Also, the frequency is discretized: we usually use $\\nu_n = n/N$.\n", 152 | "\n", 153 | "The expression above then becomes a Fourier series: **When we compute Fourier transforms, we actually compute Fourier series**!\n", 154 | "It is the user's job to know how to interpret a Fourier series as a Fourier transform.\n", 155 | "\n", 156 | "Careful however: the formula above is not the exact expression for how to compute Fourier transforms numerically (patience). " 157 | ] 158 | }, 159 | { 160 | "cell_type": "markdown", 161 | "metadata": {}, 162 | "source": [ 163 | "### Choice of interval" 164 | ] 165 | }, 166 | { 167 | "cell_type": "markdown", 168 | "metadata": { 169 | "slideshow": { 170 | "slide_type": "subslide" 171 | }, 172 | "tags": [] 173 | }, 174 | "source": [ 175 | "Mechanically, we turn any continuous function defined on the real axis as a periodic function on the interval $0\\leq x < N\\Delta_x$.\n", 176 | "Choose your interval wisely if you really need the whole thing!\n", 177 | " * Periodic function: take integer number of periods. One is enough in theory but you never know (slight aperiodicity, noise...)\n", 178 | " * Function that decays to infinity: interval wide enough that the function has almost completely decayed at edges.\n", 179 | " * Function that keeps doing interesting stuff for ever (e.g.\\ stochastic series): choose it wide enough that you encapsulate enough statistics, and know what you're not capturing.\n", 180 | "\n", 181 | "Want to capture high frequencies? Make $\\Delta_x$ smaller.\n", 182 | "\n", 183 | "Want to capture low frequencies? Make the interval ($N\\Delta_x$) longer." 184 | ] 185 | }, 186 | { 187 | "cell_type": "markdown", 188 | "metadata": {}, 189 | "source": [ 190 | "### DFT Implementation" 191 | ] 192 | }, 193 | { 194 | "cell_type": "markdown", 195 | "metadata": { 196 | "slideshow": { 197 | "slide_type": "subslide" 198 | } 199 | }, 200 | "source": [ 201 | "Now let's think about the integrals used for obtaining the Fourier coefficients $\\gamma_k$'s.\n", 202 | "\n", 203 | "* We divide $[0, L]$ up into $N$ segments and use the trapezoidal rule and periodicity of the function:\n", 204 | "\n", 205 | "\\begin{align*}\n", 206 | "\\gamma_k & = \\frac1L \\int_0^L f(x)\\exp\\left(-i\\frac{2\\pi k x}{L}\\right) dx \\\\\n", 207 | " & \\approx \\frac1L \\frac{L}N\\left[\\frac12 f(0) + \\frac12 f(L) + \\sum_{n=1}^{N-1} f(x_n) \\exp\\left(-i\\frac{2\\pi k x_n}{L}\\right) \\right] \\\\\n", 208 | " & = \\frac1N \\left[\\sum_{n=0}^{N-1} f(x_n) \\exp\\left(-i\\frac{2\\pi k n}{N}\\right) \\right] \\quad \\text{because }f(0) = f(L)\\text{ and }\\frac{x_n}L = \\frac{n}N.\n", 209 | "\\end{align*}" 210 | ] 211 | }, 212 | { 213 | "cell_type": "markdown", 214 | "metadata": { 215 | "slideshow": { 216 | "slide_type": "subslide" 217 | } 218 | }, 219 | "source": [ 220 | "* Now define the Discrete Fourier Transform (DFT) as follows:\n", 221 | " $$y_k = f(x_k); \\qquad c_k = N\\gamma_k;$$\n", 222 | "\n", 223 | " DFT: $\\boxed{\\displaystyle c_k = \\sum_{n=0}^{N-1}y_n \\exp\\left(-i\\frac{2\\pi kn}{N}\\right)}$.\n", 224 | " \n", 225 | "* Would it be better if we used a more precise integration function? No, because with the expression above we use the properties of the $\\exp(-2i\\pi kn/N)$ series to obtain two algorithms:\n", 226 | " * inverse DFT\n", 227 | " * Fast Fourier Transform" 228 | ] 229 | }, 230 | { 231 | "cell_type": "markdown", 232 | "metadata": { 233 | "slideshow": { 234 | "slide_type": "subslide" 235 | } 236 | }, 237 | "source": [ 238 | "* Note how, for $y(x)\\in \\mathbb{R}$,\n", 239 | " $$c_{N-k} = \\sum_{n=0}^{N-1}y_n \\exp\\left(-i\\frac{2\\pi (N-k)n}{N}\\right) = \\sum_{n=0}^{N-1}y_n \\underbrace{\\text e^{-i 2\\pi n}}_{=1}\\exp\\left(+i\\frac{2\\pi kn}{N}\\right) = c_k^*,$$\n", 240 | " or, in short, $c_{N-k} = c_k^*$.\n", 241 | "\n", 242 | " * If $y(x)\\in \\mathbb{R}$, then we only need $N/2 + 1$ ($N$ even) or $(N+1)/2$ ($N$ odd) points to actually know the DFT.\n", 243 | " * Python's `N//2+1` will give you this number." 244 | ] 245 | }, 246 | { 247 | "cell_type": "code", 248 | "execution_count": 58, 249 | "metadata": { 250 | "slideshow": { 251 | "slide_type": "fragment" 252 | } 253 | }, 254 | "outputs": [ 255 | { 256 | "data": { 257 | "text/plain": [ 258 | "5" 259 | ] 260 | }, 261 | "execution_count": 58, 262 | "metadata": {}, 263 | "output_type": "execute_result" 264 | } 265 | ], 266 | "source": [ 267 | "N = 9 # increase it\n", 268 | "N//2 + 1" 269 | ] 270 | }, 271 | { 272 | "cell_type": "markdown", 273 | "metadata": {}, 274 | "source": [ 275 | "### Discrete sine and cosine Fourier transforms" 276 | ] 277 | }, 278 | { 279 | "cell_type": "markdown", 280 | "metadata": { 281 | "slideshow": { 282 | "slide_type": "slide" 283 | } 284 | }, 285 | "source": [ 286 | "Recall\n", 287 | "$$f(x) = \\sum_{k=0}^{\\infty}\\left[\\alpha_k \\cos\\left(\\frac{2\\pi k x}{L}\\right) + \\beta_k\\sin\\left(\\frac{2\\pi k x}{L}\\right)\\right].$$\n", 288 | "\n", 289 | "* If $f$ odd (i.e., $f(-x) = -f(x)$), then $\\forall k$, $\\alpha_k = 0$,\n", 290 | "* If $f$ even (i.e., $f(-x) = +f(x)$), then $\\forall k$, $\\beta_k = 0$.\n", 291 | "\n", 292 | "If you know that function has one of these properties, computing only 1/2 coefficients saves time and memory. " 293 | ] 294 | }, 295 | { 296 | "cell_type": "markdown", 297 | "metadata": {}, 298 | "source": [ 299 | "## Inverse Discrete Fourier transform" 300 | ] 301 | }, 302 | { 303 | "cell_type": "markdown", 304 | "metadata": { 305 | "slideshow": { 306 | "slide_type": "slide" 307 | } 308 | }, 309 | "source": [ 310 | "The inverse DFT follows from the definition of the DFT and properties of exponential sums.\n", 311 | "\n", 312 | "iDFT: $\\boxed{\\displaystyle y_n = \\frac1N \\sum_{k=0}^{N-1}c_k \\exp\\left(i\\frac{2\\pi kn}{N}\\right)}$.\n", 313 | "\n", 314 | "\\begin{align*}\n", 315 | "\\sum_{k=0}^{N-1}c_k \\exp\\left(i\\frac{2\\pi kn}{N}\\right) & = \\sum_{k=0}^{N-1}\\sum_{p=0}^{N-1} y_p \\exp\\left(-i\\frac{2\\pi kp}{N}\\right)\\exp\\left(i\\frac{2\\pi kn}{N}\\right) \\\\\n", 316 | " &= \\sum_{k=0}^{N-1}\\sum_{p=0}^{N-1} y_p \\exp\\left(i\\frac{2\\pi k(n-p)}{N}\\right) \\\\\n", 317 | " &= \\sum_{p=0}^{N-1} y_p \\sum_{k=0}^{N-1}\\exp\\left(i\\frac{2\\pi k(n-p)}{N}\\right)\n", 318 | "\\end{align*}" 319 | ] 320 | }, 321 | { 322 | "cell_type": "markdown", 323 | "metadata": { 324 | "slideshow": { 325 | "slide_type": "subslide" 326 | } 327 | }, 328 | "source": [ 329 | "We can simplify using geometric series:\n", 330 | "$$\\forall a\\in \\mathbb C, \\quad \\sum_{k=0}^{N-1} a^k = \\frac{1-a^N}{1-a}.$$\n", 331 | "Using $a = \\exp(+i2\\pi m/N)$, \n", 332 | "$$\\sum_{k=0}^{N-1} \\exp\\left(+i\\frac{2\\pi k m}N\\right) = \\frac{1-\\exp(i2\\pi m)}{1-\\exp(i2\\pi m/N)}.$$\n", 333 | "$m\\in\\mathbb N \\Rightarrow 1-\\exp(i2\\pi m) = 0$.\n", 334 | "Two possibilities for denominator:\n", 335 | "* If $m$ not multiple of $N$, denom. $\\neq 0\\ \\Rightarrow \\sum_{k=0}^{N-1}\\dots = 0$.\n", 336 | "* If $m$ is $0$ or multiple of $N$, then $1-\\exp(i2\\pi m/N) = 0$ also!\n", 337 | " \n", 338 | " 0 divided by 0, we need to step back:\n", 339 | " $$\\sum_{k=0}^{N-1} \\exp(+i2\\pi k p) = \\sum_{k=0}^{N-1} 1 = N.$$" 340 | ] 341 | }, 342 | { 343 | "cell_type": "markdown", 344 | "metadata": { 345 | "slideshow": { 346 | "slide_type": "subslide" 347 | } 348 | }, 349 | "source": [ 350 | "Therefore, the innermost sum of the previous double-sum is $N$ when $p=n$, and zero otherwise:\n", 351 | "$$\\sum_{p=0}^{N-1} y_p \\sum_{k=0}^{N-1}\\exp\\left(i\\frac{2\\pi k(n-p)}{N}\\right) = y_n \\sum_{k=0}^{N-1}\\exp\\left(i\\frac{2\\pi k(n-n)}{N}\\right) = Ny_n.$$\n", 352 | "Divide the first and last expressions above, and you retrieve the iDFT expression framed above.\n", 353 | "\n", 354 | "Note there is no approximation error here, it's an exact result (up to machine precision)! A kind of double-compensation of errors happened, but it works, thanks to the trapezoidal rule!" 355 | ] 356 | }, 357 | { 358 | "cell_type": "markdown", 359 | "metadata": {}, 360 | "source": [ 361 | "## Fast Fourier Transforms (FFTs)" 362 | ] 363 | }, 364 | { 365 | "cell_type": "markdown", 366 | "metadata": { 367 | "slideshow": { 368 | "slide_type": "slide" 369 | } 370 | }, 371 | "source": [ 372 | "Can we speed up the DFT? Recall:\n", 373 | "\n", 374 | "$$\\qquad c_k = \\sum_{n=0}^{N-1} y_n \\exp\\left(-i\\frac{2\\pi k n}{N}\\right) $$\n", 375 | "\n", 376 | "The `dft` snippet below requires $\\approx N^2$ \"unit\" operations." 377 | ] 378 | }, 379 | { 380 | "cell_type": "code", 381 | "execution_count": null, 382 | "metadata": { 383 | "slideshow": { 384 | "slide_type": "fragment" 385 | } 386 | }, 387 | "outputs": [], 388 | "source": [ 389 | "for k in range(N//2+1):\n", 390 | " for n in range(N):\n", 391 | " c[k] += y[n]*np.exp(-2j*np.pi*k*n/N)" 392 | ] 393 | }, 394 | { 395 | "cell_type": "markdown", 396 | "metadata": { 397 | "slideshow": { 398 | "slide_type": "fragment" 399 | } 400 | }, 401 | "source": [ 402 | "* Your computer can afford a billion operations? Your limit is $N \\sim 32,\\!000$: too few to be practical.\n", 403 | "* Fast Fourier Transform (FFT) overcomes this (Cooley & Tukey 1960’s, first found by Gauss 1805).\n", 404 | "* There are alternative implementations, but we present the \"historical\" version." 405 | ] 406 | }, 407 | { 408 | "cell_type": "markdown", 409 | "metadata": {}, 410 | "source": [ 411 | "### Divide-and-conquer strategy" 412 | ] 413 | }, 414 | { 415 | "cell_type": "markdown", 416 | "metadata": { 417 | "slideshow": { 418 | "slide_type": "subslide" 419 | } 420 | }, 421 | "source": [ 422 | "Assume $N=2^M$ (other prime numbers in the decomposition are possible, but they will slow down the execution).\n", 423 | "$$\\text{Split}\\qquad c_k = \\sum_{n=0}^{N-1} y_n \\exp\\left(-i\\frac{2\\pi k n}{N}\\right) = E_k + \\omega^k O_k,$$\n", 424 | "with \n", 425 | "\\begin{align*}\n", 426 | "E_k & = \\sum_{p=0}^{N/2-1} y_{2p} \\exp\\left(-i\\frac{2 p\\pi k }{N/2}\\right)\\quad \\text{the even indices } (n=2p),\\\\\n", 427 | "O_k & = \\sum_{p=0}^{N/2-1} y_{2p+1} \\exp\\left(-i \\frac{2p\\pi k}{N/2}\\right)\\quad \\text{the odd indices, and}\\\\\n", 428 | "\\omega & = \\text e^{-i2\\pi /N}\\quad\\text{and}\\quad\\omega^k = \\text e^{-i2\\pi k/N}\\quad \\text{the \"twiddle factor\"}.\n", 429 | "\\end{align*}" 430 | ] 431 | }, 432 | { 433 | "cell_type": "markdown", 434 | "metadata": { 435 | "slideshow": { 436 | "slide_type": "subslide" 437 | } 438 | }, 439 | "source": [ 440 | "$$\\text{Split}\\qquad c_k = \\sum_{n=0}^{N-1} y_n \\exp\\left(-i\\frac{2\\pi k n}{N}\\right) = E_k + \\omega^k O_k,$$\n", 441 | "* $E_k$ and $O_k$ represent DFTs over points sampled twice as far apart as the original interval.\n", 442 | "* \\# of operations for each $E_k$ and $D_k$: $\\approx (N/2)^2$.\n", 443 | "* If we stopped here: \\# of operations would be $2\\times (N/2)^2 + 2 \\approx N^2/2+2$ (bisection + twiddle factor; OK, twiddle factor is a bit more, not enough to matter): **a lot less operations for large N!**\n", 444 | "* keep going: $E_k$ and $O_k$ can be bisected (split into two) themselves.\n", 445 | "* How many times can we do this until each $E_k$, $O_k$ has one term?\n", 446 | " * $N = 8 = 2^3$: we can do it $3 = \\log_2(8)$ times.\n", 447 | " * $N= 16 = 2^4$: we can do it $4=\\log_2(16)$ times.\n", 448 | " * ...\n", 449 | " * $N = 2^M$: we can do it $M = \\log_2(N)$ times.\n", 450 | " " 451 | ] 452 | }, 453 | { 454 | "cell_type": "markdown", 455 | "metadata": { 456 | "slideshow": { 457 | "slide_type": "subslide" 458 | } 459 | }, 460 | "source": [ 461 | "* If $N = 2^M$, after $M = \\log_2(N)$ bisections, we get $N$ DFTs of a single sample:\n", 462 | " $$c_0 = \\sum_{n=0}^{1-1}y_n\\text e^{-i 2\\pi k n/N} = y_0,$$\n", 463 | " the value at the sample point!" 464 | ] 465 | }, 466 | { 467 | "cell_type": "markdown", 468 | "metadata": { 469 | "slideshow": { 470 | "slide_type": "fragment" 471 | } 472 | }, 473 | "source": [ 474 | "So, you actually go back:\n", 475 | "* take all sample points: that's the 1st set of samples.\n", 476 | "* pair them according to the last FFT bisection in the process above,\n", 477 | "* multiply the odd pairs by appropriate twiddle factors,\n", 478 | "* use these results to work the next set of samples." 479 | ] 480 | }, 481 | { 482 | "cell_type": "markdown", 483 | "metadata": { 484 | "slideshow": { 485 | "slide_type": "subslide" 486 | } 487 | }, 488 | "source": [ 489 | "![Decimation in time of a length-N DFT into two length-N/2 DFTs followed by a combining stage, from Wikimedia Commons).](DIT-FFT-butterfly.png)" 490 | ] 491 | }, 492 | { 493 | "cell_type": "markdown", 494 | "metadata": {}, 495 | "source": [ 496 | "### General formulas" 497 | ] 498 | }, 499 | { 500 | "cell_type": "markdown", 501 | "metadata": { 502 | "slideshow": { 503 | "slide_type": "subslide" 504 | } 505 | }, 506 | "source": [ 507 | "The textbook provides the derivation, but they're hard to follow without using actual numbers.\n", 508 | "\n", 509 | "* $j$-th set of samples at the $m^\\text{th}$ stage:\n", 510 | "$$E_k^{(m,j)} = \\sum_{p=0}^{N/2^m - 1}y_{2^m p + j}\\exp\\left(-i\\frac{2\\pi k p}{N/2^m}\\right),\\quad j \\in \\{0\\dots 2^m-1\\}$$\n", 511 | "Note: all $E_k$ and $O_k$ of previous slides are now some $E_k^{(m, j)}$.\n", 512 | "* $2^m = $ \\# of DFTs at each level (indexed by $j$),\n", 513 | "* $N/2^m = $ \\# of samples per intermediate DFT (indexed by $k$),\n", 514 | "* Recursively, working from $M = \\log_2 N$:\n", 515 | " * First step: $E_k^{(M, j)} = y_j$ (no $k$ dependence), **ops:** $N$\n", 516 | " * Next steps: $E_k^{(m, j)} = E_k^{(m+1, j)} + \\omega^{2^m k}E_k^{(m+1, j+2^m)}$, **ops:** $N/2^m \\times 2^m = N$\n", 517 | " * Last step: $E_k^{(0, 0)} = c_k$, the desired DFT coefficients. **ops:** $N \\times 1 = N$" 518 | ] 519 | }, 520 | { 521 | "cell_type": "markdown", 522 | "metadata": { 523 | "slideshow": { 524 | "slide_type": "subslide" 525 | } 526 | }, 527 | "source": [ 528 | "* We end up with $N$ terms in each of the $\\log_2(N)$ bisections, so the number of operations is $N\\log_2(N)$.\n", 529 | "* Huge speed increase for large $N$\n", 530 | "* For $N=10^6$, old DFT algorithm is $O(N^2) = 10^{12}$ ops, but FFT is $O(N\\log_2(N)) \\sim 2 \\times 10^7$ ops.\n", 531 | "* Opens door to a wide range of calculations.\n", 532 | "* Also more precise: less ops = less accumulation of machine precision errors.\n", 533 | "* Note that the same reasoning applies to the inverse FT: the algorithm is called the inverse FFT (iFFT)." 534 | ] 535 | }, 536 | { 537 | "cell_type": "markdown", 538 | "metadata": { 539 | "tags": [] 540 | }, 541 | "source": [ 542 | "### Implementation Notes" 543 | ] 544 | }, 545 | { 546 | "cell_type": "markdown", 547 | "metadata": { 548 | "slideshow": { 549 | "slide_type": "fragment" 550 | } 551 | }, 552 | "source": [ 553 | "* The general formulas are useful if you want to code the FFT yourself: see textbook Exercise 7.7), and script `fft_ts.py` (derived from `dft_ts.py`)\n", 554 | "* But it's better to use packages. There are good tricks for saving memory that are implemented in packages like `numpy.fft`:\n", 555 | " https://numpy.org/doc/stable/reference/routines.fft.html" 556 | ] 557 | }, 558 | { 559 | "cell_type": "markdown", 560 | "metadata": {}, 561 | "source": [ 562 | "## 2D DFTs" 563 | ] 564 | }, 565 | { 566 | "cell_type": "markdown", 567 | "metadata": { 568 | "slideshow": { 569 | "slide_type": "slide" 570 | } 571 | }, 572 | "source": [ 573 | "Suppose we have a $M \\times N$ sample grid, with values $y_{mn}$. To perform 2D DFT:" 574 | ] 575 | }, 576 | { 577 | "cell_type": "markdown", 578 | "metadata": { 579 | "slideshow": { 580 | "slide_type": "fragment" 581 | } 582 | }, 583 | "source": [ 584 | "* Fourier transform the $M$ rows:\n", 585 | "$$c'_{m\\ell} = \\sum_{n=0}^{N-1}y_{mn}\\exp\\left(-i\\frac{2\\pi \\ell n}{N}\\right)$$" 586 | ] 587 | }, 588 | { 589 | "cell_type": "markdown", 590 | "metadata": { 591 | "slideshow": { 592 | "slide_type": "fragment" 593 | } 594 | }, 595 | "source": [ 596 | "* Fourier transform the $N$ columns:\n", 597 | "$$c_{k\\ell} = \\sum_{m=0}^{M-1}c'_{m\\ell}\\exp\\left(-i\\frac{2\\pi km}{M}\\right) = \\sum_{k=0}^{M-1}\\sum_{n=0}^{N-1} y_{mn} \\exp\\left[-i 2\\pi\\left(\\frac{km}{M} + \\frac{\\ell n}{N}\\right)\\right].$$" 598 | ] 599 | }, 600 | { 601 | "cell_type": "markdown", 602 | "metadata": {}, 603 | "source": [ 604 | "### Inverse 2D DFT" 605 | ] 606 | }, 607 | { 608 | "cell_type": "markdown", 609 | "metadata": { 610 | "slideshow": { 611 | "slide_type": "subslide" 612 | } 613 | }, 614 | "source": [ 615 | "Inverse 2D DFT:\n", 616 | "$$y_{mn} = \\frac{1}{MN}\\sum_{k=0}^{M-1}\\sum_{\\ell=0}^{N-1}c_{kl}\\exp\\left[i 2\\pi\\left(\\frac{km}{N} + \\frac{\\ell n}{N}\\right)\\right].$$" 617 | ] 618 | }, 619 | { 620 | "cell_type": "markdown", 621 | "metadata": {}, 622 | "source": [ 623 | "## Crucial points to remember" 624 | ] 625 | }, 626 | { 627 | "cell_type": "markdown", 628 | "metadata": { 629 | "cell_style": "split", 630 | "slideshow": { 631 | "slide_type": "slide" 632 | } 633 | }, 634 | "source": [ 635 | "### Discrete Fourier Transforms\n", 636 | "\n", 637 | "* Compute the integrals in the formulas for the Fourier coefficients with the trapezoidal rule\n", 638 | "* Periodicity of the signal makes the trapezoidal rule even easier\n", 639 | "* Trapezoidal rule: not the best integral, but to compute the inverse DFT yields **exactly** the original values!" 640 | ] 641 | }, 642 | { 643 | "cell_type": "markdown", 644 | "metadata": { 645 | "cell_style": "split", 646 | "slideshow": { 647 | "slide_type": "subslide" 648 | } 649 | }, 650 | "source": [ 651 | "### Fast Fourier Transforms\n", 652 | "\n", 653 | "* End result is **exactly** the end result of DFT\n", 654 | "* Much faster simply thanks to a clever rearrangement of order of operations: \"divide-and-conquer\".\n", 655 | "* Made possible by symmetries in roots of unity $\\exp(2i\\pi n/N)$\n", 656 | "* Bisections and multiplications: $O(N\\log_2N)$ ops.\n", 657 | "* Much faster than $O(N^2)$ for DFT." 658 | ] 659 | }, 660 | { 661 | "cell_type": "code", 662 | "execution_count": null, 663 | "metadata": {}, 664 | "outputs": [], 665 | "source": [] 666 | } 667 | ], 668 | "metadata": { 669 | "celltoolbar": "Slideshow", 670 | "kernelspec": { 671 | "display_name": "Python 3 (ipykernel)", 672 | "language": "python", 673 | "name": "python3" 674 | }, 675 | "language_info": { 676 | "codemirror_mode": { 677 | "name": "ipython", 678 | "version": 3 679 | }, 680 | "file_extension": ".py", 681 | "mimetype": "text/x-python", 682 | "name": "python", 683 | "nbconvert_exporter": "python", 684 | "pygments_lexer": "ipython3", 685 | "version": "3.10.9" 686 | } 687 | }, 688 | "nbformat": 4, 689 | "nbformat_minor": 4 690 | } 691 | --------------------------------------------------------------------------------