├── Jed-Midterm1.pdf ├── Jed-Midterm2.pdf ├── figures ├── factor-nd.png ├── rktrees.png ├── factor-rcm.png ├── TB-Householder.png ├── TB-Wilkinson.png ├── YingAO-KIFMM.png ├── factor-natural.png ├── sparse-natural.png ├── TB-Householder2.png ├── Toro-Shock-2.13.png ├── Warren-Treecode.png ├── YokotaBarba-FMM.png ├── Toro-Rarefaction-2.16.png ├── Berger2005-3a-SymSlope.png ├── Berger2005-3b-CellAverages.png ├── Toro-ShockRarefaction-2.14.png ├── Kirby2004-QuadraticTriangle.png └── WitherdenVincent-Quadrature.png ├── README.md ├── LICENSE ├── fdtools.py ├── Syllabus.md ├── AlgebraicSolvers.ipynb ├── Jed-HW2.ipynb └── Jed-HW1.ipynb /Jed-Midterm1.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cu-numpde/numpde-fall2018/HEAD/Jed-Midterm1.pdf -------------------------------------------------------------------------------- /Jed-Midterm2.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cu-numpde/numpde-fall2018/HEAD/Jed-Midterm2.pdf -------------------------------------------------------------------------------- /figures/factor-nd.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cu-numpde/numpde-fall2018/HEAD/figures/factor-nd.png -------------------------------------------------------------------------------- /figures/rktrees.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cu-numpde/numpde-fall2018/HEAD/figures/rktrees.png -------------------------------------------------------------------------------- /figures/factor-rcm.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cu-numpde/numpde-fall2018/HEAD/figures/factor-rcm.png -------------------------------------------------------------------------------- /figures/TB-Householder.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cu-numpde/numpde-fall2018/HEAD/figures/TB-Householder.png -------------------------------------------------------------------------------- /figures/TB-Wilkinson.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cu-numpde/numpde-fall2018/HEAD/figures/TB-Wilkinson.png -------------------------------------------------------------------------------- /figures/YingAO-KIFMM.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cu-numpde/numpde-fall2018/HEAD/figures/YingAO-KIFMM.png -------------------------------------------------------------------------------- /figures/factor-natural.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cu-numpde/numpde-fall2018/HEAD/figures/factor-natural.png -------------------------------------------------------------------------------- /figures/sparse-natural.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cu-numpde/numpde-fall2018/HEAD/figures/sparse-natural.png -------------------------------------------------------------------------------- /figures/TB-Householder2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cu-numpde/numpde-fall2018/HEAD/figures/TB-Householder2.png -------------------------------------------------------------------------------- /figures/Toro-Shock-2.13.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cu-numpde/numpde-fall2018/HEAD/figures/Toro-Shock-2.13.png -------------------------------------------------------------------------------- /figures/Warren-Treecode.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cu-numpde/numpde-fall2018/HEAD/figures/Warren-Treecode.png -------------------------------------------------------------------------------- /figures/YokotaBarba-FMM.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cu-numpde/numpde-fall2018/HEAD/figures/YokotaBarba-FMM.png -------------------------------------------------------------------------------- /figures/Toro-Rarefaction-2.16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cu-numpde/numpde-fall2018/HEAD/figures/Toro-Rarefaction-2.16.png -------------------------------------------------------------------------------- /figures/Berger2005-3a-SymSlope.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cu-numpde/numpde-fall2018/HEAD/figures/Berger2005-3a-SymSlope.png -------------------------------------------------------------------------------- /figures/Berger2005-3b-CellAverages.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cu-numpde/numpde-fall2018/HEAD/figures/Berger2005-3b-CellAverages.png -------------------------------------------------------------------------------- /figures/Toro-ShockRarefaction-2.14.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cu-numpde/numpde-fall2018/HEAD/figures/Toro-ShockRarefaction-2.14.png -------------------------------------------------------------------------------- /figures/Kirby2004-QuadraticTriangle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cu-numpde/numpde-fall2018/HEAD/figures/Kirby2004-QuadraticTriangle.png -------------------------------------------------------------------------------- /figures/WitherdenVincent-Quadrature.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cu-numpde/numpde-fall2018/HEAD/figures/WitherdenVincent-Quadrature.png -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Numerical solution of partial differential equations 2 | 3 | ## Quick info from the [Syllabus](Syllabus.md) 4 | 5 | CU Boulder: CSCI 5636-001(B) (Fall 2018) 6 | 7 | Meeting Time: MWF 2:00-2:50pm in ECCR 1B51 8 | 9 | #### Remote access via Zoom 10 | 11 | * Join via web browser: https://cuboulder.zoom.us/j/593324843 12 | * Join via Zoom app (using meeting ID 593-324-843) 13 | * Join via phone: +1-669-900-6833 or +1-646-558-8656 14 | 15 | #### Instructor 16 | 17 | [Jed Brown](https://jedbrown.org), [jed.brown@colorado.edu](mailto:jed.brown@colorado.edu), ECOT 824 18 | 19 | Office Hours: Mon 9-10am, Thu 8:30-10am, or by appointment. 20 | 21 | ## Homeworks 22 | 23 | 1. Homework 1: Due 2018-09-16 (Sunday), see [FiniteDifference notebook](FiniteDifference.ipynb) 24 | 2. Homework 2: Due 2018-09-30 (Sunday), see [FDHighOrder notebook](FDHighOrder.ipynb) 25 | 3. Homework 3: Due 2018-10-24 (Wednesday), see [FD2D notebook](FD2D.ipnb) 26 | 4. Homework 4: Due 2018-12-03 (Sunday), see [FDTransient notebook](FDTransient.ipynb) 27 | 5. Homework 5 (optional/extra credit): Due 2018-12-16 (Sunday), see [FVHyperbolic notebook](FVHyperbolic.ipynb) 28 | 29 | ## News 30 | 31 | * will be announced here 32 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2017-2018, Jed Brown 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without modification, 5 | are permitted provided that the following conditions are met: 6 | 7 | * Redistributions of source code must retain the above copyright notice, this 8 | list of conditions and the following disclaimer. 9 | * Redistributions in binary form must reproduce the above copyright notice, this 10 | list of conditions and the following disclaimer in the documentation and/or 11 | other materials provided with the distribution. 12 | 13 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 14 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 15 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 16 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR 17 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 18 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 19 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 20 | ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 21 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 22 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 23 | -------------------------------------------------------------------------------- /fdtools.py: -------------------------------------------------------------------------------- 1 | import numpy 2 | 3 | def cosspace(a, b, n=50): 4 | return (a + b)/2 + (b - a)/2 * (numpy.cos(numpy.linspace(-numpy.pi, 0, n))) 5 | 6 | def vander_chebyshev(x, n=None): 7 | if n is None: 8 | n = len(x) 9 | T = numpy.ones((len(x), n)) 10 | if n > 1: 11 | T[:,1] = x 12 | for k in range(2,n): 13 | T[:,k] = 2 * x * T[:,k-1] - T[:,k-2] 14 | return T 15 | 16 | def chebeval(z, n=None): 17 | """Build matrices to evaluate the n-term Chebyshev expansion and its derivatives at point(s) z""" 18 | z = numpy.array(z, ndmin=1) 19 | if n is None: 20 | n = len(z) 21 | Tz = vander_chebyshev(z, n) 22 | dTz = numpy.zeros_like(Tz) 23 | dTz[:,1] = 1 24 | dTz[:,2] = 4*z 25 | ddTz = numpy.zeros_like(Tz) 26 | ddTz[:,2] = 4 27 | for n in range(3,n): 28 | dTz[:,n] = n * (2*Tz[:,n-1] + dTz[:,n-2]/(n-2)) 29 | ddTz[:,n] = n * (2*dTz[:,n-1] + ddTz[:,n-2]/(n-2)) 30 | return [Tz, dTz, ddTz] 31 | 32 | def fdstencilV(z, x): 33 | """Compute finite difference weights using a Vandermonde matrix""" 34 | x = numpy.array(x) 35 | V = numpy.vander(x - z, increasing=True) 36 | scaling = numpy.array([numpy.math.factorial(i) for i in range(len(x))]) 37 | return (numpy.linalg.inv(V).T * scaling).T 38 | 39 | def fdstencil(z, x, nderiv=None): 40 | """Compute finite difference weights using recurrences for Lagrange polynomials (see Fornberg 1998)""" 41 | if nderiv is None: 42 | nderiv = len(x) 43 | x = numpy.array(x) - z 44 | k = numpy.arange(nderiv+1) 45 | c = numpy.outer(0.*k, x) 46 | c[0,0] = 1 47 | prod = 1 48 | for j in range(1,len(x)): 49 | dx = x[j] - x[:j] 50 | c[1:,j] = x[j-1]*c[1:,j-1] - k[1:]*c[:-1,j-1] 51 | c[0,j] = x[j-1]*c[0,j-1] 52 | c[:,j] *= -prod 53 | prod = numpy.prod(dx) 54 | c[:,j] /= prod 55 | c[1:,:j] = (x[j]*c[1:,:j] - k[1:,None]*c[:-1,:j]) / dx 56 | c[0,:j] = x[j]*c[0,:j] / dx 57 | return c 58 | 59 | def fdcompact(z, x, k): 60 | """Compute a compact (implicit) differencing scheme 61 | 62 | b @ u^(k)(z) = c @ u(x) 63 | 64 | that maximizes the accuracy of u^(k)(z[0]).""" 65 | z = numpy.array(z) 66 | x = numpy.array(x) 67 | n = len(x) 68 | x = x - z[0] 69 | z = z - z[0] 70 | xmin, xmax = min(x), max(x) 71 | dx = (xmax - xmin) / (n - 1) 72 | y = numpy.zeros(n + len(z) - 1) 73 | y[:n] = x 74 | for i in range(1, len(z)): 75 | if (z[i] < 0): 76 | xmin -= dx 77 | y[n + i - 1] = xmin 78 | else: 79 | xmax += dx 80 | y[n + i - 1] = xmax 81 | S = numpy.array([fdstencil(t, y, k)[k] for t in z]) 82 | b = numpy.ones(len(z)) 83 | T = S[1:,n:].T 84 | b[1:] = numpy.linalg.lstsq(T, -S[0,n:])[0] 85 | c = b.dot(S[:,:n]) 86 | return b, c 87 | 88 | def dispersion(z, x, b, c): 89 | from matplotlib import pyplot 90 | theta = numpy.linspace(0, numpy.pi, 100)[1:] 91 | phiz = numpy.exp(1j*numpy.outer(z, theta)) 92 | phix = numpy.exp(1j*numpy.outer(x, theta)) 93 | pyplot.plot(theta, (c.dot(phix) / b.dot(phiz)).imag, '.') 94 | pyplot.plot(theta, theta) 95 | pyplot.plot(theta, numpy.sin(theta)) 96 | pyplot.show() 97 | 98 | def rk_butcher_4(): 99 | A = numpy.array([[0,0,0,0],[.5,0,0,0],[0,.5,0,0],[0,0,1,0]]) 100 | b = numpy.array([1/6, 1/3, 1/3, 1/6]) 101 | return A, b 102 | 103 | def rk_butcher_ssp32(): 104 | A = numpy.array([[0, 0, 0], 105 | [1/2, 0, 0], 106 | [1/2, 1/2, 0]]) 107 | b = numpy.array([1/3, 1/3, 1/3]) 108 | return A, b 109 | 110 | def ode_rkexplicit(f, u0, butcher=None, tfinal=1, h=.1): 111 | if butcher is None: 112 | A, b = rk_butcher_4() 113 | else: 114 | A, b = butcher 115 | c = numpy.sum(A, axis=1) 116 | s = len(c) 117 | u = u0.copy() 118 | t = 0 119 | hist = [(t,u0)] 120 | while t < tfinal: 121 | if tfinal - t < 1.01*h: 122 | h = tfinal - t 123 | tnext = tfinal 124 | else: 125 | tnext = t + h 126 | h = min(h, tfinal - t) 127 | fY = numpy.zeros((s, len(u0))) 128 | for i in range(s): 129 | Yi = u.copy() 130 | for j in range(i): 131 | Yi += h * A[i,j] * fY[j] 132 | fY[i] = f(t + h*c[i], Yi) 133 | u += h * b @ fY 134 | t = tnext 135 | hist.append((t, u.copy())) 136 | return hist 137 | -------------------------------------------------------------------------------- /Syllabus.md: -------------------------------------------------------------------------------- 1 | # Numerical solution of partial differential equations 2 | 3 | ## Syllabus 4 | 5 | CU Boulder: CSCI 5636-001(B) (Fall 2018) 6 | 7 | Meeting Time: MWF 2:00-2:50pm in ECCR 1B51 8 | 9 | #### Remote access via Zoom 10 | 11 | * Join via web browser: https://cuboulder.zoom.us/j/593324843 12 | * Join via Zoom app (using meeting ID 593-324-843) 13 | * Join via phone: +1-669-900-6833 or +1-646-558-8656 14 | 15 | #### Recordings 16 | 17 | * https://cu-classcapture.colorado.edu/Mediasite/Catalog/Full/b6950dea3ec743eca009777dd486fa5e21 18 | 19 | #### Gitter discussion forum 20 | 21 | https://gitter.im/cucs-numpde/Lobby 22 | 23 | #### Instructor 24 | 25 | [Jed Brown](https://jedbrown.org), [jed.brown@colorado.edu](mailto:jed.brown@colorado.edu), ECOT 824 26 | 27 | Office Hours: Mon 9-10am, Thu 8:30-10am, or by appointment. 28 | 29 | ### Overview 30 | 31 | Partial differential equations (PDE) describe the behavior of fluids, structures, heat transfer, wave propagation, and other physical phenomena of scientific and engineering interest. This course 32 | covers discretization of PDE using finite difference and collocation methods, finite volume methods, and finite element methods for elliptic, parabolic, and hyperbolic equations. We will discuss foundational principles like will posedness and explores efficient methods for solution of the discretized equations. 33 | 34 | ### Organization 35 | 36 | We will start with a brief refresher on numerical integration, approximation of functions, and numerical differentiation. We will extend this to finite difference methods for elliptic problems (like heat and pressure equilibrium) and time integration, producing methods that converge with arbitrarily high orders of accuracy. We will discover challenges when applying these methods to hyperbolic equations (which describe wave propagation and transport phenomena), especially nonlinear hyperbolic equations, and thus develop finite volume methods which rely on weaker assumptions. We will learn about shocks, rarefactions, and Riemann solvers. Finite volume methods are easy to use in complicated geometries and/or unstructured meshes, but achieving high order accuracy in such settings is unnatural and the methods can be awkward for elliptic and parabolic equations. Finite element methods offer a flexible and robust analysis framework as well as modular implementation that allows arbitrary order of accuracy even in complicated domains. We will introduce relevant concepts in continuum mechanics as we go. 37 | 38 | ### Benefits 39 | 40 | Partial differential equations underly a broad range of high-fidelity models in science and engineering from atomic to cosmological scales. 41 | Upon completing this course, students possess an ability to 42 | 43 | * formulate problems in science and engineering in terms of partial differential equations 44 | * understand the merits and limitations of the leading numerical methods used to solve PDE 45 | * recognize and exploit structure to apply algorithms that improve performance and scalability 46 | * select and use robust software libraries 47 | * develop effective numerical software, taking into account stability, accuracy, and cost 48 | * predict scaling challenges and computational costs when solving increasingly complex problems or attempting to meet real-time requirements 49 | * interpret research papers and begin research in the field 50 | 51 | ### Evaluation 52 | 53 | * 40% class participation and contribution to homework, which will involve some software development and numerical experiments. 54 | * 30% final project. A one-page written proposal is due November 12 and the project (code + write-up) is due on December 18. I'll help you find a suitable project. 55 | * Two open-neighbor midterms of 15% each, to be held in-class October 5 and November 9. 56 | 57 | Assignments will be submitted via GitHub. Start by forking the [class repository](https://github.com/cucs-numpde/numpde). 58 | 59 | The grade for coding assignments will be a combination of pure correctness, code quality, and efficiency/scalability. It is notoriously difficult to predict the time required to produce quality code, so please start early to give yourself plenty of time. 60 | 61 | You are encouraged to work together on all assignments, but must give credit to peer contributions via the commit messages or Git history. For example, you would add 62 | 63 | Suggested-by: Friendly Neighbor 64 | 65 | to the commit message if that code incorporates an approach suggested by your neighbor. Alternatively, you can `merge` or `cherry-pick` a commit written by your peer (these operations preserve author information). You should ensure that each assignment (pull request) contains some of your own meaningful intellectual contributions. 66 | 67 | ### Programming languages and environment 68 | 69 | I will use Python and [Jupyter notebooks](https://jupyter.org/) for most examples in class. This environment is convenient to work with, general purpose, and has extensive library support. Native Python code is not high performance, however. Production numerical software is most frequently written in C, C++, or Fortran, perhaps called from a higher level programming language like Python. MATLAB is also popular for numerical computing, though it is a proprietary environment and lacks general-purpose libraries. Octave is a free MATLAB clone and Julia is a modern language that preserves much of the syntactic convenience. Any language is allowed for your own work, but I recommend one of the above. Most HPC facilities use a Linux operating system. You can use any environment for your local development environment, or use [Azure notebooks](https://notebooks.azure.com/) to experiment and develop in the cloud (sign in with colorado.edu credentials). 70 | 71 | ### Target audience 72 | 73 | Graduate students in computer science or simulation-based science or engineering. Suggested prereq: at least one of 74 | 75 | * CSCI-2820 Linear Algebra 76 | * CSCI-3656 Numerical Computation 77 | * CSCI-4576 High-Performance Scientific Computing 78 | 79 | ### Resources (updated continuously) 80 | 81 | * http://www.siam.org/students/memberships.php (SIAM Membership is free for CU students, 30% discount on SIAM books) 82 | * [LeVeque, *Finite Difference Methods for Ordinary and Partial Differential Equations*](https://faculty.washington.edu/rjl/fdmbook/) (CU students can [download free from SIAM](http://epubs.siam.org/doi/book/10.1137/1.9780898717839)) 83 | * [LeVeque, *Finite Volume Methods for Hyperbolic Problems*](https://depts.washington.edu/clawpack/book.html) and the [Clawpack software](http://www.clawpack.org/). 84 | * [Toro, *Riemann Solvers and Numerical Methods for Fluid Dynamics*](https://link.springer.com/book/10.1007%2Fb79761#toc). (CU students can download free) 85 | * [Logg, Mardal, Wells, *Automated Solution of Differential Equations by the Finite Element Method (The FEniCS Book)*](https://link.springer.com/book/10.1007%2F978-3-642-23099-8). (free download) 86 | * [Trefethen, *Spectral Methods in MATLAB*](https://people.maths.ox.ac.uk/trefethen/spectral.html). (CU students can [download free from SIAM](http://epubs.siam.org/doi/book/10.1137/1.9780898719598)) 87 | * Elman, Silvester, Wathen, *Finite Elements and Fast Iterative Solvers with Applications in Incompressible Fluid Dynamics* 88 | 89 | ### Disability Accommodations 90 | 91 | If you qualify for accommodations because of a disability, please submit to your professor a letter from Disability Services in a timely manner (for exam accommodations provide your letter at least one week prior to the exam) so that your needs can be addressed. Disability Services determines accommodations based on documented disabilities. Contact Disability Services at 303-492-8671 or by e-mail at dsinfo@colorado.edu. If you have a temporary medical condition or injury, see the Temporary Injuries guidelines under the Quick Links at the Disability Services website and discuss your needs with your professor. 92 | 93 | ### Religious Observances 94 | 95 | [Campus policy regarding religious observances](http://www.colorado.edu/policies/fac_relig.html) requires that faculty make every effort to deal reasonably and fairly with all students who, because of religious obligations, have conflicts with scheduled exams, assignments or required assignments/attendance. If this applies to you, please speak with me directly as soon as possible at the beginning of the term. See the [campus policy regarding religious observances](http://www.colorado.edu/policies/observance-religious-holidays-and-absences-classes-andor-exams) for full details. 96 | 97 | ### Classroom Behavior 98 | 99 | Students and faculty each have responsibility for maintaining an appropriate learning environment. Those who fail to adhere to such behavioral standards may be subject to discipline. Professional courtesy and sensitivity are especially important with respect to individuals and topics dealing with differences of race, color, culture, religion, creed, politics, veteran's status, sexual orientation, gender, gender identity and gender expression, age, disability,and nationalities. Class rosters are provided to the instructor with the student's legal name. I will gladly honor your request to address you by an alternate name or gender pronoun. Please advise me of this preference early in the semester so that I may make appropriate changes to my records. For more information, see the policies on [classroom behavior](http://www.colorado.edu/policies/student-classroom-and-course-related-behavior) and the [student code](http://www.colorado.edu/osc/sites/default/files/attached-files/studentconductcode_16-17-a.pdf). 100 | 101 | ### Discrimination and Harassment 102 | 103 | The University of Colorado Boulder (CU Boulder) is committed to maintaining a positive learning, working, and living environment. CU Boulder will not tolerate acts of sexual misconduct, discrimination, harassment or related retaliation against or by any employee or student. CU's Sexual Misconduct Policy prohibits sexual assault, sexual exploitation, sexual harassment,intimate partner abuse (dating or domestic violence), stalking or related retaliation. CU Boulder's Discrimination and Harassment Policy prohibits discrimination, harassment or related retaliation based on race, color,national origin, sex, pregnancy, age, disability, creed, religion, sexual orientation, gender identity, gender expression, veteran status, political affiliation or political philosophy. Individuals who believe they have been subject to misconduct under either policy should contact the Office of Institutional Equity and Compliance (OIEC) at 303-492-2127. Information about the OIEC, the above referenced policies, and the campus resources available to assist individuals regarding sexual misconduct, discrimination, harassment or related retaliation can be found at the [OIEC website](http://www.colorado.edu/institutionalequity/). 104 | 105 | ### Honor Code 106 | 107 | All students enrolled in a University of Colorado Boulder course are responsible for knowing and adhering to the [academic integrity policy](http://www.colorado.edu/policies/academic-integrity-policy) of the institution. Violations of the policy may include: plagiarism, cheating,fabrication, lying, bribery, threat, unauthorized access, clicker fraud,resubmission, and aiding academic dishonesty. All incidents of academic misconduct will be reported to the Honor Code Council (honor@colorado.edu; 303-735-2273). Students who are found responsible for violating the academic integrity policy will be subject to nonacademic sanctions from the Honor Code Council as well as academic sanctions from the faculty member. Additional information regarding the academic integrity policy can be found at http://honorcode.colorado.edu. 108 | -------------------------------------------------------------------------------- /AlgebraicSolvers.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "#### Jupyter notebooks\n", 8 | "\n", 9 | "This is a [Jupyter](http://jupyter.org/) notebook using Python. You can install Jupyter locally to edit and interact with this notebook.\n", 10 | "\n", 11 | "# Algebraic solvers\n", 12 | "\n", 13 | "Whe solving elliptic boundary value problems as well as when using implicit methods for transient PDE, we need to solve algebraic systems of equations. We will write such systems as\n", 14 | "$$ F(u) = 0 $$\n", 15 | "where $u$ is a vector of state variables and $F(u)$ is a vector of residuals of the same length.\n", 16 | "We will primarily be interested in defect correction methods of the form\n", 17 | "\\begin{gather} A \\delta u = - F(u) \\\\\n", 18 | "u \\gets u + \\gamma \\delta u\n", 19 | "\\end{gather}\n", 20 | "where $A$ is a matrix and $\\gamma$ is a scalar parameter often found using a line search.\n", 21 | "\n", 22 | "* If $A = I$, this is a Richardson iteration, which is related to gradient descent. Such methods are usually quite slow unless $F(u)$ is especially \"nice\".\n", 23 | "* If $A = \\partial F/\\partial u$, this is a Newton method and $\\gamma=1$ can often be used.\n", 24 | "\n", 25 | "## Newton-Raphson methods for systems\n", 26 | "\n", 27 | "The **Jacobian** of $F$ is\n", 28 | "$$ J(u) = \\frac{\\partial F}{\\partial u}(u) =\n", 29 | "\\begin{bmatrix} \\frac{\\partial F_0}{\\partial u_0} & \\frac{\\partial F_0}{\\partial u_1} & \\dotsb \\\\\n", 30 | " \\frac{\\partial F_1}{\\partial u_0} & \\frac{\\partial F_1}{\\partial u_1} & \\\\\n", 31 | " \\vdots & & \\ddots\n", 32 | " \\end{bmatrix}(u) . $$\n", 33 | "The method can be derived by taking the Taylor expansion of $F(u)$ at $u$,\n", 34 | "$$ F(u + \\delta u) = F(u) + \\frac{\\partial F}{\\partial u}(u) (\\delta u) + \\frac{\\partial^2 F}{\\partial u^2}(u) (\\delta u \\otimes \\delta u) / 2 + \\dotsb $$\n", 35 | "Note that each higher term is a higher rank tensor, thus computationally unweildy. If we truncate the series with the linear term and set equal to zero, we have a linear equation for $\\delta u$\n", 36 | "$$ \\frac{\\partial F}{\\partial u}(u) \\delta u = - F(u) $$\n", 37 | "which will hopefully make $F(u + \\partial u) \\approx 0$. This is Newton's method.\n", 38 | "\n", 39 | "* Each iteration requires evaluating $F(u)$ -- almost any method will have this property.\n", 40 | "* Each iteration requires evaluating the Jacobian matrix $J(u)$ -- this either requires custom code, algorithmic differentiation, or a finite difference approximation (we'll revisit this later).\n", 41 | "* Each iteration requires solving a linear system with the matrix $J(u)$. This may be expensive." 42 | ] 43 | }, 44 | { 45 | "cell_type": "code", 46 | "execution_count": 1, 47 | "metadata": {}, 48 | "outputs": [ 49 | { 50 | "name": "stdout", 51 | "output_type": "stream", 52 | "text": [ 53 | "Newton 1 anorm 2.51e+00 rnorm 3.96e-01 eratio 1.56\n", 54 | "Newton 2 anorm 9.91e+00 rnorm 1.57e+00 eratio 0.56\n", 55 | "Newton 3 anorm 3.83e-01 rnorm 6.05e-02 eratio 0.22\n", 56 | "Newton 4 anorm 5.11e-01 rnorm 8.08e-02 eratio 0.25\n", 57 | "Newton 5 anorm 5.24e-04 rnorm 8.28e-05 eratio 0.36\n", 58 | "Newton 6 anorm 9.76e-07 rnorm 1.54e-07 eratio 0.21\n", 59 | "Newton 7 anorm 3.61e-15 rnorm 5.72e-16 eratio 0.27\n" 60 | ] 61 | }, 62 | { 63 | "data": { 64 | "text/plain": [ 65 | "(array([1., 1.]), 6)" 66 | ] 67 | }, 68 | "execution_count": 1, 69 | "metadata": {}, 70 | "output_type": "execute_result" 71 | } 72 | ], 73 | "source": [ 74 | "%matplotlib inline\n", 75 | "import numpy\n", 76 | "from matplotlib import pyplot\n", 77 | "pyplot.style.use('ggplot')\n", 78 | "\n", 79 | "def fsolve_newton(F, J, u0, rtol=1e-10, maxit=50, verbose=False):\n", 80 | " u = u0.copy()\n", 81 | " Fu = F(u)\n", 82 | " norm0 = numpy.linalg.norm(Fu)\n", 83 | " enorm_last = numpy.linalg.norm(u - numpy.array([1,1]))\n", 84 | " for i in range(maxit):\n", 85 | " du = -numpy.linalg.solve(J(u), Fu)\n", 86 | " u += du\n", 87 | " Fu = F(u)\n", 88 | " norm = numpy.linalg.norm(Fu)\n", 89 | " if verbose:\n", 90 | " enorm = numpy.linalg.norm(u - numpy.array([1,1]))\n", 91 | " print('Newton {:d} anorm {:6.2e} rnorm {:6.2e} eratio {:6.2f}'.\n", 92 | " format(i+1, norm, norm/norm0, enorm/enorm_last**2))\n", 93 | " enorm_last = enorm\n", 94 | " if norm < rtol * norm0:\n", 95 | " break\n", 96 | " return u, i\n", 97 | "\n", 98 | "def rostest(a,b):\n", 99 | " def F(u):\n", 100 | " x = u[0]; y = u[1]\n", 101 | " return numpy.array([-2*(a-x) + 4*b*x**3 - 4*b*x*y,\n", 102 | " 2*b*(y-x**2)])\n", 103 | " def J(u):\n", 104 | " x = u[0]; y = u[1]\n", 105 | " return numpy.array([[2 + 12*b*x**2 - 4*b*y, -4*b*x],\n", 106 | " [-4*b*x, 2*b]])\n", 107 | " return F, J\n", 108 | "\n", 109 | "F, J = rostest(1,3)\n", 110 | "fsolve_newton(F, J, numpy.array([0, 1.]), verbose=True)" 111 | ] 112 | }, 113 | { 114 | "cell_type": "markdown", 115 | "metadata": {}, 116 | "source": [ 117 | "* Can the iteration break down? How?\n", 118 | "* How does the method depend on the initial guess?\n", 119 | "* It turns out that Newton's method has _locally quadratic_ convergence to simple roots, $$\\lim_{i \\to \\infty} |e_{i+1}|/|e_i^2| < \\infty .$$\n", 120 | "* \"The number of correct digits doubles each iteration.\"\n", 121 | "* Now that we know how to make a good guess accurate, the effort lies in getting a good guess.\n", 122 | "\n", 123 | "## Matrix-free Jacobian via finite differencing\n", 124 | "\n", 125 | "It can be error-prone and complicated to implement the Jacobian function `J(u)`. In such cases, we can use the approximation\n", 126 | "\n", 127 | "$$ J(u) v \\approx \\frac{F(u+\\epsilon v) - F(u)}{\\epsilon} $$\n", 128 | "\n", 129 | "where $\\epsilon$ is some \"small\" number. Now can't access individual entries of $J$, but we can apply its action to an arbitrary vector $u$.\n", 130 | "\n", 131 | "We know that this approximation is first order accurate in $\\epsilon$, \n", 132 | "$$ \\left\\lVert J(u) v - \\frac{F(u+\\epsilon v) - F(u)}{\\epsilon} \\right\\rVert \\in O(\\epsilon) . $$\n", 133 | "But if $\\epsilon$ is too small, we will lose accuracy due to rounding error. If $F$ has been scaled such that its norm is of order 1, then $\\epsilon = \\sqrt{\\epsilon_{\\text{machine}}}$ is a good default choice." 134 | ] 135 | }, 136 | { 137 | "cell_type": "code", 138 | "execution_count": 2, 139 | "metadata": {}, 140 | "outputs": [ 141 | { 142 | "name": "stdout", 143 | "output_type": "stream", 144 | "text": [ 145 | "Newton 0 anorm 2.51e+00 rnorm 3.96e-01\n", 146 | "Newton 1 anorm 9.91e+00 rnorm 1.57e+00\n", 147 | "Newton 2 anorm 3.83e-01 rnorm 6.05e-02\n", 148 | "Newton 3 anorm 5.11e-01 rnorm 8.08e-02\n", 149 | "Newton 4 anorm 5.24e-04 rnorm 8.28e-05\n", 150 | "Newton 5 anorm 9.76e-07 rnorm 1.54e-07\n" 151 | ] 152 | }, 153 | { 154 | "data": { 155 | "text/plain": [ 156 | "(array([1. , 0.99999992]), 5)" 157 | ] 158 | }, 159 | "execution_count": 2, 160 | "metadata": {}, 161 | "output_type": "execute_result" 162 | } 163 | ], 164 | "source": [ 165 | "import scipy.sparse.linalg as splinalg\n", 166 | "\n", 167 | "def fsolve_newtonkrylov(F, u0, epsilon=1e-8, rtol=1e-10, maxit=50, verbose=False):\n", 168 | " u = u0.copy()\n", 169 | " Fu = F(u)\n", 170 | " norm0 = numpy.linalg.norm(Fu)\n", 171 | " for i in range(maxit):\n", 172 | " def Ju_fd(v):\n", 173 | " return (F(u + epsilon*v) - Fu) / epsilon\n", 174 | " Ju = splinalg.LinearOperator((len(Fu),len(u)), matvec=Ju_fd)\n", 175 | " du, info = splinalg.gmres(Ju, Fu, atol=1.e-6)\n", 176 | " if info != 0:\n", 177 | " print(numpy.linalg.norm(Ju @ du - Fu), norm)\n", 178 | " raise RuntimeError('GMRES failed to converge: {:d}'.format(info))\n", 179 | " u -= du\n", 180 | " Fu = F(u)\n", 181 | " norm = numpy.linalg.norm(Fu)\n", 182 | " if verbose:\n", 183 | " print('Newton {:d} anorm {:6.2e} rnorm {:6.2e}'\n", 184 | " .format(i, norm, norm/norm0))\n", 185 | " if norm < rtol * norm0:\n", 186 | " break\n", 187 | " return u, i\n", 188 | "\n", 189 | "fsolve_newtonkrylov(F, numpy.array([0.,1]), rtol=1e-6, verbose=True)" 190 | ] 191 | }, 192 | { 193 | "cell_type": "code", 194 | "execution_count": null, 195 | "metadata": {}, 196 | "outputs": [], 197 | "source": [] 198 | }, 199 | { 200 | "cell_type": "markdown", 201 | "metadata": {}, 202 | "source": [ 203 | "# Sparse and iterative linear algebra\n", 204 | "\n", 205 | "Many matrices in applications, particularly the study of physical systems and graphs/networks, have entries that are mostly equal to zero. We can more efficiently store such systems by storing only the nonzero elements. We will discuss storage and optimized implementations later. Many of the methods for sparse systems apply to solving systems with matrices $A$ that can be applied to a vector ($y \\gets A x$) in significantly less than $O(n^2)$ complexity, or that are \"well-conditioned\" such that an iterative method converges in significantly less than $n$ iterations.\n", 206 | "\n", 207 | "[PETSc](https://mcs.anl.gov/petsc), the Portable Extensible Toolkit for Scientific computing, is an open source software package for the parallel solution of algebraic and differential-algebraic equations. This includes linear algebra, for which PETSc offers a broad variety of implementations. For general information about PETSc, I refer to [this primer](https://jedbrown.org/files/20150924-PETScPrimer.pdf).\n", 208 | "\n", 209 | "NumPy does not provide sparse matrix support so we will use [SciPy](https://scipy.org) in this course." 210 | ] 211 | }, 212 | { 213 | "cell_type": "markdown", 214 | "metadata": {}, 215 | "source": [ 216 | "## Direct solves\n", 217 | "\n", 218 | "The complexity of this solve is potentially dominant, so we should understand its cost in terms of the problem size. The standard method for a direct solve is $LU$ (or Cholesky) factorization. Given a $2\\times 2$ block matrix, the algorithm proceeds as\n", 219 | "\\begin{split}\n", 220 | " \\begin{bmatrix} A & B \\\\ C & D \\end{bmatrix} &=\n", 221 | " \\begin{bmatrix} L_A & \\\\ C U_A^{-1} & 1 \\end{bmatrix}\n", 222 | " \\begin{bmatrix} U_A & L_A^{-1} B \\\\ & S \\end{bmatrix}\n", 223 | "\\end{split}\n", 224 | "where $L_A U_A = A$ and $S = D - C A^{-1} B$.\n", 225 | "\n", 226 | "For a sparse operator, the complexity depends on the ordering of degrees of freedom.\n", 227 | "\n", 228 | "* \"natural\" ordering\n", 229 | "* low-bandwidth ordering\n", 230 | "* nested dissection ordering\n", 231 | "\n", 232 | "For a structured grid, the \"natural\" ordering is the ordering of the original operator.\n", 233 | "\n", 234 | "![Original operator in the natural ordering](figures/sparse-natural.png)\n", 235 | "\n", 236 | "A sparse direct solve of this system will result in fill up to the bandwidth.\n", 237 | "\n", 238 | "![Fill in natural ordering](figures/factor-natural.png)\n", 239 | "\n", 240 | "These plots can be produced in PETSc using `-mat_view draw` and `-pc_type lu -pc_factor_mat_ordering_type natural -mat_factor_view draw` (e.g., with `-draw_pause 2` to wait 2 seconds for the viewer).\n", 241 | "\n", 242 | "The Reverse Cuthill-McKee (`rcm`) ordering applies a breadth-first search to produce a low-bandwidth ordering.\n", 243 | "\n", 244 | "![Fill in RCM ordering](figures/factor-rcm.png)\n", 245 | "\n", 246 | "The nested dissection (`nd`) ordering recursively bisects the domain.\n", 247 | "\n", 248 | "![Fill in ND ordering](figures/factor-nd.png)\n", 249 | "\n", 250 | "The asymptotic costs are different for these approaches." 251 | ] 252 | }, 253 | { 254 | "cell_type": "markdown", 255 | "metadata": { 256 | "collapsed": true 257 | }, 258 | "source": [ 259 | "## Convergence of stationary iterative methods\n", 260 | "\n", 261 | "### Richardson iteration\n", 262 | "The simplest iterative method is [Richardson's method](https://en.wikipedia.org/wiki/Modified_Richardson_iteration), which solves $A x = b$ by the iteration\n", 263 | "$$ x_{k+1} = x_k + \\omega (b - A x_k) $$\n", 264 | "where $\\omega > 0$ is a damping parameter and $x_0$ is an initial guess (possibly the zero vector). If $b = A x_*$, this iteration is equivalent to\n", 265 | "$$ x_{k+1} - x_* = (x_k - x_*) - \\omega A (x_k - x_*) = (I - \\omega A) (x_k - x_*) .$$\n", 266 | "It is convenient for convergence analysis to identify the \"error\" $e_k = x_k - x_*$, in which this becomes\n", 267 | "$$ e_{k+1} = (I - \\omega A) e_k $$\n", 268 | "or\n", 269 | "$$ e_k = (I - \\omega A)^k e_0 $$\n", 270 | "in terms of the initial error. Evidently powers of the *iteration matrix* $I - \\omega A$ tell the whole story.\n", 271 | "Suppose that the eigendecomposition\n", 272 | "$$ X \\Lambda X^{-1} = I - \\omega A $$\n", 273 | "exists. Then\n", 274 | "$$ (I - \\omega A)^k = (X \\Lambda X^{-1})^k = X \\Lambda^k X^{-1} $$\n", 275 | "and the convergence (or divergence) rate depends only on the largest magnitude eigenvalue.\n", 276 | "This analysis isn't great for two reasons:\n", 277 | "\n", 278 | "1. Not all matrices are diagonalizable.\n", 279 | "2. The matrix $X$ may be very ill-conditioned.\n", 280 | "\n", 281 | "We can repair these weaknesses by using the [Schur decomposition](https://en.wikipedia.org/wiki/Schur_decomposition)\n", 282 | "$$ Q R Q^h = I - \\omega A $$\n", 283 | "where $R$ is right-triangular and $Q$ is unitary (i.e., orthogonal if real-valued; $Q^h$ is the Hermitian conjugate of $Q$).\n", 284 | "The Schur decomposition always exists and $Q$ has a condition number of 1.\n", 285 | "\n", 286 | "* Where are the eigenvalues in $R$?\n", 287 | "\n", 288 | "Evidently we must find $\\omega$ to minimize the maximum eigenvalue of $I - \\omega A$. We can do this if $A$ is well conditioned, but not in general.\n", 289 | "\n", 290 | "### Preconditioning\n", 291 | "\n", 292 | "Preconditioning is the act of creating an \"affordable\" operation \"$P^{-1}$\" such that $P^{-1} A$ (or $A P^{-1}$) is is well-conditoned or otherwise has a \"nice\" spectrum. We then solve the system\n", 293 | "\n", 294 | "$$ P^{-1} A x = P^{-1} b \\quad \\text{or}\\quad A P^{-1} \\underbrace{(P x)}_y = b $$\n", 295 | "\n", 296 | "in which case the convergence rate depends on the spectrum of the iteration matrix\n", 297 | "$$ I - \\omega P^{-1} A . $$\n", 298 | "\n", 299 | "* The preconditioner must be applied on each iteration.\n", 300 | "* It is *not* merely about finding a good initial guess.\n", 301 | "\n", 302 | "There are two complementary techniques necessary for efficient iterative methods:\n", 303 | "\n", 304 | "* \"accelerators\" or Krylov methods, which use orthogonality to adaptively converge faster than Richardson\n", 305 | "* preconditioners that improve the spectrum of the preconditioned operator\n", 306 | "\n", 307 | "Although there is ongoing research in Krylov methods and they are immensely useful, I would say preconditioning is 90% of the game for practical applications, particularly as a research area." 308 | ] 309 | }, 310 | { 311 | "cell_type": "markdown", 312 | "metadata": { 313 | "collapsed": true 314 | }, 315 | "source": [ 316 | "# Krylov subspaces\n", 317 | "\n", 318 | "All matrix iterations work with approximations in a *Krylov subspace*, which has the form\n", 319 | "\n", 320 | "$$ K_n = \\big[ b \\big| Ab \\big| A^2 b \\big| \\dotsm \\big| A^{n-1} b \\big] . $$\n", 321 | "\n", 322 | "This matrix is horribly ill-conditioned and cannot stably be computed as written. Instead, we seek an orthogonal basis $Q_n$ that spans the same space as $K_n$. We could write this as a factorization\n", 323 | "\n", 324 | "$$ K_n = Q_n R_n $$\n", 325 | "\n", 326 | "where the first column $q_0 = b / \\lVert b \\rVert$. The $R_n$ is unnecessary and hopelessly ill-conditioned, so a slightly different procedure is used.\n" 327 | ] 328 | }, 329 | { 330 | "cell_type": "markdown", 331 | "metadata": { 332 | "collapsed": true 333 | }, 334 | "source": [ 335 | "### Arnoldi iteration\n", 336 | "\n", 337 | "The Arnoldi iteration applies orthogonal similarity transformations to reduce $A$ to [Hessenberg form](https://en.wikipedia.org/wiki/Hessenberg_matrix), starting from a vector $q_0 = b$,\n", 338 | "\n", 339 | "$$ A = Q H Q^h . $$\n", 340 | "\n", 341 | "Let's multiply on the right by $Q$ and examine the first $n$ columns,\n", 342 | "\n", 343 | "$$ A Q_n = Q_{n+1} H_n $$\n", 344 | "where $H_n$ is an $(n+1) \\times n$ Hessenberg matrix.\n", 345 | "\n", 346 | "#### Conditioning\n", 347 | "This representation is well-conditioned because $Q$ is orthogonal and\n", 348 | "\n", 349 | "$$ \\lVert H_n \\rVert \\le \\lVert Q_{n+1}^h \\rVert \\lVert A \\rVert \\lVert Q_n \\rVert \\le \\lVert A \\rVert $$.\n", 350 | "\n", 351 | "For a lower bound, we have\n", 352 | "\n", 353 | "$$ \\sigma_{\\min}(A)^2 \\le x^h A^h A x $$\n", 354 | "\n", 355 | "for all $x$ of norm 1. It must also be true for any $x = Q_n y$ where $\\lVert y\\rVert = 1$, thus\n", 356 | "\n", 357 | "$$ \\sigma_{\\min}(A)^2 \\le y^h Q_n^h A^h A Q_n y = y^h H_n^h Q_{n+1}^h Q_{n+1} H_n y = y^h H_n^h H_n y . $$\n", 358 | "\n", 359 | "#### GMRES\n", 360 | "\n", 361 | "GMRES (Generalized Minimum Residual) minimizes\n", 362 | "$$ \\lVert A x - b \\rVert $$\n", 363 | "over the subspace $Q_n$. I.e., $x = Q_n y$ for some $y$. By the recurrence above, this is equivalent to\n", 364 | "$$ \\lVert Q_{n+1} H_n y - b \\lVert $$\n", 365 | "which can be solved by minimizing\n", 366 | "$$ \\lVert H_n y - Q_{n+1}^h b \\rVert . $$\n", 367 | "Since $q_0 = b/\\lVert b \\lVert$, the least squares problem is to minimize\n", 368 | "$$ \\Big\\lVert H_n y - \\lVert b \\rVert e_0 \\Big\\rVert . $$\n", 369 | "The solution of this least squares problem is achieved by incrementally updating a $QR$ factorization of $H_n$.\n", 370 | "\n", 371 | "**Notes**\n", 372 | "\n", 373 | "* GMRES minimizes the 2-norm of the residual $\\lVert r_n \\rVert$ which is equivalent to the $A^T A$ norm of the error $\\lVert x_n - x_* \\rVert_{A^T A}$.\n", 374 | "* The solution $x_n$ constructed by GMRES at iteration $n$ is not explicitly available. If a solution is needed, it must be constructed by solving the $(n+1)\\times n$ least squares problem and forming the solution as a linear combination of the $n$ vectors $Q_n$. The leading cost is $2mn$ where $m \\gg n$.\n", 375 | "* The residual vector $r_n = A x_n - b$ is not explicitly available in GMRES. To compute it, first build the solution $x_n$ as above.\n", 376 | "* GMRES needs to store the full $Q_n$, which is unaffordable for large $n$ (many iterations). The standard solution is to choose a \"restart\" $k$ and to discard $Q_n$ and start over with an initial guess $x_k$ after each $k$ iterations. This algorithm is called GMRES(k). PETSc's default solver is GMRES(30) and the restart can be controlled using the run-time option `-ksp_gmres_restart`.\n", 377 | "* Most implementations of GMRES use classical Gram-Schmidt because it is much faster in parallel (one reduction per iteration instead of $n$ reductions per iteration). The PETSc option `-ksp_gmres_modifiedgramschmidt` can be used when you suspect that classical Gram-Schmidt may be causing instability.\n", 378 | "* There is a very similar (and older) algorithm called GCR that maintains $x_n$ and $r_n$. This is useful, for example, if a convergence tolerance needs to inspect individual entries. GCR requires $2n$ vectors instead of $n$ vectors, and can tolerate a nonlinear preconditioner. FGMRES is a newer algorithm with similar properties to GCR.\n", 379 | "\n", 380 | "\n", 381 | "### Lanczos iteration: the symmetric case\n", 382 | "\n", 383 | "If $A$ is symmetric, then $H = Q^T A Q$ is also symmetric. Since $H$ is Hessenberg, this means $H$ is tridiagonal. Instead of storing $Q_n$, it is sufficient to store only the last two columns since the iteration satisfies a 3-term recurrence. The analog of GMRES for the symmetric case is called MINRES and is also useful for solving symmetric indefinite problems.\n", 384 | "\n", 385 | "#### Conjugate Gradients\n", 386 | "\n", 387 | "Instead of minimizing the $A^T A$ norm of the error, the Conjugate Gradient method minimizes the $A$ norm of the error. For $A$ to induce a norm, it must be symmetric positive definite. [Jeremy Shewchuck's guide to CG](http://www.cs.cmu.edu/%7Equake-papers/painless-conjugate-gradient.pdf) is an excellent resource." 388 | ] 389 | }, 390 | { 391 | "cell_type": "markdown", 392 | "metadata": {}, 393 | "source": [ 394 | "# Preconditioning\n", 395 | "\n", 396 | "## Classical methods\n", 397 | "\n", 398 | "We have discussed the Jacobi preconditioner\n", 399 | "$$ P_{\\text{Jacobi}}^{-1} = D^{-1} $$\n", 400 | "where $D$ is the diagonal of $A$.\n", 401 | "Gauss-Seidel is\n", 402 | "$$ P_{GS}^{-1} = (L+D)^{-1} $$\n", 403 | "where $L$ is the (strictly) lower triangular part of $A$. The upper triangular part may be used instead, or a symmetric form\n", 404 | "$$ P_{SGS}^{-1} = (L+U)^{-1} A \\Big( I - (L+D)^{-1} \\Big) . $$\n", 405 | "\n", 406 | "## Domain decomposition\n", 407 | "\n", 408 | "Given a linear operator $A : V \\to V$, suppose we have a collection of prolongation operators $P_i : V_i \\to V$. The columns of $P_i$ are \"basis functions\" for the subspace $V_i$. The Galerkin operator $A_i = P_i^T A P_i$ is the action of the original operator $A$ in the subspace.\n", 409 | "\n", 410 | "Define the subspace projection\n", 411 | "\n", 412 | "$$ S_i = P_i A_i^{-1} P_i^T A . $$\n", 413 | "\n", 414 | "* $S_i$ is a projection: $S_i^2 = S_i$\n", 415 | "* If $A$ is SPD, $S_i$ is SPD with respect to the $A$ inner product $x^T A y$\n", 416 | "* $I - S_i$ is $A$-orthogonal to the range of $P_i$\n", 417 | "\n", 418 | "These projections may be applied additively\n", 419 | "\n", 420 | "$$ I - \\sum_{i=0}^n S_i, $$\n", 421 | "\n", 422 | "multiplicatively\n", 423 | "\n", 424 | "$$ \\prod_{i=0}^n (I - S_i), $$\n", 425 | "\n", 426 | "or in some hybrid manner, such as\n", 427 | "\n", 428 | "$$ (I - S_0) (I - \\sum_{i=1}^n S_i) . $$\n", 429 | "In each case above, the action is expressed in terms of the error iteration operator.\n", 430 | "\n", 431 | "### Examples\n", 432 | "\n", 433 | "* Jacobi corresponds to the additive preconditioner with $P_i$ as the $i$th column of the identity\n", 434 | "* Gauss-Seidel is the multiplicate preconditioner with $P_i$ as the $i$th column of the identity\n", 435 | "* Block Jacobi corresponds to labeling \"subdomains\" and $P_i$ as the columns of the identity corresponding to non-overlapping subdomains\n", 436 | "* Overlapping Schwarz corresponds to overlapping subdomains\n", 437 | "* $P_i$ are eigenvectors of $A$\n", 438 | "* A domain is partitioned into interior $V_I$ and interface $V_\\Gamma$ degrees of freedom. $P_I$ is embedding of the interior degrees of freedom while $P_\\Gamma$ is \"harmonic extension\" of the interface degrees of freedom. Consider the multiplicative combination $(I - S_\\Gamma)(I - S_I)$.\n", 439 | "\n", 440 | "### Convergence theory\n", 441 | "\n", 442 | "The formal convergence is beyond the scope of this course, but the following estimates are useful. We let $h$ be the element diameter, $H$ be the subdomain diameter, and $\\delta$ be the overlap, each normalized such that the global domain diameter is 1. We express the convergence in terms of the condition number $\\kappa$ for the preconditioned operator.\n", 443 | "\n", 444 | "* (Block) Jacobi: $\\delta=0$, $\\kappa \\sim H^{-2} H/h = (Hh)^{-1}$\n", 445 | "* Overlapping Schwarz: $\\kappa \\sim H^{-2} H/\\delta = (H \\delta)^{-1}$\n", 446 | "* 2-level overlapping Schwarz: $\\kappa \\sim H/\\delta$\n", 447 | "\n", 448 | "### Hands-on with PETSc: demonstrate these estimates\n", 449 | "\n", 450 | "* Symmetric example: `src/snes/examples/tutorials/ex5.c`\n", 451 | "* Nonsymmetric example: `src/snes/examples/tutorials/ex19.c`\n", 452 | "* Compare preconditioned versus unpreconditioned norms.\n", 453 | "* Compare BiCG versus GMRES\n", 454 | "* Compare domain decomposition and multigrid preconditioning\n", 455 | " * `-pc_type asm` (Additive Schwarz)\n", 456 | " * `-pc_asm_type basic` (symmetric, versus `restrict`)\n", 457 | " * `-pc_asm_overlap 2` (increase overlap)\n", 458 | " * Effect of direct subdomain solver: `-sub_pc_type lu`\n", 459 | " * `-pc_type mg` (Geometric Multigrid)\n", 460 | "* Use monitors:\n", 461 | " * `-ksp_monitor_true_residual`\n", 462 | " * `-ksp_monitor_singular_value`\n", 463 | " * `-ksp_converged_reason`\n", 464 | "* Explain methods: `-snes_view`\n", 465 | "* Performance info: `-log_view`\n", 466 | "\n", 467 | "#### Examples\n", 468 | "```\n", 469 | "mpiexec -n 4 ./ex19 -lidvelocity 2 -snes_monitor -da_refine 5 -ksp_monitor -pc_type asm -sub_pc_type lu\n", 470 | "```" 471 | ] 472 | }, 473 | { 474 | "cell_type": "code", 475 | "execution_count": null, 476 | "metadata": {}, 477 | "outputs": [], 478 | "source": [] 479 | } 480 | ], 481 | "metadata": { 482 | "kernelspec": { 483 | "display_name": "Python 3", 484 | "language": "python", 485 | "name": "python3" 486 | }, 487 | "language_info": { 488 | "codemirror_mode": { 489 | "name": "ipython", 490 | "version": 3 491 | }, 492 | "file_extension": ".py", 493 | "mimetype": "text/x-python", 494 | "name": "python", 495 | "nbconvert_exporter": "python", 496 | "pygments_lexer": "ipython3", 497 | "version": "3.7.1" 498 | } 499 | }, 500 | "nbformat": 4, 501 | "nbformat_minor": 2 502 | } 503 | -------------------------------------------------------------------------------- /Jed-HW2.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# Homework 2\n", 8 | "\n", 9 | "Use a Chebyshev method to solve the second order ordinary differential equation\n", 10 | "\n", 11 | "$$ u''(t) + a u'(t) + b u(t) = f(t) $$\n", 12 | "\n", 13 | "from $t=0$ to $t=1$ with initial conditions $u(0) = 1$ and $u'(0) = 0$.\n", 14 | "\n", 15 | "1. Do a grid convergence study to test the accuracy of your method.\n", 16 | "* Setting $f(t)=0$, experiment with the values $a$ and $b$ to identify two regimes with qualitatively different dynamics.\n", 17 | "\n", 18 | "## Jed's suggestions\n", 19 | "\n", 20 | "### Differentiating shifted/scaled Chebyshev polynomials\n", 21 | "\n", 22 | "Let $x \\in [-1,1]$ denote the reference interval. Suppose we wish to construct a well-conditioned Chebyshev basis on some interval $t(x)$. In this example,\n", 23 | "\n", 24 | "$$ t(x) = (x + 1) / 2 $$\n", 25 | "\n", 26 | "so that $t \\in [0,1]$. This transformation is invertible, yielding\n", 27 | "\n", 28 | "$$ x(t) = 2 t - 1 .$$\n", 29 | "\n", 30 | "Now we can transform any function $f(x)$ to $f(x(t))$ and compute its derivatives via the chain rule\n", 31 | "\n", 32 | "$$ \\frac{d f}{d t} = \\frac{df}{dx} \\frac{dx}{dt} .$$\n", 33 | "\n", 34 | "In our example $dx/dt = 2$. This process can be applied repeatedly for higher derivatives.\n", 35 | "\n", 36 | "### Applying two initial conditions\n", 37 | "\n", 38 | "The problem statement includes two initial conditions and no final condition. We would like the interior equations to be satisfied everywhere but the endpoint, thus it is most natural to implement those initial conditions by overwriting the first and last rows of the matrix." 39 | ] 40 | }, 41 | { 42 | "cell_type": "code", 43 | "execution_count": 1, 44 | "metadata": {}, 45 | "outputs": [], 46 | "source": [ 47 | "%matplotlib inline\n", 48 | "import numpy\n", 49 | "from matplotlib import pyplot\n", 50 | "pyplot.style.use('ggplot')" 51 | ] 52 | }, 53 | { 54 | "cell_type": "code", 55 | "execution_count": 2, 56 | "metadata": { 57 | "scrolled": true 58 | }, 59 | "outputs": [ 60 | { 61 | "name": "stdout", 62 | "output_type": "stream", 63 | "text": [ 64 | "cond(A) = 1.042175e+05\n" 65 | ] 66 | }, 67 | { 68 | "data": { 69 | "image/png": "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\n", 70 | "text/plain": [ 71 | "
" 72 | ] 73 | }, 74 | "metadata": { 75 | "needs_background": "light" 76 | }, 77 | "output_type": "display_data" 78 | } 79 | ], 80 | "source": [ 81 | "# We have utility functions like cosspace and chebeval defined\n", 82 | "# in the file fdtools.py. We would like to import it here, but\n", 83 | "# perhaps we would like to be able to edit the file and yet keep\n", 84 | "# using it from inside the notebook. If we just import fdtools,\n", 85 | "# then the file won't be reloaded unless we restarted the Jupyter\n", 86 | "# kernel. The %run command is much like\n", 87 | "#\n", 88 | "# from fdtools import *\n", 89 | "#\n", 90 | "# but actually re-runs the file each time.\n", 91 | "%run fdtools.py\n", 92 | "\n", 93 | "def discretize(n, a, b, frhs=lambda t:0*t, u0=1, du0=0):\n", 94 | " \"\"\"\n", 95 | " Discretize the initial value problem\n", 96 | " u'' + a*u' + b*u = frhs(t)\n", 97 | " u(0) = u0, u'(u) = du0\n", 98 | " using a Chebyshev collocation method with n points.\n", 99 | " \"\"\"\n", 100 | " t = cosspace(0, 1, n)\n", 101 | " T = chebeval(2*t - 1)\n", 102 | " dxdt = 2\n", 103 | " T[1] *= dxdt\n", 104 | " T[2] *= dxdt**2\n", 105 | " A = T[2] + a*T[1] + b*T[0]\n", 106 | " A[0] = T[0][0]\n", 107 | " A[-1] = T[1][0]\n", 108 | " rhs = frhs(t)\n", 109 | " rhs[0] = u0\n", 110 | " rhs[-1] = du0\n", 111 | " A = A @ numpy.linalg.inv(T[0])\n", 112 | " return t, A, rhs\n", 113 | "\n", 114 | "t, A, rhs = discretize(20, 0, (4*numpy.pi)**2)\n", 115 | "print('cond(A) = {:e}'.format(numpy.linalg.cond(A)))\n", 116 | "u = numpy.linalg.solve(A, rhs)\n", 117 | "\n", 118 | "pyplot.plot(t, u, 'o')\n", 119 | "tt = numpy.linspace(0, 1, 200)\n", 120 | "pyplot.plot(tt, numpy.cos(4*numpy.pi*tt));" 121 | ] 122 | }, 123 | { 124 | "cell_type": "markdown", 125 | "metadata": {}, 126 | "source": [ 127 | "### Analytic solution\n", 128 | "It turns out that $\\cos(n\\pi t)$ is an eigenfunction of the second derivative for every integer $n$. We choose\n", 129 | "\\begin{gather}\n", 130 | " u(t) = \\cos 8 \\pi t \\\\\n", 131 | " u''(t) = -(8\\pi)^2 u(t)\n", 132 | "\\end{gather}\n", 133 | "so that\n", 134 | "$$ u''(t) + \\underbrace{(8 \\pi)^2}_{b} u(t) = 0 $$\n", 135 | "solves the differential equation and satisfies the requested initial conditions.\n", 136 | "We could use this to test the solution, though it does not exercise $a \\ne 0$ or $f(t) \\ne 0$." 137 | ] 138 | }, 139 | { 140 | "cell_type": "code", 141 | "execution_count": 3, 142 | "metadata": { 143 | "scrolled": true 144 | }, 145 | "outputs": [ 146 | { 147 | "data": { 148 | "image/png": "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\n", 149 | "text/plain": [ 150 | "
" 151 | ] 152 | }, 153 | "metadata": { 154 | "needs_background": "light" 155 | }, 156 | "output_type": "display_data" 157 | } 158 | ], 159 | "source": [ 160 | "def analytic_error(n):\n", 161 | " \"\"\"This is lazy by not testing 'a' or 'frhs' nonzero.\"\"\"\n", 162 | " t, A, rhs = discretize(n, 0, (8*numpy.pi)**2)\n", 163 | " u = numpy.linalg.solve(A, rhs)\n", 164 | " return numpy.linalg.norm(u - numpy.cos(8*numpy.pi*t), numpy.inf)\n", 165 | "\n", 166 | "ns = numpy.geomspace(3, 100)\n", 167 | "errors = [analytic_error(n) for n in ns]\n", 168 | "pyplot.semilogy(ns, errors, '.');" 169 | ] 170 | }, 171 | { 172 | "cell_type": "markdown", 173 | "metadata": {}, 174 | "source": [ 175 | "The gradual growth in the error after discretization error drops below rounding error (around $n=40$ in this example) is due to growth in the condition number of differentiation on increasingly fine discretizations.\n", 176 | "\n", 177 | "### Manufactured solution\n", 178 | "\n", 179 | "As a more discerning test, we can manufacture a solution." 180 | ] 181 | }, 182 | { 183 | "cell_type": "code", 184 | "execution_count": 4, 185 | "metadata": {}, 186 | "outputs": [ 187 | { 188 | "data": { 189 | "image/png": "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\n", 190 | "text/plain": [ 191 | "
" 192 | ] 193 | }, 194 | "metadata": { 195 | "needs_background": "light" 196 | }, 197 | "output_type": "display_data" 198 | } 199 | ], 200 | "source": [ 201 | "class exact_tanh:\n", 202 | " def __init__(self, k=1, x0=0):\n", 203 | " self.k = k\n", 204 | " self.x0 = x0\n", 205 | " def u(self, x):\n", 206 | " return numpy.tanh(self.k*(x - self.x0))\n", 207 | " def du(self, x):\n", 208 | " return self.k * numpy.cosh(self.k*(x - self.x0))**(-2)\n", 209 | " def ddu(self, x):\n", 210 | " return -2 * self.k**2 * numpy.tanh(self.k*(x - self.x0)) * numpy.cosh(self.k*(x - self.x0))**(-2)\n", 211 | " def rhs(self, a, b):\n", 212 | " def f(t):\n", 213 | " return self.ddu(t) + a*self.du(t) + b*self.u(t)\n", 214 | " return f\n", 215 | "\n", 216 | "ref = exact_tanh(k=3, x0=.4) \n", 217 | "\n", 218 | "def mms_error(n):\n", 219 | " a = 1.2\n", 220 | " b = 3.4\n", 221 | " t, A, rhs = discretize(n, a, b, ref.rhs(a, b), ref.u(0), ref.du(0))\n", 222 | " u = numpy.linalg.solve(A, rhs)\n", 223 | " return numpy.linalg.norm(u - ref.u(t), numpy.inf)\n", 224 | "\n", 225 | "ns = numpy.geomspace(3, 100)\n", 226 | "errors = [mms_error(n) for n in ns]\n", 227 | "pyplot.semilogy(ns, errors, '.');" 228 | ] 229 | }, 230 | { 231 | "cell_type": "markdown", 232 | "metadata": {}, 233 | "source": [ 234 | "## Dynamics\n", 235 | "\n", 236 | "The differential equation\n", 237 | "\n", 238 | "$$ u'' + a u' + b u = 0 $$\n", 239 | "\n", 240 | "can be analyzed by inspecting the roots of the characteristic equation\n", 241 | "\n", 242 | "$$ x^2 + a x + b = 0. $$\n", 243 | "\n", 244 | "When the discriminant\n", 245 | "$$ a^2 - 4 b $$\n", 246 | "is positive, we get all-real roots, thus non-oscillatory decay. When the discriminant is negative, we get complex roots, thus (damped when $a\\ne 0$) oscillations." 247 | ] 248 | }, 249 | { 250 | "cell_type": "code", 251 | "execution_count": 5, 252 | "metadata": {}, 253 | "outputs": [ 254 | { 255 | "data": { 256 | "image/png": "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\n", 257 | "text/plain": [ 258 | "
" 259 | ] 260 | }, 261 | "metadata": { 262 | "needs_background": "light" 263 | }, 264 | "output_type": "display_data" 265 | } 266 | ], 267 | "source": [ 268 | "def plot_solution(a, b):\n", 269 | " t, A, rhs = discretize(40, a, b)\n", 270 | " pyplot.plot(t, numpy.linalg.solve(A, rhs), label='$({:g},{:g})$'.format(a, b))\n", 271 | " \n", 272 | "plot_solution(5, 100) # discriminant < 0\n", 273 | "plot_solution(20, 100) # discriminant = 0\n", 274 | "plot_solution(50, 100) # discriminant > 0\n", 275 | "pyplot.legend();" 276 | ] 277 | } 278 | ], 279 | "metadata": { 280 | "kernelspec": { 281 | "display_name": "Python 3", 282 | "language": "python", 283 | "name": "python3" 284 | }, 285 | "language_info": { 286 | "codemirror_mode": { 287 | "name": "ipython", 288 | "version": 3 289 | }, 290 | "file_extension": ".py", 291 | "mimetype": "text/x-python", 292 | "name": "python", 293 | "nbconvert_exporter": "python", 294 | "pygments_lexer": "ipython3", 295 | "version": "3.7.0" 296 | } 297 | }, 298 | "nbformat": 4, 299 | "nbformat_minor": 2 300 | } 301 | -------------------------------------------------------------------------------- /Jed-HW1.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# Homework 1\n", 8 | "\n", 9 | "1. Fork the class repository, clone, and create a directory `hw1` inside the repository. Add your source file(s) to that directory.\n", 10 | "2. Write a function `diffmat(x)` that returns a square matrix $D$ that computes first derivatives at all points.\n", 11 | "3. Write a function `diff2mat(x)` that returns a square matrix $D_2$ that computes second derivatives at all points.\n", 12 | "4. Use test solutions to determine the order of accuracy of your methods for evenly and non-evenly spaced points. Which norm did you use?\n", 13 | "5. Add `README.md` in the `hw1` directory and summarize your results (one paragraph or a few bullet items is fine).\n", 14 | "6. Commit (`git commit`) your source code and `README.md` in the `hw1` directory and push to your fork. I'll pull from there after the due date.\n", 15 | "\n", 16 | "\n", 17 | "* You may assume that the points `x` are monotonically increasing.\n", 18 | "* You'll need to think about what to do at the endpoints.\n", 19 | "\n", 20 | "## Jed's suggestions\n", 21 | "\n", 22 | "We would like a second order accurate discretization of the derivative $u'(0)$ using $u(0), u(x), and u(y)$. We could use a Taylor expansion\n", 23 | "\\begin{gather}\n", 24 | "u(x) = u(0) + u'(0) x + u''(0) x^2/2 + O(x^3) \\\\\n", 25 | "u(y) = u(0) + u'(0) y + u''(0) y^2/2 + O(y^3)\n", 26 | "\\end{gather}\n", 27 | "and ask for coefficients $\\{ s_0, s_1, s_2 \\}$ such that\n", 28 | "$$ s_0 u(0) + s_1 u(x) + s_2 u_(y) = u'(0) + O(x^3) + O(y^3) . $$\n", 29 | "This turns out to be equivalent to polynomial interpolation followed by differentiation. It is implemented in the `fdstencil` function in `fdtools.py`." 30 | ] 31 | }, 32 | { 33 | "cell_type": "code", 34 | "execution_count": 1, 35 | "metadata": {}, 36 | "outputs": [], 37 | "source": [ 38 | "%matplotlib inline\n", 39 | "import numpy\n", 40 | "from matplotlib import pyplot\n", 41 | "pyplot.style.use('ggplot')" 42 | ] 43 | }, 44 | { 45 | "cell_type": "code", 46 | "execution_count": 2, 47 | "metadata": {}, 48 | "outputs": [ 49 | { 50 | "data": { 51 | "image/png": "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\n", 52 | "text/plain": [ 53 | "
" 54 | ] 55 | }, 56 | "metadata": { 57 | "needs_background": "light" 58 | }, 59 | "output_type": "display_data" 60 | } 61 | ], 62 | "source": [ 63 | "# We have utility functions like cosspace and chebeval defined\n", 64 | "# in the file fdtools.py. We would like to import it here, but\n", 65 | "# perhaps we would like to be able to edit the file and yet keep\n", 66 | "# using it from inside the notebook. If we just import fdtools,\n", 67 | "# then the file won't be reloaded unless we restarted the Jupyter\n", 68 | "# kernel. The %run command is much like\n", 69 | "#\n", 70 | "# from fdtools import *\n", 71 | "#\n", 72 | "# but actually re-runs the file each time.\n", 73 | "%run fdtools.py\n", 74 | "\n", 75 | "def diffmat(x):\n", 76 | " m = len(x)\n", 77 | " D = numpy.zeros((m, m))\n", 78 | " D[0,:3] = fdstencil(x[0], x[:3])[1]\n", 79 | " for i in range(1, m-1):\n", 80 | " D[i, i-1:i+2] = fdstencil(x[i], x[i-1:i+2])[1]\n", 81 | " D[-1,-3:] = fdstencil(x[-1], x[-3:])[1]\n", 82 | " return D\n", 83 | "\n", 84 | "class exact_tanh:\n", 85 | " def __init__(self, k=1, x0=0):\n", 86 | " self.k = k\n", 87 | " self.x0 = x0\n", 88 | " def u(self, x):\n", 89 | " return numpy.tanh(self.k*(x - self.x0))\n", 90 | " def du(self, x):\n", 91 | " return self.k * numpy.cosh(self.k*(x - self.x0))**(-2)\n", 92 | " def ddu(self, x):\n", 93 | " return -2 * self.k**2 * numpy.tanh(self.k*(x - self.x0)) * numpy.cosh(self.k*(x - self.x0))**(-2)\n", 94 | " \n", 95 | "ref = exact_tanh(3, 0.2)\n", 96 | "x = numpy.linspace(-1, 1, 30)\n", 97 | "xx = numpy.linspace(-1, 1, 100)\n", 98 | "Dx = diffmat(x)\n", 99 | "pyplot.plot(x, Dx @ ref.u(x), 'o')\n", 100 | "pyplot.plot(xx, ref.du(xx));" 101 | ] 102 | }, 103 | { 104 | "cell_type": "code", 105 | "execution_count": 3, 106 | "metadata": {}, 107 | "outputs": [ 108 | { 109 | "data": { 110 | "image/png": "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\n", 111 | "text/plain": [ 112 | "
" 113 | ] 114 | }, 115 | "metadata": { 116 | "needs_background": "light" 117 | }, 118 | "output_type": "display_data" 119 | } 120 | ], 121 | "source": [ 122 | "def perturb(x):\n", 123 | " \"\"\"Randomly perturb the points x while maintaining monotonicity.\"\"\"\n", 124 | " h = numpy.diff(x) # distance between each successive grid point\n", 125 | " minh = numpy.minimum(h[:-1], h[1:]) # distance to closest of left and right neighbor\n", 126 | " y = x.copy()\n", 127 | " y[1:-1] += minh * (numpy.random.rand(len(minh)) - 0.5)\n", 128 | " return y\n", 129 | "\n", 130 | "y = perturb(x)\n", 131 | "pyplot.plot(x, y, 'o');" 132 | ] 133 | }, 134 | { 135 | "cell_type": "code", 136 | "execution_count": 4, 137 | "metadata": { 138 | "scrolled": true 139 | }, 140 | "outputs": [ 141 | { 142 | "data": { 143 | "image/png": "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\n", 144 | "text/plain": [ 145 | "
" 146 | ] 147 | }, 148 | "metadata": { 149 | "needs_background": "light" 150 | }, 151 | "output_type": "display_data" 152 | } 153 | ], 154 | "source": [ 155 | "Dy = diffmat(y)\n", 156 | "pyplot.plot(y, Dy @ ref.u(y), '^')\n", 157 | "pyplot.plot(y, Dx @ ref.u(y), 'o')\n", 158 | "pyplot.plot(xx, ref.du(xx));" 159 | ] 160 | }, 161 | { 162 | "cell_type": "markdown", 163 | "metadata": {}, 164 | "source": [ 165 | "Note that the uniform derivative operator `Dx` is junk when used for data on an unevenly spaced grid.\n", 166 | "\n", 167 | "The second derivative is defined similarly to `diffmat`." 168 | ] 169 | }, 170 | { 171 | "cell_type": "code", 172 | "execution_count": 5, 173 | "metadata": { 174 | "scrolled": true 175 | }, 176 | "outputs": [ 177 | { 178 | "data": { 179 | "image/png": "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\n", 180 | "text/plain": [ 181 | "
" 182 | ] 183 | }, 184 | "metadata": { 185 | "needs_background": "light" 186 | }, 187 | "output_type": "display_data" 188 | } 189 | ], 190 | "source": [ 191 | "def diff2mat(x):\n", 192 | " m = len(x)\n", 193 | " D2 = numpy.zeros((m, m))\n", 194 | " D2[0,:3] = fdstencil(x[0], x[:3])[2]\n", 195 | " for i in range(1, m-1):\n", 196 | " D2[i, i-1:i+2] = fdstencil(x[i], x[i-1:i+2])[2]\n", 197 | " D2[-1,-3:] = fdstencil(x[-1], x[-3:])[2]\n", 198 | " return D2\n", 199 | "\n", 200 | "D2x = diff2mat(x)\n", 201 | "D2y = diff2mat(y)\n", 202 | "pyplot.plot(y, D2x @ ref.u(x), '^')\n", 203 | "pyplot.plot(y, D2y @ ref.u(y), 'o')\n", 204 | "pyplot.plot(xx, ref.ddu(xx));" 205 | ] 206 | }, 207 | { 208 | "cell_type": "markdown", 209 | "metadata": {}, 210 | "source": [ 211 | "## Convergence study\n", 212 | "\n", 213 | "We will work in the max ($\\infty$) norm." 214 | ] 215 | }, 216 | { 217 | "cell_type": "code", 218 | "execution_count": 6, 219 | "metadata": {}, 220 | "outputs": [ 221 | { 222 | "data": { 223 | "image/png": "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\n", 224 | "text/plain": [ 225 | "
" 226 | ] 227 | }, 228 | "metadata": { 229 | "needs_background": "light" 230 | }, 231 | "output_type": "display_data" 232 | } 233 | ], 234 | "source": [ 235 | "def mms_errors(n):\n", 236 | " x = numpy.linspace(-1, 1, n)\n", 237 | " y = perturb(x)\n", 238 | " Dx = diffmat(x)\n", 239 | " Dy = diffmat(y)\n", 240 | " D2x = diff2mat(x)\n", 241 | " D2y = diff2mat(y)\n", 242 | " return (numpy.linalg.norm(Dx @ ref.u(x) - ref.du(x), numpy.inf),\n", 243 | " numpy.linalg.norm(Dy @ ref.u(y) - ref.du(y), numpy.inf),\n", 244 | " numpy.linalg.norm(D2x @ ref.u(x) - ref.ddu(x), numpy.inf),\n", 245 | " numpy.linalg.norm(D2y @ ref.u(y) - ref.ddu(y), numpy.inf))\n", 246 | "\n", 247 | "ns = numpy.geomspace(5, 200, dtype=int)\n", 248 | "errors = numpy.array([mms_errors(n) for n in ns])\n", 249 | "pyplot.loglog(ns, errors[:,0], 'o', label='D uniform')\n", 250 | "pyplot.loglog(ns, errors[:,1], 's', label='D perturb')\n", 251 | "pyplot.loglog(ns, errors[:,2], '<', label='D2 uniform')\n", 252 | "pyplot.loglog(ns, errors[:,3], '*', label='D2 perturb')\n", 253 | "pyplot.loglog(ns, 10*ns**(-1.), label='$O(n^{-1})$')\n", 254 | "pyplot.loglog(ns, 10*ns**(-2.), label='$O(n^{-2})$')\n", 255 | "pyplot.legend();" 256 | ] 257 | }, 258 | { 259 | "cell_type": "markdown", 260 | "metadata": {}, 261 | "source": [ 262 | "Evidently the first derivative operators are all second order while the second derivatives are first order on uniform grids and noisy (but probably still first order in the limit) on non-uniform grids." 263 | ] 264 | } 265 | ], 266 | "metadata": { 267 | "kernelspec": { 268 | "display_name": "Python 3", 269 | "language": "python", 270 | "name": "python3" 271 | }, 272 | "language_info": { 273 | "codemirror_mode": { 274 | "name": "ipython", 275 | "version": 3 276 | }, 277 | "file_extension": ".py", 278 | "mimetype": "text/x-python", 279 | "name": "python", 280 | "nbconvert_exporter": "python", 281 | "pygments_lexer": "ipython3", 282 | "version": "3.7.0" 283 | } 284 | }, 285 | "nbformat": 4, 286 | "nbformat_minor": 2 287 | } 288 | --------------------------------------------------------------------------------