{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "\n", "\n", "\n", "\n", "\n", "\n", "\n", "Vibration problems lead to differential equations with solutions that\n", "oscillate in time, typically in a damped or undamped sinusoidal\n", "fashion. Such solutions put certain demands on the numerical methods\n", "compared to other phenomena whose solutions are monotone or very smooth.\n", "Both the frequency and amplitude of the oscillations need to be\n", "accurately handled by the numerical schemes. The forthcoming text\n", "presents a range of different methods, from classical ones\n", "(Runge-Kutta and midpoint/Crank-Nicolson methods), to more\n", "modern and popular symplectic (geometric) integration schemes (Leapfrog,\n", "Euler-Cromer, and\n", "Stoermer-Verlet\n", "methods), but with a clear emphasis on the latter. Vibration problems\n", "occur throughout mechanics and physics, but the methods discussed\n", "in this text are also fundamental for constructing successful algorithms\n", "for partial differential equations\n", "of wave nature in multiple spatial dimensions.\n", "\n", "\n", "# Finite difference discretization\n", "
\n", "\n", "Many of the numerical challenges faced when computing oscillatory\n", "solutions to ODEs and PDEs can be captured by the very simple ODE\n", "$u^{\\prime\\prime} + u =0$. This ODE is thus chosen as our starting\n", "point for method development, implementation, and analysis.\n", "\n", "## A basic model for vibrations\n", "\n", "\n", "The simplest model of a vibrating mechanical system has the following form:" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "\n", "
\n", "\n", "$$\n", "\\begin{equation}\n", "u^{\\prime\\prime} + \\omega^2u = 0,\\quad u(0)=I,\\ u^{\\prime}(0)=0,\\ t\\in (0,T\\rbrack\n", "% \\thinspace .\n", "\\label{vib:ode1} \\tag{1}\n", "\\end{equation}\n", "$$" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Here, $\\omega$ and $I$ are given constants.\n", "The section [Applications of vibration models](vib_app.ipynb#vib:app:mass_spring) derives ([1](#vib:ode1)) from physical\n", "principles and explains what the constants mean.\n", "\n", "\n", "The exact solution of ([1](#vib:ode1)) is" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "\n", "
\n", "\n", "$$\n", "\\begin{equation}\n", "u(t) = I\\cos (\\omega t)\n", "\\thinspace .\n", "\\label{vib:ode1:uex} \\tag{2}\n", "\\end{equation}\n", "$$" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "That is, $u$ oscillates with constant amplitude $I$ and\n", "angular frequency $\\omega$.\n", "The corresponding period of oscillations (i.e., the time between two\n", "neighboring peaks in the cosine function) is $P=2\\pi/\\omega$.\n", "The number of periods per second\n", "is $f=\\omega/(2\\pi)$ and measured in the unit Hz.\n", "Both $f$ and $\\omega$ are referred to as frequency, but $\\omega$\n", "is more precisely named *angular frequency*, measured in rad/s.\n", "\n", "In vibrating mechanical systems modeled by ([1](#vib:ode1)), $u(t)$\n", "very often represents a position or a displacement of a particular\n", "point in the system. The derivative $u^{\\prime}(t)$ then has the\n", "interpretation of velocity, and $u^{\\prime\\prime}(t)$ is the associated\n", "acceleration. The model ([1](#vib:ode1)) is not only\n", "applicable to vibrating mechanical systems, but also to oscillations\n", "in electrical circuits.\n", "\n", "## A centered finite difference scheme\n", "
\n", "\n", "To formulate a finite difference method for the model\n", "problem ([1](#vib:ode1)), we follow the four steps explained in Section 1.1.2\n", "in [[Langtangen_decay]](#Langtangen_decay).\n", "\n", "\n", "### Step 1: Discretizing the domain\n", "\n", "The domain is discretized by\n", "introducing a uniformly partitioned time mesh.\n", "The points in the mesh are $t_n=n\\Delta t$, $n=0,1,\\ldots,N_t$,\n", "where $\\Delta t = T/N_t$ is the constant length of the time steps.\n", "We introduce a mesh function $u^n$ for $n=0,1,\\ldots,N_t$, which\n", "approximates the exact solution at the mesh points. (Note that\n", "$n=0$ is the known initial condition, so $u^n$ is identical to the mathematical\n", "$u$ at this point.) The mesh\n", "function $u^n$ will be computed from algebraic equations derived from\n", "the differential equation problem.\n", "\n", "\n", "### Step 2: Fulfilling the equation at discrete time points\n", "\n", "The ODE is to be satisfied at each mesh point where the solution\n", "must be found:" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "\n", "
\n", "\n", "$$\n", "\\begin{equation}\n", "u^{\\prime\\prime}(t_n) + \\omega^2u(t_n) = 0,\\quad n=1,\\ldots,N_t\n", "\\thinspace .\n", "\\label{vib:ode1:step2} \\tag{3}\n", "\\end{equation}\n", "$$" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Step 3: Replacing derivatives by finite differences\n", "\n", "The derivative $u^{\\prime\\prime}(t_n)$ is to be replaced by a finite\n", "difference approximation. A common second-order accurate approximation\n", "to the second-order derivative is" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "\n", "
\n", "\n", "$$\n", "\\begin{equation}\n", "u^{\\prime\\prime}(t_n) \\approx \\frac{u^{n+1}-2u^n + u^{n-1}}{\\Delta t^2}\n", "\\thinspace .\n", "\\label{vib:ode1:step3} \\tag{4}\n", "\\end{equation}\n", "$$" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Inserting ([4](#vib:ode1:step3)) in ([3](#vib:ode1:step2))\n", "yields" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "\n", "
\n", "\n", "$$\n", "\\begin{equation}\n", "\\frac{u^{n+1}-2u^n + u^{n-1}}{\\Delta t^2} = -\\omega^2 u^n\n", "\\thinspace .\n", "\\label{vib:ode1:step3b} \\tag{5}\n", "\\end{equation}\n", "$$" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We also need to replace the derivative in the initial condition by\n", "a finite difference. Here we choose a centered difference, whose\n", "accuracy is similar to the centered difference we used for $u^{\\prime\\prime}$:" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "\n", "
\n", "\n", "$$\n", "\\begin{equation}\n", "\\frac{u^1-u^{-1}}{2\\Delta t} = 0\n", "\\label{vib:ode1:step3c} \\tag{6}\n", "\\thinspace .\n", "\\end{equation}\n", "$$" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Step 4: Formulating a recursive algorithm\n", "\n", "To formulate the computational algorithm, we assume that we\n", "have already computed $u^{n-1}$ and $u^n$, such that $u^{n+1}$ is the\n", "unknown value to be solved for:" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "\n", "
\n", "\n", "$$\n", "\\begin{equation}\n", "u^{n+1} = 2u^n - u^{n-1} - \\Delta t^2\\omega^2 u^n\n", "\\thinspace .\n", "\\label{vib:ode1:step4} \\tag{7}\n", "\\end{equation}\n", "$$" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The computational algorithm is simply to apply ([7](#vib:ode1:step4))\n", "successively for $n=1,2,\\ldots,N_t-1$. This numerical scheme sometimes\n", "goes under the name\n", "Stoermer's\n", "method, [Verlet integration](http://en.wikipedia.org/wiki/Verlet_integration), or the Leapfrog method\n", "(one should note\n", "that Leapfrog is used for many quite different methods for quite\n", "different differential equations!).\n", "\n", "\n", "### Computing the first step\n", "\n", "We observe that ([7](#vib:ode1:step4)) cannot be used for $n=0$ since\n", "the computation of $u^1$ then involves the undefined value $u^{-1}$\n", "at $t=-\\Delta t$. The discretization of the initial condition\n", "then comes to our rescue: ([6](#vib:ode1:step3c)) implies $u^{-1} = u^1$\n", "and this relation can be combined with ([7](#vib:ode1:step4))\n", "for $n=0$ to yield a value for $u^1$:" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "$$\n", "u^1 = 2u^0 - u^{1} - \\Delta t^2 \\omega^2 u^0,\n", "$$" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "which reduces to" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "\n", "
\n", "\n", "$$\n", "\\begin{equation}\n", "u^1 = u^0 - \\frac{1}{2} \\Delta t^2 \\omega^2 u^0\n", "\\thinspace .\n", "\\label{vib:ode1:step4b} \\tag{8}\n", "\\end{equation}\n", "$$" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "[Exercise 5: Use a Taylor polynomial to compute $u^1$](#vib:exer:step4b:alt) asks you to perform an alternative derivation\n", "and also to generalize the initial condition to $u^{\\prime}(0)=V\\neq 0$.\n", "\n", "### The computational algorithm\n", "\n", "The steps for solving ([1](#vib:ode1)) become\n", "\n", "1. $u^0=I$\n", "\n", "2. compute $u^1$ from ([8](#vib:ode1:step4b))\n", "\n", "3. for $n=1,2,\\ldots,N_t-1$: compute $u^{n+1}$ from ([7](#vib:ode1:step4))\n", "\n", "The algorithm is more precisely expressed directly in Python:" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "```python\n", "t = linspace(0, T, Nt+1) # mesh points in time\n", "dt = t[1] - t[0] # constant time step\n", "u = zeros(Nt+1) # solution\n", "\n", "u[0] = I\n", "u[1] = u[0] - 0.5*dt**2*w**2*u[0]\n", "for n in range(1, Nt):\n", " u[n+1] = 2*u[n] - u[n-1] - dt**2*w**2*u[n]\n", "```" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "**Remark on using `w` for $\\omega$ in computer code.**\n", "\n", "In the code, we use `w` as the symbol for $\\omega$.\n", "The reason is that the authors prefer `w` for readability\n", "and comparison with the mathematical $\\omega$ instead of\n", "the full word `omega` as variable name.\n", "\n", "\n", "\n", "\n", "### Operator notation\n", "\n", "We may write the scheme using a compact difference notation\n", "listed in the [Finite difference operator notation](../A_formulas/formulas.ipynb#sec:form:fdop) section\n", "(see also Section 1.1.8 in [[Langtangen_decay]](#Langtangen_decay)).\n", "The difference ([4](#vib:ode1:step3)) has the operator\n", "notation $[D_tD_t u]^n$ such that we can write:" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "\n", "
\n", "\n", "$$\n", "\\begin{equation}\n", "[D_tD_t u + \\omega^2 u = 0]^n\n", "\\thinspace .\n", "\\label{vib:ode1:step4:op} \\tag{9}\n", "\\end{equation}\n", "$$" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Note that $[D_tD_t u]^n$ means applying a central difference with step $\\Delta t/2$ twice:" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "$$\n", "[D_t(D_t u)]^n = \\frac{[D_t u]^{n+\\frac{1}{2}} - [D_t u]^{n-\\frac{1}{2}}}{\\Delta t}\n", "$$" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "which is written out as" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "$$\n", "\\frac{1}{\\Delta t}\\left(\\frac{u^{n+1}-u^n}{\\Delta t} - \\frac{u^{n}-u^{n-1}}{\\Delta t}\\right) = \\frac{u^{n+1}-2u^n + u^{n-1}}{\\Delta t^2}\n", "\\thinspace .\n", "$$" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The discretization of initial conditions can in the operator notation\n", "be expressed as" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "\n", "
\n", "\n", "$$\n", "\\begin{equation}\n", "[u = I]^0,\\quad [D_{2t} u = 0]^0,\n", "\\label{_auto1} \\tag{10}\n", "\\end{equation}\n", "$$" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "where the operator $[D_{2t} u]^n$ is defined as" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "\n", "
\n", "\n", "$$\n", "\\begin{equation}\n", "[D_{2t} u]^n = \\frac{u^{n+1} - u^{n-1}}{2\\Delta t}\n", "\\thinspace .\n", "\\label{_auto2} \\tag{11}\n", "\\end{equation}\n", "$$" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Implementation\n", "
\n", "\n", "## Making a solver function\n", "
\n", "\n", "\n", "The algorithm from the previous section is readily translated to\n", "a complete Python function for computing and returning\n", "$u^0,u^1,\\ldots,u^{N_t}$ and $t_0,t_1,\\ldots,t_{N_t}$, given the\n", "input $I$, $\\omega$, $\\Delta t$, and $T$:" ] }, { "cell_type": "code", "execution_count": 1, "metadata": {}, "outputs": [], "source": [ "# NBVAL_IGNORE_OUTPUT\n", "import numpy as np\n", "import matplotlib.pyplot as plt\n", "from devito import Dimension, Constant, TimeFunction, Eq, solve, Operator" ] }, { "cell_type": "code", "execution_count": 2, "metadata": {}, "outputs": [], "source": [ "# %load -s solver, src-vib/vib_undamped.py\n", "def solver(I, w, dt, T):\n", " \"\"\"\n", " Solve u'' + w**2*u = 0 for t in (0,T], u(0)=I and u'(0)=0,\n", " by a central finite difference method with time step dt.\n", " \"\"\"\n", " dt = float(dt)\n", " Nt = int(round(T/dt))\n", " t = Dimension('t', spacing=Constant('h_t'))\n", "\n", " u = TimeFunction(name='u', dimensions=(t,),\n", " shape=(Nt+1,), space_order=2)\n", "\n", " u.data[:] = I\n", " eqn = u.dt2 + (w**2)*u\n", " stencil = Eq(u.forward, solve(eqn, u.forward))\n", " op = Operator(stencil)\n", " op.apply(h_t=dt, t_M=Nt-1)\n", " return u.data, np.linspace(0, Nt*dt, Nt+1)\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We have imported `numpy` and `matplotlib` under the names `np` and `plt`, as this is very common in the Python scientific computing community and a good programming habit (since we explicitly\n", "see where the different functions come from).\n", "\n", "A function for plotting the numerical and the exact solution is also\n", "convenient to have:" ] }, { "cell_type": "code", "execution_count": 4, "metadata": {}, "outputs": [], "source": [ "# %load -s u_exact, src-vib/vib_undamped.py\n", "def u_exact(t, I, w):\n", " return I*np.cos(w*t)\n" ] }, { "cell_type": "code", "execution_count": 5, "metadata": {}, "outputs": [], "source": [ "# %load -s visualize, src-vib/vib_undamped.py\n", "def visualize(u, t, I, w):\n", " plt.plot(t, u, 'r--o')\n", " t_fine = np.linspace(0, t[-1], 1001) # very fine mesh for u_e\n", " u_e = u_exact(t_fine, I, w)\n", " plt.plot(t_fine, u_e, 'b-')\n", " plt.legend(['numerical', 'exact'], loc='upper left')\n", " plt.xlabel('t')\n", " plt.ylabel('u')\n", " dt = t[1] - t[0]\n", " plt.title('dt=%g' % dt)\n", " umin = 1.2*u.min(); umax = -umin\n", " plt.axis([t[0], t[-1], umin, umax])\n", " plt.savefig('tmp1.png'); plt.savefig('tmp1.pdf')\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "A corresponding main program calling these functions to simulate\n", "a given number of periods (`num_periods`) may take the form" ] }, { "cell_type": "code", "execution_count": 13, "metadata": {}, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "Operator `Kernel` run in 0.01 s\n" ] }, { "data": { "image/png": "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\n", "text/plain": [ "
" ] }, "metadata": { "needs_background": "light" }, "output_type": "display_data" } ], "source": [ "I = 1\n", "w = 2*np.pi\n", "dt = 0.05\n", "num_periods = 5\n", "P = 2*np.pi/w # one period\n", "T = P*num_periods\n", "u, t = solver(I, w, dt, T)\n", "visualize(u, t, I, w)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Adjusting some of the input parameters via the command line can be\n", "handy. Here is a code segment using the `ArgumentParser` tool in\n", "the `argparse` module to define option value (`--option value`)\n", "pairs on the command line:" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "```python\n", "import argparse\n", "parser = argparse.ArgumentParser()\n", "parser.add_argument('--I', type=float, default=1.0)\n", "parser.add_argument('--w', type=float, default=2*np.pi)\n", "parser.add_argument('--dt', type=float, default=0.05)\n", "parser.add_argument('--num_periods', type=int, default=5)\n", "a = parser.parse_args()\n", "I, w, dt, num_periods = a.I, a.w, a.dt, a.num_periods\n", "```" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Such parsing of the command line is explained in more detail in Section 5.2.3 in \n", "[[Langtangen_decay]](#Langtangen_decay).\n", "\n", "A typical execution goes like" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ " Terminal> python vib_undamped.py --num_periods 20 --dt 0.1\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Verification\n", "
\n", "\n", "\n", "### Manual calculation\n", "\n", "The simplest type of verification, which is also instructive for understanding\n", "the algorithm, is to compute $u^1$, $u^2$, and $u^3$\n", "with the aid of a calculator\n", "and make a function for comparing these results with those from the `solver`\n", "function. The `test_three_steps` function in\n", "the file [`vib_undamped.py`](https://github.com/devitocodes/devito_book/blob/master/fdm-devito-notebooks/01_vib/src-vib/vib_undamped.py)\n", "shows the details of how we use the hand calculations to test the code:" ] }, { "cell_type": "code", "execution_count": 14, "metadata": {}, "outputs": [], "source": [ "# %load -s test_three_steps, src-vib/vib_undamped.py\n", "def test_three_steps():\n", " from math import pi\n", " I = 1; w = 2*pi; dt = 0.1; T = 1\n", " u_by_hand = np.array([1.000000000000000,\n", " 0.802607911978213,\n", " 0.288358920740053])\n", " u, t = solver(I, w, dt, T)\n", " diff = np.abs(u_by_hand - u[:3]).max()\n", " tol = 1E-14\n", " assert diff < tol\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "This function is a proper *test function*,\n", "compliant with the pytest and nose testing\n", "framework for Python code, because\n", "\n", " * the function name begins with `test_`\n", "\n", " * the function takes no arguments\n", "\n", " * the test is formulated as a boolean condition and executed by `assert`\n", "\n", "We shall in this book implement all software verification via such\n", "proper test functions, also known as unit testing.\n", "\n", "See Section 5.3.2 in [[Langtangen_decay]](#Langtangen_decay)\n", "for more details on how to construct test functions and utilize nose\n", "or pytest for automatic execution of tests. Our recommendation is to\n", "use pytest. With this choice, you can\n", "run all test functions in `vib_undamped.py` by" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ " Terminal> py.test -s -v vib_undamped.py\n", " ============================= test session starts ======...\n", " platform linux2 -- Python 2.7.9 -- ...\n", " collected 2 items\n", " \n", " vib_undamped.py::test_three_steps PASSED\n", " vib_undamped.py::test_convergence_rates PASSED\n", " \n", " =========================== 2 passed in 0.19 seconds ===...\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Testing very simple polynomial solutions\n", "\n", "Constructing test problems where the exact solution is constant or\n", "linear helps initial debugging and verification as one expects any\n", "reasonable numerical method to reproduce such solutions to machine\n", "precision. Second-order accurate methods will often also reproduce a\n", "quadratic solution. Here $[D_tD_tt^2]^n=2$, which is the exact\n", "result. A solution $u=t^2$ leads to $u^{\\prime\\prime}+\\omega^2 u=2 + (\\omega\n", "t)^2\\neq 0$. We must therefore add a source in the equation: $u^{\\prime\\prime} +\n", "\\omega^2 u = f$ to allow a solution $u=t^2$ for $f=2 + (\\omega t)^2$. By\n", "simple insertion we can show that the mesh function $u^n = t_n^2$ is\n", "also a solution of the discrete equations. [Problem 1: Use linear/quadratic functions for verification](#vib:exer:undamped:verify:linquad) asks you to carry out all\n", "details to show that linear and quadratic solutions are solutions\n", "of the discrete equations. Such results are very useful for debugging\n", "and verification. You are strongly encouraged to do this problem now!\n", "\n", "\n", "### Checking convergence rates\n", "\n", "Empirical computation of convergence rates yields a good method for\n", "verification. The method and its computational details are explained\n", "in detail in Section 3.1.6 in [[Langtangen_decay]](#Langtangen_decay). Readers not\n", "familiar with the concept should look up this reference before\n", "proceeding.\n", "\n", "In the present problem, computing convergence rates means that we must\n", "\n", " * perform $m$ simulations, halving the time steps as: $\\Delta t_i=2^{-i}\\Delta t_0$, $i=1,\\ldots,m-1$, and $\\Delta t_i$ is the time step used in simulation $i$;\n", "\n", " * compute the $L^2$ norm of the error,\n", " $E_i=\\sqrt{\\Delta t_i\\sum_{n=0}^{N_t-1}(u^n-u(t_n))^2}$ in each case;\n", "\n", " * estimate the convergence rates $r_i$ based on two consecutive\n", " experiments $(\\Delta t_{i-1}, E_{i-1})$ and $(\\Delta t_{i}, E_{i})$,\n", " assuming $E_i=C(\\Delta t_i)^{r}$ and $E_{i-1}=C(\\Delta t_{i-1})^{r}$, where $C$ is a constant.\n", " From these equations it follows that\n", " $r = \\ln (E_{i-1}/E_i)/\\ln (\\Delta t_{i-1}/\\Delta t_i)$. Since this $r$\n", " will vary with $i$, we equip it with an index and call it $r_{i-1}$,\n", " where $i$ runs from $1$ to $m-1$.\n", "\n", "The computed rates $r_0,r_1,\\ldots,r_{m-2}$ hopefully converge to the\n", "number 2 in the present\n", "problem, because theory (from the section [Analysis of the numerical scheme](#vib:ode1:analysis)) shows\n", "that the error of the numerical method we use behaves like $\\Delta t^2$.\n", "The convergence of the sequence $r_0,r_1,\\ldots,r_{m-2}$\n", "demands that the time steps\n", "$\\Delta t_i$ are sufficiently small for the error model $E_i=C(\\Delta t_i)^r$\n", "to be valid.\n", "\n", "All the implementational details of computing the sequence\n", "$r_0,r_1,\\ldots,r_{m-2}$ appear below." ] }, { "cell_type": "code", "execution_count": 15, "metadata": {}, "outputs": [], "source": [ "# %load -s convergence_rates, src-vib/vib_undamped.py\n", "def convergence_rates(m, solver_function, num_periods=8):\n", " \"\"\"\n", " Return m-1 empirical estimates of the convergence rate\n", " based on m simulations, where the time step is halved\n", " for each simulation.\n", " solver_function(I, w, dt, T) solves each problem, where T\n", " is based on simulation for num_periods periods.\n", " \"\"\"\n", " from math import pi\n", " w = 0.35; I = 0.3 # just chosen values\n", " P = 2*pi/w # period\n", " dt = P/30 # 30 time step per period 2*pi/w\n", " T = P*num_periods\n", "\n", " dt_values = []\n", " E_values = []\n", " for i in range(m):\n", " u, t = solver_function(I, w, dt, T)\n", " u_e = u_exact(t, I, w)\n", " E = np.sqrt(dt*np.sum((u_e-u)**2))\n", " dt_values.append(dt)\n", " E_values.append(E)\n", " dt = dt/2\n", "\n", " r = [np.log(E_values[i-1]/E_values[i])/\n", " np.log(dt_values[i-1]/dt_values[i])\n", " for i in range(1, m, 1)]\n", " return r, E_values, dt_values\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The error analysis in the section [Analysis of the numerical scheme](#vib:ode1:analysis) is quite\n", "detailed and suggests that $r=2$.\n", "mathcal{I}_t is also a intuitively reasonable result, since we used a\n", "second-order accurate finite difference approximation $[D_tD_tu]^n$ to\n", "the ODE and a second-order accurate finite difference formula for the\n", "initial condition for $u^{\\prime}$.\n", "\n", "In the present problem, when $\\Delta t_0$ corresponds to 30 time steps\n", "per period, the returned `r` list has all its values equal to 2.00\n", "(if rounded to two decimals). This amazingly accurate result means that all\n", "$\\Delta t_i$ values are well into the asymptotic regime where the\n", "error model $E_i = C(\\Delta t_i)^r$ is valid.\n", "\n", "We can now construct a proper test function that computes convergence rates\n", "and checks that the final (and usually the best) estimate is sufficiently\n", "close to 2. Here, a rough tolerance of 0.1 is enough. Later, we will argue\n", "for an improvement by adjusting omega and include also that case in our test\n", "function here. The unit test goes like" ] }, { "cell_type": "code", "execution_count": 16, "metadata": {}, "outputs": [], "source": [ "# %load -s test_convergence_rates, src-vib/vib_undamped.py\n", "def test_convergence_rates():\n", " r, E, dt = convergence_rates(\n", " m=5, solver_function=solver, num_periods=8)\n", " # Accept rate to 1 decimal place\n", " tol = 0.1\n", " assert abs(r[-1] - 2.0) < tol\n", " # Test that adjusted w obtains 4th order convergence\n", " r, E, dt = convergence_rates(\n", " m=5, solver_function=solver_adjust_w, num_periods=8)\n", " print(\"adjust w rates:\")\n", " print(r)\n", " assert abs(r[-1] - 4.0) < tol\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "where `solver_adjust_w` is a slight variation on the original `solver` function, as follows:" ] }, { "cell_type": "code", "execution_count": 17, "metadata": {}, "outputs": [], "source": [ "# %load -s solver_adjust_w, src-vib/vib_undamped.py\n", "def solver_adjust_w(I, w, dt, T, adjust_w=True):\n", " \"\"\"\n", " Solve u'' + w**2*u = 0 for t in (0,T], u(0)=I and u'(0)=0,\n", " by a central finite difference method with time step dt.\n", " \"\"\"\n", " dt = float(dt)\n", " Nt = int(round(T/dt))\n", " t = Dimension('t', spacing=Constant('h_t'))\n", "\n", " u = TimeFunction(name='u', dimensions=(t,),\n", " shape=(Nt+1,), space_order=2)\n", "\n", " w_adj = w*(1 - w**2*dt**2/24.) if adjust_w else w\n", "\n", " u.data[:] = I\n", " eqn = u.dt2 + (w**2)*u\n", " stencil = Eq(u.forward, solve(eqn, u.forward))\n", " op = Operator(stencil)\n", " op.apply(h_t=dt, t_M=Nt-1)\n", " return u.data, np.linspace(0, Nt*dt, Nt+1)\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The complete code appears in the file [`vib_undamped.py`](https://github.com/devitocodes/devito_book/blob/master/fdm-devito-notebooks/01_vib/src-vib/vib_undamped.py).\n", "\n", "\n", "### Visualizing convergence rates with slope markers\n", "\n", "Tony S. Yu has written a script [`plotslopes.py`](http://goo.gl/A4Utm7)\n", "that is very useful to indicate the slope of a graph, especially\n", "a graph like $\\ln E = r\\ln \\Delta t + \\ln C$ arising from the model\n", "$E=C\\Delta t^r$. A copy of the script resides in the [`src-vib`](https://github.com/devitocodes/devito_book/tree/master/fdm-devito-notebooks/01_vib/src-vib)\n", "directory. Let us use it to compare the original method for $u'' + \\omega^2u =0$\n", "with the same method applied to the equation with a modified\n", "$\\omega$. We make log-log plots of the error versus $\\Delta t$.\n", "For each curve we attach a slope marker using the `slope_marker((x,y), r)`\n", "function from `plotslopes.py`, where `(x,y)` is the position of the\n", "marker and `r` and the slope ($(r,1)$), here (2,1) and (4,1)." ] }, { "cell_type": "code", "execution_count": 18, "metadata": {}, "outputs": [], "source": [ "# %load -s plot_convergence_rates, src-vib/vib_undamped.py\n", "def plot_convergence_rates():\n", " r2, E2, dt2 = convergence_rates(\n", " m=5, solver_function=solver, num_periods=8)\n", " plt.loglog(dt2, E2)\n", " r4, E4, dt4 = convergence_rates(\n", " m=5, solver_function=solver_adjust_w, num_periods=8)\n", " plt.loglog(dt4, E4)\n", " plt.legend(['original scheme', r'adjusted $\\omega$'],\n", " loc='upper left')\n", " plt.title('Convergence of finite difference methods')\n", " # from plotslopes import slope_marker\n", " # slope_marker((dt2[1], E2[1]), (2,1))\n", " # slope_marker((dt4[1], E4[1]), (4,1))\n", " plt.savefig('tmp_convrate.png'); plt.savefig('tmp_convrate.pdf')\n", " plt.show()\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "[Figure](#vib:ode1:verify:fig:convrate_curve) displays the two curves\n", "with the markers. The match of the curve slope and the marker slope is\n", "excellent.\n", "\n", "\n", "\n", "
\n", "\n", "

Empirical convergence rate curves with special slope marker.

\n", "\n", "\n", "\n", "\n", "\n", "## Scaled model\n", "\n", "It is advantageous to use dimensionless variables in simulations,\n", "because fewer parameters need to be set. The present problem is made\n", "dimensionless by introducing dimensionless variables $\\bar t = t/t_c$\n", "and $\\bar u = u/u_c$, where $t_c$ and $u_c$ are characteristic scales\n", "for $t$ and $u$, respectively. We refer to Section 2.2.1 in \n", "[[Langtangen_scaling]](#Langtangen_scaling) for all details about this scaling.\n", "\n", "The scaled ODE problem reads" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "$$\n", "\\frac{u_c}{t_c^2}\\frac{d^2\\bar u}{d\\bar t^2} + u_c\\bar u = 0,\\quad\n", "u_c\\bar u(0) = I,\\ \\frac{u_c}{t_c}\\frac{d\\bar u}{d\\bar t}(0)=0\\thinspace .\n", "$$" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "A common choice is to take $t_c$ as one period of\n", "the oscillations, $t_c = 2\\pi/w$, and $u_c=I$.\n", "This gives the dimensionless model" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "\n", "
\n", "\n", "$$\n", "\\begin{equation}\n", "\\frac{d^2\\bar u}{d\\bar t^2} + 4\\pi^2 \\bar u = 0,\\quad \\bar u(0)=1,\\ \n", "\\bar u^{\\prime}(0)=0\\thinspace .\n", "\\label{vib:ode1:model:scaled} \\tag{13}\n", "\\end{equation}\n", "$$" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Observe that there are no physical parameters in ([13](#vib:ode1:model:scaled))!\n", "We can therefore perform\n", "a single numerical simulation $\\bar u(\\bar t)$ and afterwards\n", "recover any $u(t; \\omega, I)$ by" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "$$\n", "u(t;\\omega, I) = u_c\\bar u(t/t_c) = I\\bar u(\\omega t/(2\\pi))\\thinspace .\n", "$$" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We can easily check this assertion: the solution of the scaled problem\n", "is $\\bar u(\\bar t) = \\cos(2\\pi\\bar t)$. The formula for $u$ in terms\n", "of $\\bar u$ gives $u = I\\cos(\\omega t)$, which is nothing but the solution\n", "of the original problem with dimensions.\n", "\n", "The scaled model can be run by calling `solver(I=1, w=2*pi, dt, T)`.\n", "Each period is now 1 and `T` simply counts the number of periods.\n", "Choosing `dt` as `1./M` gives `M` time steps per period.\n", "\n", "\n", "# Visualization of long time simulations\n", "
\n", "\n", "[Figure](#vib:ode1:2dt) shows a comparison of the exact and numerical\n", "solution for the scaled model ([13](#vib:ode1:model:scaled)) with\n", "$\\Delta t=0.1, 0.05$.\n", "From the plot we make the following observations:\n", "\n", " * The numerical solution seems to have correct amplitude.\n", "\n", " * There is an angular frequency error which is reduced by decreasing the time step.\n", "\n", " * The total angular frequency error grows with time.\n", "\n", "By angular frequency error we mean that the numerical angular frequency differs\n", "from the exact $\\omega$. This is evident by looking\n", "at the peaks of the numerical solution: these have incorrect\n", "positions compared with the peaks of the exact cosine solution. The\n", "effect can be mathematically expressed by writing the numerical solution\n", "as $I\\cos\\tilde\\omega t$, where $\\tilde\\omega$ is not exactly\n", "equal to $\\omega$. Later, we shall mathematically\n", "quantify this numerical angular frequency $\\tilde\\omega$.\n", "\n", "Here, we show the effect of halving the time step on the error." ] }, { "cell_type": "code", "execution_count": 19, "metadata": {}, "outputs": [], "source": [ "I = 1\n", "w = 2*np.pi\n", "num_periods = 5\n", "P = 2*np.pi/w # one period\n", "T = P*num_periods" ] }, { "cell_type": "code", "execution_count": 20, "metadata": {}, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "Operator `Kernel` run in 0.01 s\n" ] }, { "data": { "image/png": "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\n", "text/plain": [ "
" ] }, "metadata": { "needs_background": "light" }, "output_type": "display_data" } ], "source": [ "dt = 0.1\n", "u_1, t = solver(I, w, dt, T)\n", "visualize(u_1, t, I, w)" ] }, { "cell_type": "code", "execution_count": 21, "metadata": {}, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "Operator `Kernel` run in 0.01 s\n" ] }, { "data": { "image/png": "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\n", "text/plain": [ "
" ] }, "metadata": { "needs_background": "light" }, "output_type": "display_data" } ], "source": [ "dt = 0.05\n", "u_2, t = solver(I, w, dt, T)\n", "visualize(u_2, t, I, w)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "\n", "## Using a moving plot window\n", "\n", "\n", "In vibration problems it is often of interest to investigate the system's\n", "behavior over long time intervals. Errors in the angular frequency accumulate\n", "and become more visible as time grows. We can investigate long\n", "time series by introducing a moving plot window that can move along with\n", "the $p$ most recently computed periods of the solution. The\n", "[SciTools](https://github.com/hplgit/scitools) package contains\n", "a convenient tool for this: `MovingPlotWindow`. Typing\n", "`pydoc scitools.MovingPlotWindow` shows a demo and a description of its use.\n", "The function below utilizes the moving plot window and is in fact\n", "called by the `main` function in the `vib_undamped` module\n", "if the number of periods in the simulation exceeds 10." ] }, { "cell_type": "code", "execution_count": 22, "metadata": {}, "outputs": [], "source": [ "# %load -s visualize_front, src-vib/vib_undamped.py\n", "def visualize_front(u, t, I, w, savefig=False, skip_frames=1):\n", " \"\"\"\n", " Visualize u and the exact solution vs t, using a\n", " moving plot window and continuous drawing of the\n", " curves as they evolve in time.\n", " Makes it easy to plot very long time series.\n", " Plots are saved to files if savefig is True.\n", " Only each skip_frames-th plot is saved (e.g., if\n", " skip_frame=10, only each 10th plot is saved to file;\n", " this is convenient if plot files corresponding to\n", " different time steps are to be compared).\n", " \"\"\"\n", " import scitools.std as st\n", " from scitools.MovingPlotWindow import MovingPlotWindow\n", " from math import pi\n", "\n", " # Remove all old plot files tmp_*.png\n", " import glob, os\n", " for filename in glob.glob('tmp_*.png'):\n", " os.remove(filename)\n", "\n", " P = 2*pi/w # one period\n", " umin = 1.2*u.min(); umax = -umin\n", " dt = t[1] - t[0]\n", " plot_manager = MovingPlotWindow(\n", " window_width=8*P,\n", " dt=dt,\n", " yaxis=[umin, umax],\n", " mode='continuous drawing')\n", " frame_counter = 0\n", " for n in range(1,len(u)):\n", " if plot_manager.plot(n):\n", " s = plot_manager.first_index_in_plot\n", " st.plot(t[s:n+1], u[s:n+1], 'r-1',\n", " t[s:n+1], I*cos(w*t)[s:n+1], 'b-1',\n", " title='t=%6.3f' % t[n],\n", " axis=plot_manager.axis(),\n", " show=not savefig) # drop window if savefig\n", " if savefig and n % skip_frames == 0:\n", " filename = 'tmp_%04d.png' % frame_counter\n", " st.savefig(filename)\n", " print('making plot file', filename, 'at t=%g' % t[n])\n", " frame_counter += 1\n", " plot_manager.update(n)\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We run the scaled problem (the default values for the command-line arguments\n", "`--I` and `--w` correspond to the scaled problem) for 40 periods with 20\n", "time steps per period:" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ " Terminal> python vib_undamped.py --dt 0.05 --num_periods 40\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The moving plot window is invoked, and we can follow the numerical and exact\n", "solutions as time progresses. From this demo we see that\n", "the angular frequency error is small in the beginning, and that it becomes more\n", "prominent with time. A new run with $\\Delta t=0.1$ (i.e., only 10 time steps per period)\n", "clearly shows that the phase errors become significant even earlier\n", "in the time series, deteriorating the solution further.\n", "\n", "## Making animations\n", "
\n", "\n", "\n", "### Producing standard video formats\n", "\n", "The `visualize_front` function stores all the plots in\n", "files whose names are numbered:\n", "`tmp_0000.png`, `tmp_0001.png`, `tmp_0002.png`,\n", "and so on. From these files we may make a movie. The Flash\n", "format is popular," ] }, { "cell_type": "markdown", "metadata": {}, "source": [ " Terminal> ffmpeg -r 25 -i tmp_%04d.png -c:v flv movie.flv\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The `-r` option should come first and\n", "describes the number of frames per second in the movie (even if we\n", "would like to have slow movies, keep this number as large as 25,\n", "otherwise files are skipped from the movie). The\n", "`-i` option describes the name of the plot files.\n", "Other formats can be generated by changing the video codec\n", "and equipping the video file with the right extension:\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "
Format Codec and filename
Flash -c:v flv movie.flv
MP4 -c:v libx264 movie.mp4
WebM -c:v libvpx movie.webm
Ogg -c:v libtheora movie.ogg
\n", "\n", "The video file can be played by some video player like `vlc`, `mplayer`,\n", "`gxine`, or `totem`, e.g.," ] }, { "cell_type": "markdown", "metadata": {}, "source": [ " Terminal> vlc movie.webm\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "A web page can also be used to play the movie. Today's standard is\n", "to use the HTML5 `video` tag:" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ " \n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Modern browsers do not support all of the video formats.\n", "MP4 is needed to successfully play the videos on Apple devices\n", "that use the Safari browser.\n", "WebM is the preferred format for Chrome, Opera, Firefox, and Internet\n", "Explorer v9+. Flash was a popular format, but older browsers that\n", "required Flash can play MP4. All browsers that work with Ogg can also\n", "work with WebM. This means that to have a video work in all browsers,\n", "the video should be available in the MP4 and WebM formats.\n", "The proper HTML code reads" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ " \n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The MP4 format should appear first to ensure that Apple devices will\n", "load the video correctly.\n", "\n", "**Caution: number the plot files correctly.**\n", "\n", "To ensure that the individual plot frames are shown in correct order,\n", "it is important to number the files with zero-padded numbers\n", "(0000, 0001, 0002, etc.). The printf format `%04d` specifies an\n", "integer in a field of width 4, padded with zeros from the left.\n", "A simple Unix wildcard file specification like `tmp_*.png`\n", "will then list the frames in the right order. If the numbers in the\n", "filenames were not zero-padded, the frame `tmp_11.png` would appear\n", "before `tmp_2.png` in the movie.\n", "\n", "\n", "\n", "### Playing PNG files in a web browser\n", "\n", "The `scitools movie` command can create a movie player for a set\n", "of PNG files such that a web browser can be used to watch the movie.\n", "This interface has the advantage that the speed of the movie can\n", "easily be controlled, a feature that scientists often appreciate.\n", "The command for creating an HTML with a player for a set of\n", "PNG files `tmp_*.png` goes like" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ " Terminal> scitools movie output_file=vib.html fps=4 tmp_*.png\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The `fps` argument controls the speed of the movie (\"frames per second\").\n", "\n", "To watch the movie, load the video file `vib.html` into some browser, e.g.," ] }, { "cell_type": "markdown", "metadata": {}, "source": [ " Terminal> google-chrome vib.html # invoke web page\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Click on `Start movie` to see the result. Moving this movie to\n", "some other place requires moving `vib.html` *and all the PNG files*\n", "`tmp_*.png`:" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ " Terminal> mkdir vib_dt0.1\n", " Terminal> mv tmp_*.png vib_dt0.1\n", " Terminal> mv vib.html vib_dt0.1/index.html\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Making animated GIF files\n", "\n", "The `convert` program from the ImageMagick software suite can be\n", "used to produce animated GIF files from a set of PNG files:" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ " Terminal> convert -delay 25 tmp_vib*.png tmp_vib.gif\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The `-delay` option needs an argument of the delay between each frame,\n", "measured in 1/100 s, so 4 frames/s here gives 25/100 s delay.\n", "Note, however, that in this particular example\n", "with $\\Delta t=0.05$ and 40 periods,\n", "making an animated GIF file out of\n", "the large number of PNG files is a very heavy process and not\n", "considered feasible. Animated GIFs are best suited for animations with\n", "not so many frames and where you want to see each frame and play them\n", "slowly.\n", "\n", "[hpl 2: Combine two simulations side by side!]\n", "\n", "## Using Bokeh to compare graphs\n", "\n", "\n", "Instead of a moving plot frame, one can use tools that allow panning\n", "by the mouse. For example, we can show four periods of several signals in\n", "several plots and then scroll with the mouse through the rest of the\n", "simulation *simultaneously* in all the plot windows.\n", "The [Bokeh](http://bokeh.pydata.org/en/latest) plotting library offers such tools, but the plots must be displayed in\n", "a web browser. The documentation of Bokeh is excellent, so here we just\n", "show how the library can be used to compare a set of $u$ curves corresponding\n", "to long time simulations. (By the way, the guidance to correct\n", "pronunciation of Bokeh in\n", "the [documentation](http://bokeh.pydata.org/en/0.10.0/docs/faq.html#how-do-you-pronounce-bokeh) and on [Wikipedia](https://en.wikipedia.org/wiki/Bokeh) is not directly compatible with a [YouTube video](https://www.youtube.com/watch?v=OR8HSHevQTM)...).\n", "\n", "Imagine we have performed experiments for a set of $\\Delta t$ values.\n", "We want each curve, together with the exact solution, to appear in\n", "a plot, and then arrange all plots in a grid-like fashion:\n", "\n", "\n", "\n", "\n", "

\n", "\n", "\n", "\n", "\n", "\n", "Furthermore, we want the axes to couple such that if we move into\n", "the future in one plot, all the other plots follows (note the\n", "displaced $t$ axes!):\n", "\n", "\n", "\n", "\n", "

\n", "\n", "\n", "\n", "\n", "\n", "\n", "A function for creating a Bokeh plot, given a list of `u` arrays\n", "and corresponding `t` arrays, is implemented below.\n", "The code combines data from different simulations, described\n", "compactly in a list of strings `legends`." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "A particular example using the `bokeh_plot` function appears below." ] }, { "cell_type": "code", "execution_count": 23, "metadata": {}, "outputs": [], "source": [ "# %load -s bokeh_plot, src-vib/vib_undamped.py\n", "def bokeh_plot(u, t, legends, I, w, t_range, filename):\n", " \"\"\"\n", " Make plots for u vs t using the Bokeh library.\n", " u and t are lists (several experiments can be compared).\n", " legens contain legend strings for the various u,t pairs.\n", " \"\"\"\n", " if not isinstance(u, (list,tuple)):\n", " u = [u] # wrap in list\n", " if not isinstance(t, (list,tuple)):\n", " t = [t] # wrap in list\n", " if not isinstance(legends, (list,tuple)):\n", " legends = [legends] # wrap in list\n", "\n", " import bokeh.plotting as plt\n", " plt.output_file(filename, mode='cdn', title='Comparison')\n", " # Assume that all t arrays have the same range\n", " t_fine = np.linspace(0, t[0][-1], 1001) # fine mesh for u_e\n", " tools = 'pan,wheel_zoom,box_zoom,reset,'\\\n", " 'save,box_select,lasso_select'\n", " u_range = [-1.2*I, 1.2*I]\n", " font_size = '8pt'\n", " p = [] # list of plot objects\n", " # Make the first figure\n", " p_ = plt.figure(\n", " width=300, plot_height=250, title=legends[0],\n", " x_axis_label='t', y_axis_label='u',\n", " x_range=t_range, y_range=u_range, tools=tools)\n", " p_.xaxis.axis_label_text_font_size=font_size\n", " p_.yaxis.axis_label_text_font_size=font_size\n", " p_.line(t[0], u[0], line_color='blue')\n", " # Add exact solution\n", " u_e = u_exact(t_fine, I, w)\n", " p_.line(t_fine, u_e, line_color='red', line_dash='4 4')\n", " p.append(p_)\n", " # Make the rest of the figures and attach their axes to\n", " # the first figure's axes\n", " for i in range(1, len(t)):\n", " p_ = plt.figure(\n", " width=300, plot_height=250, title=legends[i],\n", " x_axis_label='t', y_axis_label='u',\n", " x_range=p[0].x_range, y_range=p[0].y_range, tools=tools)\n", " p_.xaxis.axis_label_text_font_size = font_size\n", " p_.yaxis.axis_label_text_font_size = font_size\n", " p_.line(t[i], u[i], line_color='blue')\n", " p_.line(t_fine, u_e, line_color='red', line_dash='4 4')\n", " p.append(p_)\n", "\n", " # Arrange all plots in a grid with 3 plots per row\n", " grid = [[]]\n", " for i, p_ in enumerate(p):\n", " grid[-1].append(p_)\n", " if (i+1) % 3 == 0:\n", " # New row\n", " grid.append([])\n", " plot = plt.gridplot(grid, toolbar_location='left')\n", " plt.save(plot)\n", " plt.show(plot)\n" ] }, { "cell_type": "code", "execution_count": 1, "metadata": {}, "outputs": [], "source": [ "# %load -s demo_bokeh, src-vib/vib_undamped.py\n", "def demo_bokeh():\n", " \"\"\"Solve a scaled ODE u'' + u = 0.\"\"\"\n", " from math import pi\n", " w = 1.0 # Scaled problem (frequency)\n", " P = 2*np.pi/w # Period\n", " num_steps_per_period = [5, 10, 20, 40, 80]\n", " T = 40*P # Simulation time: 40 periods\n", " u = [] # List of numerical solutions\n", " t = [] # List of corresponding meshes\n", " legends = []\n", " for n in num_steps_per_period:\n", " dt = P/n\n", " u_, t_ = solver(I=1, w=w, dt=dt, T=T)\n", " u.append(u_)\n", " t.append(t_)\n", " legends.append('# time steps per period: %d' % n)\n", " bokeh_plot(u, t, legends, I=1, w=w, t_range=[0, 4*P],\n", " filename='tmp.html')\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We can run this below, which should open a window with the Bokeh plots where you can experiment with the graphs yourself:" ] }, { "cell_type": "code", "execution_count": 25, "metadata": {}, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "Operator `Kernel` run in 0.01 s\n", "Operator `Kernel` run in 0.01 s\n", "Operator `Kernel` run in 0.01 s\n", "Operator `Kernel` run in 0.01 s\n", "Operator `Kernel` run in 0.01 s\n" ] } ], "source": [ "# NBVAL_IGNORE_OUTPUT\n", "demo_bokeh()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Using a line-by-line ascii plotter\n", "\n", "Plotting functions vertically, line by line, in the terminal window\n", "using ascii characters only is a simple, fast, and convenient\n", "visualization technique for long time series. Note that the time\n", "axis then is positive downwards on the screen, so we can let the\n", "solution be visualized \"forever\".\n", "The tool\n", "`scitools.avplotter.Plotter` makes it easy to create such plots:" ] }, { "cell_type": "code", "execution_count": 26, "metadata": {}, "outputs": [], "source": [ "# %load -s visualize_front_ascii, src-vib/vib_undamped.py\n", "def visualize_front_ascii(u, t, I, w, fps=10):\n", " \"\"\"\n", " Plot u and the exact solution vs t line by line in a\n", " terminal window (only using ascii characters).\n", " Makes it easy to plot very long time series.\n", " \"\"\"\n", " from scitools.avplotter import Plotter\n", " import time\n", " from math import pi\n", " P = 2*pi/w\n", " umin = 1.2*u.min(); umax = -umin\n", "\n", " p = Plotter(ymin=umin, ymax=umax, width=60, symbols='+o')\n", " for n in range(len(u)):\n", " print(p.plot(t[n], u[n], I*np.cos(w*t[n])), '%.1f' % (t[n]/P))\n", " time.sleep(1/float(fps))\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The call `p.plot` returns a line of text, with the $t$ axis marked and\n", "a symbol `+` for the first function (`u`) and `o` for the second\n", "function (the exact solution). Here we append to this text\n", "a time counter reflecting how many periods the current time point\n", "corresponds to.\n", "\n", "The function can be run as follows:" ] }, { "cell_type": "code", "execution_count": 27, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ " | o 0.0\n", " | o+ 0.1\n", " | o + 0.1\n", " | o + 0.2\n", " | o + 0.2\n", " | + 0.2\n", " o + | 0.3\n", " o + | 0.4\n", " o + | 0.4\n", " o + | 0.5\n", " o | 0.5\n", " +o | 0.6\n", " + o | 0.6\n", " + o | 0.7\n", " + o | 0.7\n", " + | 0.8\n", " | + o 0.8\n", " | + o 0.9\n", " | + o 0.9\n", " | +o 1.0\n", " | o 1.0\n", " | o+ 1.1\n", " | o + 1.1\n", " | o + 1.2\n", " | o + 1.2\n", " | + 1.2\n", " o + | 1.3\n", " o + | 1.4\n", " o + | 1.4\n", " o+ | 1.5\n", " o | 1.5\n", " +o | 1.6\n", " + o | 1.6\n", " + o | 1.7\n", " + o | 1.7\n", " + | 1.8\n", " | + o 1.8\n", " | + o 1.9\n", " | +o 1.9\n", " | +o 2.0\n", " | o 2.0\n", " | o 2.1\n", " | o + 2.1\n", " | o + 2.1\n", " | o + 2.2\n", " | + 2.2\n", " o + | 2.3\n", " o + | 2.4\n", " o+ | 2.4\n", " o+ | 2.5\n", " o | 2.5\n", " o | 2.6\n", " +o | 2.6\n", " +o | 2.7\n", " + o | 2.7\n", " + | 2.8\n", " | + o 2.8\n", " | + o 2.9\n", " | +o 2.9\n", " | +o 3.0\n", " | o 3.0\n", " | o 3.1\n", " | o+ 3.1\n", " | o+ 3.2\n", " | o+ 3.2\n", " | + 3.2\n", " o + | 3.3\n", " o + | 3.4\n", " o+ | 3.4\n", " o+ | 3.5\n", " o | 3.5\n", " o | 3.6\n", " +o | 3.6\n", " +o | 3.7\n", " +o | 3.7\n", " +| 3.8\n", " | + o 3.8\n", " | +o 3.9\n", " | +o 3.9\n", " | +o 4.0\n", " | o 4.0\n", " | o 4.0\n", " | o+ 4.1\n", " | o+ 4.2\n", " | o+ 4.2\n", " |+ 4.2\n", " o+ | 4.3\n", " o+ | 4.4\n", " o | 4.4\n", " o+ | 4.5\n", " o | 4.5\n", " o | 4.5\n", " +o | 4.6\n", " o | 4.7\n", " +o | 4.7\n", " +| 4.8\n", " | +o 4.8\n", " | +o 4.9\n", " | o 4.9\n", " | o 5.0\n", " | o 5.0\n" ] } ], "source": [ "visualize_front_ascii(u, t, I, w)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Empirical analysis of the solution\n", "
\n", "\n", "\n", "For oscillating functions like those in [Figure](#vib:ode1:2dt) we may\n", "compute the amplitude and frequency (or period) empirically.\n", "That is, we run through the discrete solution points $(t_n, u_n)$ and\n", "find all maxima and minima points. The distance between two consecutive\n", "maxima (or minima) points can be used as estimate of the local period,\n", "while half the difference between the $u$ value at a maximum and a nearby\n", "minimum gives an estimate of the local amplitude.\n", "\n", "The local maxima are the points where" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "\n", "
\n", "\n", "$$\n", "\\begin{equation}\n", "u^{n-1} < u^n > u^{n+1},\\quad n=1,\\ldots,N_t-1,\n", "\\label{_auto4} \\tag{14}\n", "\\end{equation}\n", "$$" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "and the local minima are recognized by" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "\n", "
\n", "\n", "$$\n", "\\begin{equation}\n", "u^{n-1} > u^n < u^{n+1},\\quad n=1,\\ldots,N_t-1\n", "\\thinspace .\n", "\\label{_auto5} \\tag{15}\n", "\\end{equation}\n", "$$" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "In computer code this becomes" ] }, { "cell_type": "code", "execution_count": 9, "metadata": {}, "outputs": [], "source": [ "# %load -s minmax, src-vib/vib_empirical_analysis.py\n", "def minmax(t, u):\n", " \"\"\"\n", " Compute all local minima and maxima of the function u(t),\n", " represented by discrete points in the arrays u and t.\n", " Return lists minima and maxima of (t[i],u[i]) extreme points.\n", " \"\"\"\n", " minima = []; maxima = []\n", " for n in range(1, len(u)-1, 1):\n", " if u[n-1] > u[n] < u[n+1]:\n", " minima.append((t[n], u[n]))\n", " if u[n-1] < u[n] > u[n+1]:\n", " maxima.append((t[n], u[n]))\n", " return minima, maxima\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Note that the two returned objects are lists of tuples.\n", "\n", "Let $(t_i, e_i)$, $i=0,\\ldots,M-1$, be the sequence of all\n", "the $M$ maxima points, where $t_i$\n", "is the time value and $e_i$ the corresponding $u$ value.\n", "The local period can be defined as $p_i=t_{i+1}-t_i$.\n", "With Python syntax this reads" ] }, { "cell_type": "code", "execution_count": 10, "metadata": {}, "outputs": [], "source": [ "# %load -s periods, src-vib/vib_empirical_analysis.py\n", "def periods(extrema):\n", " \"\"\"\n", " Given a list of (t,u) points of the maxima or minima,\n", " return an array of the corresponding local periods.\n", " \"\"\"\n", " p = [extrema[n][0] - extrema[n-1][0]\n", " for n in range(1, len(extrema))]\n", " return np.array(p)\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The list `p` created by a list comprehension is converted to an array\n", "since we probably want to compute with it, e.g., find the corresponding\n", "frequencies `2*pi/p`.\n", "\n", "Having the minima and the maxima, the local amplitude can be\n", "calculated as the difference between two neighboring minimum and\n", "maximum points:" ] }, { "cell_type": "code", "execution_count": 11, "metadata": {}, "outputs": [], "source": [ "# %load -s amplitudes, src-vib/vib_empirical_analysis.py\n", "def amplitudes(minima, maxima):\n", " \"\"\"\n", " Given a list of (t,u) points of the minima and maxima of\n", " u, return an array of the corresponding local amplitudes.\n", " \"\"\"\n", " # Compare first maxima with first minima and so on\n", " a = [(abs(maxima[n][1] - minima[n][1]))/2.0\n", " for n in range(min(len(minima),len(maxima)))]\n", " return np.array(a)\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The code segments are found in the file [`vib_empirical_analysis.py`](https://github.com/devitocodes/devito_book/blob/master/fdm-devito-notebooks/01_vib/src-vib/vib_empirical_analysis.py).\n", "\n", "Since `a[i]` and `p[i]` correspond to\n", "the $i$-th amplitude estimate and the $i$-th period estimate, respectively,\n", "it is most convenient to visualize the `a` and `p` values with the\n", "index `i` on the horizontal axis.\n", "(There is no unique time point associated with either of these estimate\n", "since values at two different time points were used in the\n", "computations.)\n", "\n", "In the analysis of very long time series, it is advantageous to\n", "compute and plot `p` and `a` instead of $u$ to get an impression of\n", "the development of the oscillations. Let us do this for the scaled\n", "problem and $\\Delta t=0.1, 0.05, 0.01$.\n", "A ready-made function" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "```python\n", "plot_empirical_freq_and_amplitude(u, t, I, w)\n", "```" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "computes the empirical amplitudes and periods, and creates a plot\n", "where the amplitudes and angular frequencies\n", "are visualized together with the exact amplitude `I`\n", "and the exact angular frequency `w`. " ] }, { "cell_type": "code", "execution_count": 12, "metadata": {}, "outputs": [], "source": [ "# %load -s plot_empirical_freq_and_amplitude, src-vib/vib_undamped.py\n", "def plot_empirical_freq_and_amplitude(u, t, I, w):\n", " \"\"\"\n", " Find the empirical angular frequency and amplitude of\n", " simulations in u and t. u and t can be arrays or (in\n", " the case of multiple simulations) multiple arrays.\n", " One plot is made for the amplitude and one for the angular\n", " frequency (just called frequency in the legends).\n", " \"\"\"\n", " from math import pi\n", " if not isinstance(u, (list,tuple)):\n", " u = [u]\n", " t = [t]\n", " legends1 = []\n", " legends2 = []\n", " for i in range(len(u)):\n", " minima, maxima = minmax(t[i], u[i])\n", " p = periods(maxima)\n", " a = amplitudes(minima, maxima)\n", " plt.figure(1)\n", " plt.plot(range(len(p)), 2*pi/p)\n", " legends1.append('frequency, case%d' % (i+1))\n", " plt.figure(2)\n", " plt.plot(range(len(a)), a)\n", " legends2.append('amplitude, case%d' % (i+1))\n", " plt.figure(1)\n", " plt.plot(range(len(p)), [w]*len(p), 'k--')\n", " legends1.append('exact frequency')\n", " plt.legend(legends1, loc='lower left')\n", " plt.axis([0, len(a)-1, 0.8*w, 1.2*w])\n", " plt.savefig('tmp1.png'); plt.savefig('tmp1.pdf')\n", " plt.figure(2)\n", " plt.plot(range(len(a)), [I]*len(a), 'k--')\n", " legends2.append('exact amplitude')\n", " plt.legend(legends2, loc='lower left')\n", " plt.axis([0, len(a)-1, 0.8*I, 1.2*I])\n", " plt.savefig('tmp2.png'); plt.savefig('tmp2.pdf')\n", " plt.show()\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We can make a little program\n", "for creating the plot:" ] }, { "cell_type": "code", "execution_count": 14, "metadata": {}, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "Operator `Kernel` run in 0.01 s\n", "Operator `Kernel` run in 0.01 s\n", "Operator `Kernel` run in 0.01 s\n" ] }, { "data": { "image/png": "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Y0fM/8vNzY08Nu6/Hd/atWv56U9/ygUXXMDixYspKSlJKrZob775Jps3b+bdd9+loKCA5cuX097eTlFREZ988gmvvvoq69ev509/+hO//vWvE8YuhEgfqexMwcqVK/ntb38b+dtMaueddx5PPfUUAK+88grHjx8HYPz48Rw7dgy/309HRwd/+ctfABg9ejQVFRU888wzgJG0P/nkk4TvfdFFF7FhwwZaW1sBaGhoAIyRlxdffDG33nprr+qWH/3oR5ESd7QVK1bw8MMPA0YdeVNTE01NTRQVFVFQUMCePXt47733APD5fIRCIdauXcs999zDRx99dFKxCyGGhyT0FNx///1UV1ezYMEC5s6dy/r16wH4yU9+wrZt25g3bx7PPfccU6dOBSA7O5u77rqLZcuWcdFFFzF79uzIaz355JM8+uijVFZWMm/ePF588cWE771q1SouvfRSlixZQlVVVa+eJNdeey0Oh4OVK1dGln322WdMmDCh3+v85je/4Y033mD+/PksXryYXbt2sWrVKrq7u5kzZw533nknZ555JgB1dXUsX76cqqoqvvWtb/Gv//qvCWP/8MMPKSsr45lnnmHdunXMm2eNqywhRgo10CX9UFmyZImurq7utWz37t3MmTMnLfEMpvLycqqrq3G73cPyfvfeey9NTU38/Oc/jyy7+OKLefXVV4fl/VNll/0sRDoopbZrrZfEWmfZOnSRnCuuuIIDBw7062Vi1WQuhBg6ktCHgNfrHbb3ilVPLoQYmaQOXQghbEISuhBC2IQkdCGEsAlJ6EIIYROS0PuQ6XNP3n333cfcuXNZsGABK1as4NChQ+kOSYgRRRJ6Hw899BCvv/46Tz75ZK/l3d3daYoocyxcuJDq6mo+/fRTrrzySn7wgx+kOyQhRhRJ6FFk+txTmz73a1/7WmTysDPPPJPa2tqT3hdCiNRZtx/6K3fCl58N7mtOmA+r/0fc1TJ97uBNn/voo4+yevXqAT8HIcTgSSqhK6W8wAkgCHT3HXaqlFoOvAh4woue01r/bPDCTB+ZPjf16XOfeOIJqqur2bp1a8JYhBCDK5US+te01r4E69/SWn/9VAOKSFCSHk4yfW5q0+du3ryZX/ziF2zdurXXdgghhp7UoadAps9NPH3uxx9/zLp163jppZcYN25c0p+rEGJwJJvQNfCaUmq7UurmOM85Syn1iVLqFaWULedNlelzE0+f+/3vf5+WlhauuuoqqqqquPTSS0/mYxZCnKSkps9VSk3WWtcppcYBrwN3aK23Ra0fDYS01i1KqUuA32itZ8Z4nZuBmwGmTp26uG8/ZbtMqyrT5yZml/0sRDqc8vS5Wuu68O9jSqnngWXAtqj1zVGPNyqlHlJKufvWuWutHwEeAWM+9JS3RPQj0+cKIUwDJnSllAtwaK1PhB+vBH7W5zkTgKNaa62UWoZRleMfioAzgUyfK4RIh2RK6OOB55VS5vOf0lpvUkrdAqC1Xg9cCdyqlOoG2oCrdbpuhSSEECPUgAlda30QqIyxfH3U4weABwY3NCGEEKmQbotCCGETktCFEMImJKH3IdPnnrz169czf/58qqqqOOecc3pNkyCEGHrWnZwrTR566CE2b95MWVlZr+Xd3d1kZcnHlcg111zDLbfcAsBLL73EP/3TP7Fp06Y0RyXEyCEl9Cgyfe6pTZ87evToyPsEAgHCPaOEEMPEskXOX37wS/Y07BnU15xdPJsfLvth3PUyfe6pT5/74IMPct9999HZ2dlvsJMQYmhZNqFbhUyfm9r0ubfffju33347Tz31FPfccw+/+93vEsYjhBg8lk3oiUrSw0mmz01t+lzT1Vdfza233ppyLEKIkyd16CmQ6XMTT5+7b9++yPu8/PLLzJzZb342IcQQkoSeApk+N/H0uQ888ADz5s2jqqqK++67T6pbhBhmSU2fOxSWLFmiq6urey2zy7SqMn1uYnbZz0KkwylPnyusS6bPFUKYJKEPAZk+VwiRDlKHLoQQNiEJXQghbEISuhBC2IQkdCGEsAlJ6MPohRdeiDulbH19PWeccQYLFy7krbfeGubIhBB2IAl9GCVK6H/961+ZP38+H3/8Meeee26vdcFgcDjCE0JkOEnofTzxxBMsW7aMqqoq1q1bRzAY5MMPP2TBggW0t7cTCASYN28eO3fupKWlhRUrVrBo0SLmz5/fa7Tn73//exYsWEBlZSXXXXcd77zzDi+99BLf//73qaqq4sCBA5Hn7tixgx/84Ae8+OKLVFVV0dbWxqhRo/je975HZWUl7777bsy4ADZs2MBpp53GsmXLuOmmm/j7v/97wJiGN3rq3VGjRkUe/9u//RtLly5lwYIF/OQnPwGMrpZz5szhpptuYt68eaxcuZK2tjYA9u/fz4UXXkhlZSWLFi3iwIEDXH/99bzwwguR17z22msHHO0qhBhiWuu0/CxevFj3tWvXrl5/n3/++f1+HnzwQa211oFAIOb6DRs2aK21rq+v77duILt27dJf//rXdWdnp9Za61tvvVX/7ne/01pr/eMf/1h/73vf07fddpv+l3/5F6211l1dXbqpqSnyfjNmzNChUEjv3LlTz5w5U9fX12uttfb7/VprrW+44Qb9zDPPxHzvDRs26Ntvvz3yN6CffvrphHEdOXJET5kyRR87dkx3dHTos88+O/Iafd/L5XJprbV+9dVX9U033aRDoZAOBoN6zZo1euvWrdrj8Win06k//vhjrbXWV111lf7DH/6gtdZ62bJl+rnnntNaa93W1qYDgYB+88039WWXXaa11rqxsVGXl5frrq6uAT9jc3uEECcHqNZx8qoMLIry17/+le3bt7N06VIA2traGDduHAB33XUXS5cuJS8vj/vvvx8wTob//M//zLZt23A4HNTV1XH06FG2bNnCVVddFRn6b051mwqn08natWsTxvX++++zfPlySktLAfi7v/s7Pv/884Sv+9prr/Haa6+xcOFCwJjKd9++fUydOpWKigqqqqoAWLx4MV6vlxMnTlBXV8cVV1wBEJlp8vzzz+e2226jvr6eP//5z6xdu1bu6CREmln6G/jmm2/GXVdQUJBwvdvtTrg+Fq01N9xwQ2QSqmh+v5+Wlha6urpob2/H5XLx5JNPUl9fz/bt28nOzqa8vDwyRe6pysvLw+l0Jowrusqjr+ipe0OhEJ2dnZHX+tGPfsS6det6Pd/r9faattfpdEaqXOK5/vrreeKJJ/jjH//Ihg0bkt84IcSQkDr0KCtWrODZZ5/l2LFjgDFF7aFDhwBYt24dP//5z7n22mv54Q+NudqbmpoYN24c2dnZvPHGG5HnXnDBBTzzzDP4/f7I6wAUFhZy4sSJQYvrjDPOYOvWrfj9frq6uiJT2oIxQdj27dsB4/6eXV1dgDFp12OPPRa5U1FdXV3kdWMpLCykrKwscvLo6OiITOF744038utf/xqAuXPnprxdQojBZekS+nCbO3cu99xzDytXriQUCpGdnc2DDz7I1q1byc7O5pprriEYDHL22WezZcsWrr32Wr7xjW8wf/58lixZEpked968efz4xz/m/PPPx+l0snDhQh5//HGuvvpqbrrpJu6//36effZZZsyYcUpxnXnmmdx9992cddZZjB07NlJdAnDTTTdx2WWXUVlZyapVqyI36li5ciW7d+/mrLPOAozG0ieeeCJyNRDLH/7wB9atW8ddd91FdnY2zzzzDNOnT2f8+PHMmTOHyy+//GQ/ciHEIJLpc23k8ccfp7q6mgceeGBY3q+1tZX58+fz0UcfRW5llwzZz0KcvETT50qVizgpmzdvZs6cOdxxxx0pJXMhxNCRKhcbufHGG7nxxhuH5b0uvPDCSJuBEMIapIQuhBA2YbmEnq46fTE8ZP8KMXQsldDz8vLw+/3ypbcprTV+vz8yOEkIMbgsVYdeVlZGbW0t9fX16Q5FDJG8vDzKysrSHYYQtmSphJ6dnU1FRUW6wxBCiIxkqSoXIYQQJ08SuhBC2IQkdCGEsIm0Df1XSp0A9qblzU+OG/ClO4gUSLxDS+IdepkW83DFO01rXRprRTobRffGm4/AipRS1RLv0JF4h1amxQuZF7MV4pUqFyGEsAlJ6EIIYRPpTOiPpPG9T4bEO7Qk3qGVafFC5sWc9njT1igqhBBicEmVixBC2ERaErpSapVSaq9Sar9S6s50xJAKpZRXKfWZUmqHUqp64P8YXkqpx5RSx5RSO6OWFSulXldK7Qv/LkpnjNHixHu3Uqou/BnvUEpdks4Yoymlpiil3lBK7VJK/YdS6h/Cyy35GSeI15KfsVIqTyn1gVLqk3C8Pw0vr1BKvR/OE08rpXLSHSskjPdxpZQn6vOtGui1Bp3Welh/ACdwAJgO5ACfAHOHO44UY/YC7nTHkSC+84BFwM6oZf8TuDP8+E7gl+mOc4B47wb+e7pjixPvRGBR+HEh8Dkw16qfcYJ4LfkZAwoYFX6cDbwPnAn8Cbg6vHw9cGu6Yx0g3seBK9MZWzpK6MuA/Vrrg1rrTuCPwGVpiMM2tNbbgIY+iy8Dfhd+/DvAMndyjhOvZWmtv9BafxR+fALYDUzGop9xgngtSRtawn9mh380cAHwbHi5lT7fePGmXToS+mSgJurvWix8sIVp4DWl1Hal1M3pDiZJ47XWX4QffwmMT2cwSfp7pdSn4SoZS1Rf9KWUKgcWYpTKLP8Z94kXLPoZK6WcSqkdwDHgdYyr+EatdXf4KZbKE33j1Vqbn+8vwp/vr5RSucMdlzSKJuccrfUiYDVwu1LqvHQHlAptXBtaogSRwMPADKAK+AL4X+kNpz+l1Cjgz8B/01o3R6+z4mccI17LfsZa66DWugoow7iKn53mkBLqG69S6nTgRxhxLwWKgR8Od1zpSOh1wJSov8vCyyxLa10X/n0MeB7jgLO6o0qpiQDh38fSHE9CWuuj4S9JCPg/WOwzVkplYyTHJ7XWz4UXW/YzjhWv1T9jAK11I/AGcBYwVillTk9iyTwRFe+qcFWX1lp3ABtIw+ebjoT+ITAz3IKdA1wNvJSGOJKilHIppQrNx8BKYGfi/7KEl4Abwo9vAF5MYywDMhNj2BVY6DNWSingUWC31vq+qFWW/IzjxWvVz1gpVaqUGht+nA9chFHv/wZwZfhpVvp8Y8W7J+rkrjDq+4f9803LwKJwd6lfY/R4eUxr/YthDyJJSqnpGKVyMCYze8pq8Sql/h1YjjHb21HgJ8ALGL0EpgKHgP+stbZEQ2SceJdjVAVojF5F66Lqp9NKKXUO8BbwGRAKL/5njHppy33GCeL9Jhb8jJVSCzAaPZ0Yhcw/aa1/Fv7u/RGj+uJj4Fvh0m9aJYh3C1CK0QtmB3BLVOPp8MSWjoQuhBBi8EmjqBBC2IQkdCGEsAlJ6EIIYROS0IUQwiYkoQshhE1IQhdCCJuQhC6EEDYhCV0IIWzi/wMbH4TWPnzCDgAAAABJRU5ErkJggg==\n", "text/plain": [ "
" ] }, "metadata": { "needs_background": "light" }, "output_type": "display_data" }, { "data": { "image/png": "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LGh4aEIIISxBzjQSQgg7J4leCCHsnCR6IYSwc5LohRDCzkmiF0IIOyeJXggh7JwkeiGEsHOS6IUQws5JohdCCDsniV4IIeycJHohhLBzkuiFEMLOSaIXQgg7J4leCCHsnCR6IYSwc5LohRDCzkmiF0IIOyeJXggh7JxZiV4pNUYpdUgpdVQp9cxFyjsppX5SSu1VSm1WSgVVKStTSsWabt9YMnghhBD1q/easUopR+A94EYgGdillPpGa32gymRvAIu11ouUUiOBV4ApprICrXW4heMWQghhJnNa9AOBo1rr41rrYmA5MK7GNMHARtPjTRcpF0IIYSPmJPr2QFKV58mm16raA9xhenw74KWU8jU9d1NKRSmltiulbqtrIUqp6abpotLT080MXwghRH0stTP2SeA6pdRu4DogBSgzlXXSWkcCk4C3lFLdLjYDrfV8rXWk1jrS39/fQmEJIYSot48eI2l3qPI8yPRaJa31KUwteqWUJzBea51lKksx3R9XSm0G+gHHGh25EEIIs5jTot8FdFdKdVFKuQB3AdWOnlFK+SmlKub1LLDA9HorpZRrxTTANUDVnbhCCCGsrN5Er7UuBWYCPwDxwBda6/1KqblKqVtNkw0HDimlDgNtgJdNr/cGopRSezB20r5a42gdIYQQVqa01raOoZbIyEgdFRVl6zCEEOKKoZSKNu0PrUXOjBVCCDsniV4IIeycJHohhLBzkuiFEMLOSaIXQgg7J4leCCHsnCR6IYSwc5LohRDCzpkz1k2zUFJSQnJyMoWFhbYORViJm5sbQUFBODs72zoUIezKFZPok5OT8fLyonPnziilbB2OsDCtNRkZGSQnJ9OlSxdbhyOEXblium4KCwvx9fWVJG+nlFL4+vrKPzYhrOCKSfSAJHk7J/UrhHVcUYleCCFEw0mibwY8PT0BOHXqFH/84x8BiI2NZd26dQ2e15w5c3jjjTcsGp8lZGRkMGLECDw9PZk5c6atwxHid0USfTMSGBjIihUrgMtP9M2Vm5sbL774YrPcCAlh766Yo26q+uea/Rw4lWPReQYHevPCLSGXnOa2224jKSmJwsJCHnvsMaZPnw4YLfKHHnqIdevW0a5dO/71r3/x9NNPk5iYyFtvvcWtt97KwoULWbVqFdnZ2aSkpDB58mReeOGFavM/efIkN998MzExMTz//PMUFBSw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\n", "text/plain": [ "
" ] }, "metadata": { "needs_background": "light" }, "output_type": "display_data" } ], "source": [ "# NBVAL_IGNORE_OUTPUT\n", "from math import pi\n", "dt_values = [0.1, 0.05, 0.01]\n", "u_cases = []\n", "t_cases = []\n", "for dt in dt_values:\n", " # Simulate scaled problem for 40 periods\n", " u, t = solver(I=1, w=2*pi, dt=dt, T=40)\n", " u_cases.append(u)\n", " t_cases.append(t)\n", "plot_empirical_freq_and_amplitude(u_cases, t_cases, I=1, w=2*pi)\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Empirical angular frequency (top) and amplitude (bottom) for three different time steps.\n", "\n", "We can clearly see that\n", "lowering $\\Delta t$ improves the angular frequency significantly, while the\n", "amplitude seems to be more accurate.\n", "The lines with\n", "$\\Delta t=0.01$, corresponding to 100 steps per period, can hardly be\n", "distinguished from the exact values. The next section shows how we\n", "can get mathematical insight into why amplitudes are good while frequencies\n", "are more inaccurate." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Analysis of the numerical scheme\n", "
\n", "\n", "## Deriving a solution of the numerical scheme\n", "
\n", "\n", "After having seen the phase error grow with time in the previous\n", "section, we shall now quantify this error through mathematical\n", "analysis. The key tool in the analysis will be to establish an exact\n", "solution of the discrete equations. The difference equation\n", "([7](#vib:ode1:step4)) has constant coefficients and is\n", "homogeneous. Such equations are known to have solutions on the form\n", "$u^n=CA^n$, where $A$ is some number\n", "to be determined from the difference equation and $C$ is found as the\n", "initial condition ($C=I$). Recall that $n$ in $u^n$ is a\n", "superscript labeling the time level, while $n$ in $A^n$ is an\n", "exponent.\n", "\n", "With oscillating functions as solutions, the algebra will\n", "be considerably simplified if we seek an $A$ on the form" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "$$\n", "A=e^{i\\tilde\\omega \\Delta t},\n", "$$" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "and solve for the numerical frequency $\\tilde\\omega$ rather than\n", "$A$. Note that $i=\\sqrt{-1}$ is the imaginary unit. (Using a\n", "complex exponential function gives simpler arithmetics than working\n", "with a sine or cosine function.)\n", "We have" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "$$\n", "A^n = e^{i\\tilde\\omega \\Delta t\\, n}=e^{i\\tilde\\omega t_n} =\n", "\\cos (\\tilde\\omega t_n) + i\\sin(\\tilde \\omega t_n)\n", "\\thinspace .\n", "$$" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The physically relevant numerical solution can\n", "be taken as the real part of this complex expression.\n", "\n", "The calculations go as" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "$$\n", "\\begin{align*}\n", "[D_tD_t u]^n &= \\frac{u^{n+1} - 2u^n + u^{n-1}}{\\Delta t^2}\\\\ \n", "&= I\\frac{A^{n+1} - 2A^n + A^{n-1}}{\\Delta t^2}\\\\ \n", "&= \\frac{I}{\\Delta t^{2}}(e^{i\\tilde\\omega(t_n+\\Delta t)} - 2e^{i\\tilde\\omega t_n} + e^{i\\tilde\\omega(t_n-\\Delta t)})\\\\ \n", "&= Ie^{i\\tilde\\omega t_n}\\frac{1}{\\Delta t^2}\\left(e^{i\\tilde\\omega\\Delta t} + e^{i\\tilde\\omega(-\\Delta t)} - 2\\right)\\\\ \n", "&= Ie^{i\\tilde\\omega t_n}\\frac{2}{\\Delta t^2}\\left(\\cosh(i\\tilde\\omega\\Delta t) -1 \\right)\\\\ \n", "&= Ie^{i\\tilde\\omega t_n}\\frac{2}{\\Delta t^2}\\left(\\cos(\\tilde\\omega\\Delta t) -1 \\right)\\\\ \n", "&= -Ie^{i\\tilde\\omega t_n}\\frac{4}{\\Delta t^2}\\sin^2(\\frac{\\tilde\\omega\\Delta t}{2})\n", "\\end{align*}\n", "$$" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The last line follows from the relation\n", "$\\cos x - 1 = -2\\sin^2(x/2)$ (try `cos(x)-1` on\n", "[wolframalpha.com](http://www.wolframalpha.com) to see the formula).\n", "\n", "The scheme ([7](#vib:ode1:step4))\n", "with $u^n=Ie^{i\\tilde\\omega\\Delta t\\, n}$ inserted now gives" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "\n", "
\n", "\n", "$$\n", "\\begin{equation}\n", "-Ie^{i\\tilde\\omega t_n}\n", "\\frac{4}{\\Delta t^2}\\sin^2(\\frac{\\tilde\\omega\\Delta t}{2})\n", "+ \\omega^2 Ie^{i\\tilde\\omega t_n} = 0,\n", "\\label{_auto6} \\tag{16}\n", "\\end{equation}\n", "$$" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "which after dividing by $Ie^{i\\tilde\\omega t_n}$ results in" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "\n", "
\n", "\n", "$$\n", "\\begin{equation}\n", "\\frac{4}{\\Delta t^2}\\sin^2(\\frac{\\tilde\\omega\\Delta t}{2}) = \\omega^2\n", "\\thinspace .\n", "\\label{_auto7} \\tag{17}\n", "\\end{equation}\n", "$$" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The first step in solving for the unknown $\\tilde\\omega$ is" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "$$\n", "\\sin^2(\\frac{\\tilde\\omega\\Delta t}{2}) = \\left(\\frac{\\omega\\Delta t}{2}\\right)^2\n", "\\thinspace .\n", "$$" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Then, taking the square root, applying the inverse sine function, and\n", "multiplying by $2/\\Delta t$, results in" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "\n", "
\n", "\n", "$$\n", "\\begin{equation}\n", "\\tilde\\omega = \\pm \\frac{2}{\\Delta t}\\sin^{-1}\\left(\\frac{\\omega\\Delta t}{2}\\right)\n", "\\thinspace .\n", "\\label{vib:ode1:tildeomega} \\tag{18}\n", "\\end{equation}\n", "$$" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## The error in the numerical frequency\n", "
\n", "\n", "\n", "The first observation following ([18](#vib:ode1:tildeomega)) tells that there\n", "is a phase error since the numerical frequency $\\tilde\\omega$ never\n", "equals the exact frequency $\\omega$. But how good is the approximation\n", "([18](#vib:ode1:tildeomega))? That is, what is the error $\\omega -\n", "\\tilde\\omega$ or $\\tilde\\omega/\\omega$? Taylor series expansion for\n", "small $\\Delta t$ may give an expression that is easier to understand\n", "than the complicated function in ([18](#vib:ode1:tildeomega)):" ] }, { "cell_type": "code", "execution_count": 33, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "w + dt**2*w**3/24 + O(dt**4)\n" ] } ], "source": [ "from sympy import *\n", "dt, w = symbols('dt w')\n", "w_tilde_e = 2/dt*asin(w*dt/2)\n", "w_tilde_series = w_tilde_e.series(dt, 0, 4)\n", "print(w_tilde_series)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "This means that\n", "\n", "" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "\n", "
\n", "\n", "$$\n", "\\begin{equation}\n", "\\tilde\\omega = \\omega\\left( 1 + \\frac{1}{24}\\omega^2\\Delta t^2\\right)\n", "+ \\mathcal{O}({\\Delta t^4})\n", "\\thinspace .\n", "\\label{vib:ode1:tildeomega:series} \\tag{19}\n", "\\end{equation}\n", "$$" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The error in the numerical frequency is of second-order in $\\Delta t$,\n", "and the error vanishes as $\\Delta t\\rightarrow 0$. We see that\n", "$\\tilde\\omega > \\omega$ since the term $\\omega^3\\Delta t^2/24 >0$ and\n", "this is by far the biggest term in the series expansion for small\n", "$\\omega\\Delta t$. A numerical frequency that is too large gives an\n", "oscillating curve that oscillates too fast and therefore \"lags\n", "behind\" the exact oscillations, a feature that can be seen in the\n", "left plot in [Figure](#vib:ode1:2dt).\n", "\n", "[Figure](#vib:ode1:tildeomega:plot) plots the discrete frequency\n", "([18](#vib:ode1:tildeomega)) and its approximation\n", "([19](#vib:ode1:tildeomega:series)) for $\\omega =1$ (based on the\n", "program [`vib_plot_freq.py`](https://github.com/devitocodes/devito_book/blob/master/fdm-devito-notebooks/01_vib/src-vib/vib_plot_freq.py)).\n", "Although $\\tilde\\omega$ is a function of $\\Delta t$ in\n", "([19](#vib:ode1:tildeomega:series)), it is misleading to think of\n", "$\\Delta t$ as the important discretization parameter. mathcal{I}_t is the\n", "product $\\omega\\Delta t$ that is the key discretization\n", "parameter. This quantity reflects the *number of time steps per\n", "period* of the oscillations. To see this, we set $P=N_P\\Delta t$,\n", "where $P$ is the length of a period, and $N_P$ is the number of time\n", "steps during a period. Since $P$ and $\\omega$ are related by\n", "$P=2\\pi/\\omega$, we get that $\\omega\\Delta t = 2\\pi/N_P$, which shows\n", "that $\\omega\\Delta t$ is directly related to $N_P$.\n", "\n", "The plot shows that at least $N_P\\sim 25-30$ points per period are\n", "necessary for reasonable accuracy, but this depends on the length of\n", "the simulation ($T$) as the total phase error due to the frequency\n", "error grows linearly with time (see [Exercise 2: Show linear growth of the phase with time](#vib:exer:phase:err:growth)).\n", "\n", "\n", "\n", "
\n", "\n", "

Exact discrete frequency and its second-order series expansion.

\n", "\n", "\n", "\n", "\n", "\n", "\n", "## Empirical convergence rates and adjusted $\\omega$\n", "\n", "The expression ([19](#vib:ode1:tildeomega:series)) suggests that\n", "adjusting omega to" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "$$\n", "\\omega\\left( 1 - \\frac{1}{24}\\omega^2\\Delta t^2\\right),\n", "$$" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "could have effect on the *convergence rate* of the global error in $u$\n", "(cf. the section [Verification](#vib:ode1:verify)). With the `convergence_rates` function\n", "in `vib_undamped.py` we can easily check this. A special solver, with\n", "adjusted $w$, is available as the function `solver_adjust_w`. A\n", "call to `convergence_rates` with this solver reveals that the rate is\n", "4.0! With the original, physical $\\omega$ the rate is 2.0 - as expected\n", "from using second-order finite difference approximations,\n", "as expected from the forthcoming derivation of the global error,\n", "and as expected from truncation error analysis as explained in the [truncation error analysis](../B_trunc/trunc.ipynb#trunc:vib:undamped) section.\n", "\n", "Adjusting $\\omega$ is an ideal trick for this simple problem, but when\n", "adding damping and nonlinear terms, we have no simple formula for the\n", "impact on $\\omega$, and therefore we cannot use the trick.\n", "\n", "## Exact discrete solution\n", "
\n", "\n", "\n", "Perhaps more important than the $\\tilde\\omega = \\omega + {\\cal O}(\\Delta t^2)$\n", "result found above is the fact that we have an exact discrete solution of\n", "the problem:" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "\n", "
\n", "\n", "$$\n", "\\begin{equation}\n", "u^n = I\\cos\\left(\\tilde\\omega n\\Delta t\\right),\\quad\n", "\\tilde\\omega = \\frac{2}{\\Delta t}\\sin^{-1}\\left(\\frac{\\omega\\Delta t}{2}\\right)\n", "\\thinspace .\n", "\\label{vib:ode1:un:exact} \\tag{20}\n", "\\end{equation}\n", "$$" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We can then compute the error mesh function" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "\n", "
\n", "\n", "$$\n", "\\begin{equation}\n", "e^n = u(t_n) - u^n =\n", "I\\cos\\left(\\omega n\\Delta t\\right) - I\\cos\\left(\\tilde\\omega n\\Delta t\\right)\\thinspace .\n", "\\label{vib:ode1:en} \\tag{21}\n", "\\end{equation}\n", "$$" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "From the formula $\\cos 2x - \\cos 2y = -2\\sin(x-y)\\sin(x+y)$ we can\n", "rewrite $e^n$ so the expression is easier to interpret:" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "\n", "
\n", "\n", "$$\n", "\\begin{equation}\n", "e^n = -2I\\sin\\left(t\\frac{1}{2}\\left( \\omega - \\tilde\\omega\\right)\\right)\n", "\\sin\\left(t\\frac{1}{2}\\left( \\omega + \\tilde\\omega\\right)\\right)\\thinspace .\n", "\\label{vib:ode1:en2} \\tag{22}\n", "\\end{equation}\n", "$$" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The error mesh function is ideal for verification purposes and you are\n", "strongly encouraged to make a test based on ([20](#vib:ode1:un:exact))\n", "by doing [Exercise 11: Use an exact discrete solution for verification](#vib:exer:discrete:omega).\n", "\n", "\n", "## Convergence\n", "
\n", "\n", "\n", "We can use ([19](#vib:ode1:tildeomega:series)) and ([21](#vib:ode1:en)), or\n", "([22](#vib:ode1:en2)), to show *convergence* of the numerical scheme,\n", "i.e., $e^n\\rightarrow 0$ as $\\Delta t\\rightarrow 0$, which implies\n", "that the numerical solution approaches the exact solution as $\\Delta\n", "t$ approaches to zero. We have that" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "$$\n", "\\lim_{\\Delta t\\rightarrow 0}\n", "\\tilde\\omega = \\lim_{\\Delta t\\rightarrow 0}\n", "\\frac{2}{\\Delta t}\\sin^{-1}\\left(\\frac{\\omega\\Delta t}{2}\\right)\n", "= \\omega,\n", "$$" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "by L'Hopital's rule. This result could also been computed [WolframAlpha](http://www.wolframalpha.com/input/?i=%282%2Fx%29*asin%28w*x%2F2%29+as+x-%3E0), or\n", "we could use the limit functionality in `sympy`:" ] }, { "cell_type": "code", "execution_count": 34, "metadata": {}, "outputs": [ { "data": { "text/latex": [ "$\\displaystyle w$" ], "text/plain": [ "w" ] }, "execution_count": 34, "metadata": {}, "output_type": "execute_result" } ], "source": [ "import sympy as sym\n", "dt, w = sym.symbols('x w')\n", "sym.limit((2/dt)*sym.asin(w*dt/2), dt, 0, dir='+')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Also ([19](#vib:ode1:tildeomega:series)) can be used to establish\n", "that $\\tilde\\omega\\rightarrow\\omega$ when $\\Delta t\\rightarrow 0$.\n", "mathcal{I}_t then follows from the expression(s) for $e^n$ that $e^n\\rightarrow 0$.\n", "\n", "## The global error\n", "\n", "\n", "To achieve more analytical insight into the nature of the global\n", "error, we can Taylor expand the error mesh function\n", "([21](#vib:ode1:en)). Since $\\tilde\\omega$ in\n", "([18](#vib:ode1:tildeomega)) contains $\\Delta t$ in the denominator we\n", "use the series expansion for $\\tilde\\omega$ inside the cosine\n", "function. A relevant `sympy` session is" ] }, { "cell_type": "code", "execution_count": 35, "metadata": {}, "outputs": [ { "data": { "text/latex": [ "$\\displaystyle w + \\frac{dt^{2} w^{3}}{24} + O\\left(dt^{4}\\right)$" ], "text/plain": [ "w + dt**2*w**3/24 + O(dt**4)" ] }, "execution_count": 35, "metadata": {}, "output_type": "execute_result" } ], "source": [ "from sympy import *\n", "dt, w, t = symbols('dt w t')\n", "w_tilde_e = 2/dt*asin(w*dt/2)\n", "w_tilde_series = w_tilde_e.series(dt, 0, 4)\n", "w_tilde_series" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Series expansions in `sympy` have the inconvenient `O()` term that\n", "prevents further calculations with the series. We can use the\n", "`removeO()` command to get rid of the `O()` term:" ] }, { "cell_type": "code", "execution_count": 36, "metadata": {}, "outputs": [ { "data": { "text/latex": [ "$\\displaystyle \\frac{dt^{2} w^{3}}{24} + w$" ], "text/plain": [ "dt**2*w**3/24 + w" ] }, "execution_count": 36, "metadata": {}, "output_type": "execute_result" } ], "source": [ "w_tilde_series = w_tilde_series.removeO()\n", "w_tilde_series" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Using this `w_tilde_series` expression for $\\tilde w$ in\n", "([21](#vib:ode1:en)), dropping $I$ (which is a common factor), and\n", "performing a series expansion of the error yields" ] }, { "cell_type": "code", "execution_count": 37, "metadata": {}, "outputs": [ { "data": { "text/latex": [ "$\\displaystyle \\frac{dt^{2} t w^{3} \\sin{\\left(t w \\right)}}{24} + \\frac{dt^{4} t^{2} w^{6} \\cos{\\left(t w \\right)}}{1152} + O\\left(dt^{6}\\right)$" ], "text/plain": [ "dt**2*t*w**3*sin(t*w)/24 + dt**4*t**2*w**6*cos(t*w)/1152 + O(dt**6)" ] }, "execution_count": 37, "metadata": {}, "output_type": "execute_result" } ], "source": [ "error = cos(w*t) - cos(w_tilde_series*t)\n", "error.series(dt, 0, 6)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Since we are mainly interested in the leading-order term in\n", "such expansions (the term with lowest power in $\\Delta t$, which\n", "goes most slowly to zero), we use the `.as_leading_term(dt)`\n", "construction to pick out this term:" ] }, { "cell_type": "code", "execution_count": 38, "metadata": {}, "outputs": [ { "data": { "text/latex": [ "$\\displaystyle \\frac{dt^{2} t w^{3} \\sin{\\left(t w \\right)}}{24}$" ], "text/plain": [ "dt**2*t*w**3*sin(t*w)/24" ] }, "execution_count": 38, "metadata": {}, "output_type": "execute_result" } ], "source": [ "error.series(dt, 0, 6).as_leading_term(dt)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The last result\n", "means that the leading order global (true) error at a point $t$\n", "is proportional to $\\omega^3t\\Delta t^2$. Considering only the\n", "discrete $t_n$ values for $t$, $t_n$ is related\n", "to $\\Delta t$ through $t_n=n\\Delta t$. The factor\n", "$\\sin(\\omega t)$ can at most be 1, so we use this value to\n", "bound the leading-order expression to its maximum value" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "$$\n", "e^n = \\frac{1}{24}n\\omega^3\\Delta t^3\\thinspace .\n", "$$" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "This is the dominating term of the error *at a point*.\n", "\n", "We are interested in the accumulated global error, which can be taken\n", "as the $\\ell^2$ norm of $e^n$. The norm is simply computed by summing\n", "contributions from all mesh points:" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "$$\n", "||e^n||_{\\ell^2}^2 = \\Delta t\\sum_{n=0}^{N_t} \\frac{1}{24^2}n^2\\omega^6\\Delta t^6\n", "=\\frac{1}{24^2}\\omega^6\\Delta t^7 \\sum_{n=0}^{N_t} n^2\\thinspace .\n", "$$" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The sum $\\sum_{n=0}^{N_t} n^2$ is approximately equal to\n", "$\\frac{1}{3}N_t^3$. Replacing $N_t$ by $T/\\Delta t$ and taking\n", "the square root gives the expression" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "$$\n", "||e^n||_{\\ell^2} = \\frac{1}{24}\\sqrt{\\frac{T^3}{3}}\\omega^3\\Delta t^2\\thinspace .\n", "$$" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "This is our expression for the global (or integrated) error.\n", "A primary result from this expression is that the global error\n", "is proportional to $\\Delta t^2$.\n", "\n", "\n", "## Stability\n", "\n", "Looking at ([20](#vib:ode1:un:exact)), it appears that the numerical\n", "solution has constant and correct amplitude, but an error in the\n", "angular frequency. A constant amplitude is not necessarily the case,\n", "however! To see this, note that if only $\\Delta t$ is large enough,\n", "the magnitude of the argument to $\\sin^{-1}$ in\n", "([18](#vib:ode1:tildeomega)) may be larger than 1, i.e., $\\omega\\Delta\n", "t/2 > 1$. In this case, $\\sin^{-1}(\\omega\\Delta t/2)$ has a complex\n", "value and therefore $\\tilde\\omega$ becomes complex. Type, for\n", "example, `asin(x)` in [wolframalpha.com](http://www.wolframalpha.com) to see basic properties of $\\sin^{-1}\n", "(x)$).\n", "\n", "A complex $\\tilde\\omega$ can be written $\\tilde\\omega = \\tilde\\omega_r\n", "+ i\\tilde\\omega_i$. Since $\\sin^{-1}(x)$ has a *negative* imaginary\n", "part for $x>1$, $\\tilde\\omega_i < 0$, which means that\n", "$e^{i\\tilde\\omega t}=e^{-\\tilde\\omega_i t}e^{i\\tilde\\omega_r t}$ will\n", "lead to exponential growth in time because $e^{-\\tilde\\omega_i t}$\n", "with $\\tilde\\omega_i <0$ has a positive exponent.\n", "\n", "\n", "**Stability criterion.**\n", "\n", "We do not tolerate growth in the amplitude since such growth is not\n", "present in the exact solution. Therefore, we\n", "must impose a *stability criterion* so that\n", "the argument in the inverse sine function leads\n", "to real and not complex values of $\\tilde\\omega$. The stability\n", "criterion reads" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "\n", "
\n", "\n", "$$\n", "\\begin{equation}\n", "\\frac{\\omega\\Delta t}{2} \\leq 1\\quad\\Rightarrow\\quad\n", "\\Delta t \\leq \\frac{2}{\\omega}\n", "\\thinspace .\n", "\\label{_auto8} \\tag{23}\n", "\\end{equation}\n", "$$" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "With $\\omega =2\\pi$, $\\Delta t > \\pi^{-1} = 0.3183098861837907$ will give\n", "growing solutions. [Figure](#vib:ode1:dt:unstable)\n", "displays what happens when $\\Delta t =0.3184$,\n", "which is slightly above the critical value: $\\Delta t =\\pi^{-1} + 9.01\\cdot\n", "10^{-5}$.\n", "\n", "\n", "\n", "
\n", "\n", "

Growing, unstable solution because of a time step slightly beyond the stability limit.

\n", "\n", "\n", "\n", "\n", "\n", "## About the accuracy at the stability limit\n", "\n", "An interesting question is whether the stability condition $\\Delta t <\n", "2/\\omega$ is unfortunate, or more precisely: would it be meaningful to\n", "take larger time steps to speed up computations? The answer is a\n", "clear no. At the stability limit, we have that $\\sin^{-1}\\omega\\Delta\n", "t/2 = \\sin^{-1} 1 = \\pi/2$, and therefore $\\tilde\\omega = \\pi/\\Delta\n", "t$. (Note that the approximate formula\n", "([19](#vib:ode1:tildeomega:series)) is very inaccurate for this value of\n", "$\\Delta t$ as it predicts $\\tilde\\omega = 2.34/pi$, which is a 25\n", "percent reduction.) The corresponding period of the numerical solution\n", "is $\\tilde P=2\\pi/\\tilde\\omega = 2\\Delta t$, which means that there is\n", "just one time step $\\Delta t$ between a peak (maximum) and a\n", "[through](https://simple.wikipedia.org/wiki/Wave_(physics))\n", "(minimum) in the numerical solution. This is the shortest possible\n", "wave that can be represented in the mesh! In other words, it is not\n", "meaningful to use a larger time step than the stability limit.\n", "\n", "Also, the error in angular frequency when $\\Delta t = 2/\\omega$ is\n", "severe: [Figure](#vib:ode1:dt:stablimit) shows a comparison of the\n", "numerical and analytical solution with $\\omega = 2\\pi$ and $\\Delta t =\n", "2/\\omega = \\pi^{-1}$. Already after one period, the numerical solution\n", "has a through while the exact solution has a peak (!). The error in\n", "frequency when $\\Delta t$ is at the stability limit becomes $\\omega -\n", "\\tilde\\omega = \\omega(1-\\pi/2)\\approx -0.57\\omega$. The corresponding\n", "error in the period is $P - \\tilde P \\approx 0.36P$. The error after\n", "$m$ periods is then $0.36mP$. This error has reached half a period\n", "when $m=1/(2\\cdot 0.36)\\approx 1.38$, which theoretically confirms the\n", "observations in [Figure](#vib:ode1:dt:stablimit) that the numerical\n", "solution is a through ahead of a peak already after one and a half\n", "period. Consequently, $\\Delta t$ should be chosen much less than the\n", "stability limit to achieve meaningful numerical computations.\n", "\n", "\n", "\n", "
\n", "\n", "

Numerical solution with $\\Delta t$ exactly at the stability limit.

\n", "\n", "\n", "\n", "\n", "\n", "\n", "**Summary.**\n", "\n", "From the accuracy and stability\n", "analysis we can draw three important conclusions:\n", "\n", "1. The key parameter in the formulas is $p=\\omega\\Delta t$.\n", " The period of oscillations is $P=2\\pi/\\omega$, and the\n", " number of time steps per period is $N_P=P/\\Delta t$.\n", " Therefore, $p=\\omega\\Delta t = 2\\pi/N_P$, showing that the\n", " critical parameter is the number of time steps per period.\n", " The smallest possible $N_P$ is 2, showing that $p\\in (0,\\pi]$.\n", "\n", "2. Provided $p\\leq 2$, the amplitude of the numerical solution is\n", " constant.\n", "\n", "3. The ratio of the numerical angular frequency and the exact\n", " one is\n", " $\\tilde\\omega/\\omega \\approx 1 + \\frac{1}{24}p^2$.\n", " The error $\\frac{1}{24}p^2$ leads to wrongly displaced peaks of the numerical\n", " solution, and the error in peak location grows linearly with time\n", " (see [Exercise 2: Show linear growth of the phase with time](#vib:exer:phase:err:growth)).\n", "\n", "\n", "\n", "\n", "# Alternative schemes based on 1st-order equations\n", "
\n", "\n", "\n", "A standard technique for solving second-order ODEs is to rewrite them\n", "as a system of first-order ODEs and then choose a solution strategy\n", "from the vast collection of methods for first-order ODE systems.\n", "Given the second-order ODE problem" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "$$\n", "u^{\\prime\\prime} + \\omega^2 u = 0,\\quad u(0)=I,\\ u^{\\prime}(0)=0,\n", "$$" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "we introduce the auxiliary variable $v=u^{\\prime}$ and express the ODE problem\n", "in terms of first-order derivatives of $u$ and $v$:" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "\n", "
\n", "\n", "$$\n", "\\begin{equation}\n", "u^{\\prime} = v,\n", "\\label{vib:model2x2:ueq} \\tag{24}\n", "\\end{equation}\n", "$$" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "\n", "
\n", "\n", "$$\n", "\\begin{equation} \n", "v^{\\prime} = -\\omega^2 u\n", "\\label{vib:model2x2:veq} \\tag{25}\n", "\\thinspace .\n", "\\end{equation}\n", "$$" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The initial conditions become $u(0)=I$ and $v(0)=0$.\n", "\n", "\n", "\n", "## The Forward Euler scheme\n", "\n", "A Forward Euler approximation to our $2\\times 2$ system of ODEs\n", "([24](#vib:model2x2:ueq))-([25](#vib:model2x2:veq)) becomes" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "\n", "
\n", "\n", "$$\n", "\\begin{equation}\n", "\\lbrack D_t^+ u = v\\rbrack^n,\n", "\\label{_auto9} \\tag{26}\n", "\\end{equation}\n", "$$" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "\n", "
\n", "\n", "$$\n", "\\begin{equation} \n", "\\lbrack D_t^+ v = -\\omega^2 u\\rbrack^n,\n", "\\label{_auto10} \\tag{27}\n", "\\end{equation}\n", "$$" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "or written out," ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "\n", "
\n", "\n", "$$\n", "\\begin{equation}\n", "u^{n+1} = u^n + \\Delta t v^n,\n", "\\label{vib:undamped:FE1} \\tag{28}\n", "\\end{equation}\n", "$$" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "\n", "
\n", "\n", "$$\n", "\\begin{equation} \n", "v^{n+1} = v^n -\\Delta t \\omega^2 u^n\n", "\\label{vib:undamped:FE2} \\tag{29}\n", "\\thinspace .\n", "\\end{equation}\n", "$$" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Let us briefly compare this Forward Euler method with the centered\n", "difference scheme for the second-order differential equation. We have\n", "from ([28](#vib:undamped:FE1)) and ([29](#vib:undamped:FE2)) applied at\n", "levels $n$ and $n-1$ that" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "$$\n", "u^{n+1} = u^n + \\Delta t v^n = u^n + \\Delta t (v^{n-1} -\\Delta t \\omega^2 u^{n-1})\\thinspace .\n", "$$" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Since from ([28](#vib:undamped:FE1))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "$$\n", "v^{n-1} = \\frac{1}{\\Delta t}(u^{n}-u^{n-1}),\n", "$$" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "it follows that" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "$$\n", "u^{n+1} = 2u^n - u^{n-1} -\\Delta t^2\\omega^2 u^{n-1},\n", "$$" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "which is very close to the centered difference scheme, but the last\n", "term is evaluated at $t_{n-1}$ instead of $t_n$. Rewriting, so that\n", "$\\Delta t^2\\omega^2u^{n-1}$ appears alone on the right-hand side, and\n", "then dividing by $\\Delta t^2$, the new left-hand side is an\n", "approximation to $u^{\\prime\\prime}$ at $t_n$, while the right-hand\n", "side is sampled at $t_{n-1}$. All terms should be sampled at the same\n", "mesh point, so using $\\omega^2 u^{n-1}$ instead of $\\omega^2 u^n$\n", "points to a kind of mathematical error in the derivation of the\n", "scheme. This error turns out to be rather crucial for the accuracy of\n", "the Forward Euler method applied to vibration problems (the section [Comparison of schemes](#vib:model2x2:compare) has examples).\n", "\n", "The reasoning above does not imply that the Forward Euler scheme is not\n", "correct, but more that it is almost equivalent to a second-order accurate\n", "scheme for the second-order ODE formulation, and that the error\n", "committed has to do with a wrong sampling point.\n", "\n", "## The Backward Euler scheme\n", "\n", "A Backward Euler approximation to the ODE system is equally easy to\n", "write up in the operator notation:" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "\n", "
\n", "\n", "$$\n", "\\begin{equation}\n", "\\lbrack D_t^- u = v\\rbrack^{n+1},\n", "\\label{_auto11} \\tag{30}\n", "\\end{equation}\n", "$$" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "\n", "
\n", "\n", "$$\n", "\\begin{equation} \n", "\\lbrack D_t^- v = -\\omega u\\rbrack^{n+1} \\thinspace .\n", "\\label{_auto12} \\tag{31}\n", "\\end{equation}\n", "$$" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "This becomes a coupled system for $u^{n+1}$ and $v^{n+1}$:" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "\n", "
\n", "\n", "$$\n", "\\begin{equation}\n", "u^{n+1} - \\Delta t v^{n+1} = u^{n},\n", "\\label{vib:undamped:BE1} \\tag{32}\n", "\\end{equation}\n", "$$" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "\n", "
\n", "\n", "$$\n", "\\begin{equation} \n", "v^{n+1} + \\Delta t \\omega^2 u^{n+1} = v^{n}\n", "\\label{vib:undamped:BE2} \\tag{33}\n", "\\thinspace .\n", "\\end{equation}\n", "$$" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We can compare ([32](#vib:undamped:BE1))-([33](#vib:undamped:BE2)) with\n", "the centered scheme ([7](#vib:ode1:step4)) for the second-order\n", "differential equation. To this end, we eliminate $v^{n+1}$ in\n", "([32](#vib:undamped:BE1)) using ([33](#vib:undamped:BE2)) solved with\n", "respect to $v^{n+1}$. Thereafter, we eliminate $v^n$ using\n", "([32](#vib:undamped:BE1)) solved with respect to $v^{n+1}$ and also\n", "replacing $n+1$ by $n$ and $n$ by $n-1$. The resulting equation\n", "involving only $u^{n+1}$, $u^n$, and $u^{n-1}$ can be ordered as" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "$$\n", "\\frac{u^{n+1}-2u^n+u^{n-1}}{\\Delta t^2} = -\\omega^2 u^{n+1},\n", "$$" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "which has almost the same form as the centered scheme for the\n", "second-order differential equation, but the right-hand side is\n", "evaluated at $u^{n+1}$ and not $u^n$. This inconsistent sampling\n", "of terms has a dramatic effect on the numerical solution, as we\n", "demonstrate in the section [Comparison of schemes](#vib:model2x2:compare).\n", "\n", "## The Crank-Nicolson scheme\n", "
\n", "\n", "The Crank-Nicolson scheme takes this form in the operator notation:" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "\n", "
\n", "\n", "$$\n", "\\begin{equation}\n", "\\lbrack D_t u = \\overline{v}^t\\rbrack^{n+\\frac{1}{2}},\n", "\\label{_auto13} \\tag{34}\n", "\\end{equation}\n", "$$" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "\n", "
\n", "\n", "$$\n", "\\begin{equation} \n", "\\lbrack D_t v = -\\omega^2 \\overline{u}^t\\rbrack^{n+\\frac{1}{2}}\n", "\\thinspace .\n", "\\label{_auto14} \\tag{35}\n", "\\end{equation}\n", "$$" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Writing the equations out and rearranging terms,\n", "shows that this is also a coupled system of two linear equations\n", "at each time level:" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "\n", "
\n", "\n", "$$\n", "\\begin{equation}\n", "u^{n+1} - \\frac{1}{2}\\Delta t v^{n+1} = u^{n} + \\frac{1}{2}\\Delta t v^{n},\n", "\\label{_auto15} \\tag{36}\n", "\\end{equation}\n", "$$" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "\n", "
\n", "\n", "$$\n", "\\begin{equation} \n", "v^{n+1} + \\frac{1}{2}\\Delta t \\omega^2 u^{n+1} = v^{n}\n", "- \\frac{1}{2}\\Delta t \\omega^2 u^{n}\n", "\\thinspace .\n", "\\label{_auto16} \\tag{37}\n", "\\end{equation}\n", "$$" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We may compare also this scheme to the centered discretization of\n", "the second-order ODE. It turns out that the Crank-Nicolson scheme is\n", "equivalent to the discretization" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "\n", "
\n", "\n", "$$\n", "\\begin{equation}\n", "\\frac{u^{n+1} - 2u^n + u^{n-1}}{\\Delta t^2} = - \\omega^2\n", "\\frac{1}{4}(u^{n+1} + 2u^n + u^{n-1}) = -\\omega^2 u^{n} +\n", "\\mathcal{O}{\\Delta t^2}\\thinspace .\n", "\\label{vib:undamped:CN:equiv_utt} \\tag{38}\n", "\\end{equation}\n", "$$" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "That is, the Crank-Nicolson is equivalent to ([7](#vib:ode1:step4))\n", "for the second-order ODE, apart from an extra term of size\n", "$\\Delta t^2$, but this is an error of the same order as in\n", "the finite difference approximation on the left-hand side of the\n", "equation anyway. The fact that the Crank-Nicolson scheme is so\n", "close to ([7](#vib:ode1:step4)) makes it a much better method than\n", "the Forward or Backward Euler methods for vibration problems,\n", "as will be illustrated in the section [Comparison of schemes](#vib:model2x2:compare).\n", "\n", "Deriving ([38](#vib:undamped:CN:equiv_utt)) is a bit tricky.\n", "We start with rewriting the Crank-Nicolson equations as follows" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "\n", "
\n", "\n", "$$\n", "\\begin{equation}\n", "u^{n+1} - u^n = \\frac{1}{2}\\Delta t(v^{n+1} + v^n),\n", "\\label{vib:undamped:CN3a} \\tag{39}\n", "\\end{equation}\n", "$$" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "\n", "
\n", "\n", "$$\n", "\\begin{equation} \n", "v^{n+1} = v^n -\\frac{1}{2}\\Delta t\\omega^2 (u^{n+1} + u^n),\n", "\\label{vib:undamped:CN4a} \\tag{40}\n", "\\end{equation}\n", "$$" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "and add the latter at the previous time level as well:" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "\n", "
\n", "\n", "$$\n", "\\begin{equation}\n", "v^{n} = v^{n-1} -\\frac{1}{2}\\Delta t\\omega^2(u^{n} + u^{n-1})\n", "\\label{vib:undamped:CN4b1} \\tag{41}\n", "\\end{equation}\n", "$$" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We can also rewrite ([39](#vib:undamped:CN3a)) at the previous time level\n", "as" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "\n", "
\n", "\n", "$$\n", "\\begin{equation}\n", "v^{n} + v^{n-1} = \\frac{2}{\\Delta t}(u^{n} - u^{n-1})\\thinspace .\n", "\\label{vib:undamped:CN4b} \\tag{42}\n", "\\end{equation}\n", "$$" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Inserting ([40](#vib:undamped:CN4a)) for $v^{n+1}$ in\n", "([39](#vib:undamped:CN3a)) and\n", "([41](#vib:undamped:CN4b1)) for $v^{n}$ in\n", "([39](#vib:undamped:CN3a)) yields after some reordering:" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "$$\n", "u^{n+1} - u^n = \\frac{1}{2}(-\\frac{1}{2}\\Delta t\\omega^2\n", "(u^{n+1} + 2u^n + u^{n-1}) + v^n + v^{n-1})\\thinspace .\n", "$$" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Now, $v^n + v^{n-1}$ can be eliminated by means of\n", "([42](#vib:undamped:CN4b)). The result becomes" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "\n", "
\n", "\n", "$$\n", "\\begin{equation}\n", "u^{n+1} - 2u^n + u^{n-1} = -\\Delta t^2\\omega^2\n", "\\frac{1}{4}(u^{n+1} + 2u^n + u^{n-1})\\thinspace .\n", "\\label{vib:undamped:CN5} \\tag{43}\n", "\\end{equation}\n", "$$" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "It can be shown that" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "$$\n", "\\frac{1}{4}(u^{n+1} + 2u^n + u^{n-1}) \\approx u^n + \\mathcal{O}{\\Delta t^2},\n", "$$" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "meaning that ([43](#vib:undamped:CN5)) is an approximation to\n", "the centered scheme ([7](#vib:ode1:step4)) for the second-order ODE where\n", "the sampling error in the term $\\Delta t^2\\omega^2 u^n$ is of the same\n", "order as the approximation errors in the finite differences, i.e.,\n", "$\\mathcal{O}{\\Delta t^2}$. The Crank-Nicolson scheme written as\n", "([43](#vib:undamped:CN5)) therefore has consistent sampling of all\n", "terms at the same time point $t_n$.\n", "\n", "\n", "## Comparison of schemes\n", "
\n", "\n", "\n", "We can easily compare methods like the ones above (and many more!)\n", "with the aid of the\n", "[Odespy](https://github.com/hplgit/odespy) package. Below is\n", "a sketch of the code." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "```python\n", "import odespy\n", "import numpy as np\n", "\n", "def f(u, t, w=1):\n", " # v, u numbering for EulerCromer to work well\n", " v, u = u # u is array of length 2 holding our [v, u]\n", " return [-w**2*u, v]\n", "\n", "def run_solvers_and_plot(solvers, timesteps_per_period=20,\n", " num_periods=1, I=1, w=2*np.pi):\n", " P = 2*np.pi/w # duration of one period\n", " dt = P/timesteps_per_period\n", " Nt = num_periods*timesteps_per_period\n", " T = Nt*dt\n", " t_mesh = np.linspace(0, T, Nt+1)\n", "\n", " legends = []\n", " for solver in solvers:\n", " solver.set(f_kwargs={'w': w})\n", " solver.set_initial_condition([0, I])\n", " u, t = solver.solve(t_mesh)\n", "```" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "There is quite some more code dealing with plots also, and we refer\n", "to the source file [`vib_undamped_odespy.py`](https://github.com/devitocodes/devito_book/blob/master/fdm-devito-notebooks/01_vib/src-vib/vib_undamped_odespy.py)\n", "for details. Observe that keyword arguments in `f(u,t,w=1)` can\n", "be supplied through a solver parameter `f_kwargs` (dictionary of\n", "additional keyword arguments to `f`).\n", "\n", "Specification of the Forward Euler, Backward Euler, and\n", "Crank-Nicolson schemes is done like this:" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "```python\n", "solvers = [\n", " odespy.ForwardEuler(f),\n", " # Implicit methods must use Newton solver to converge\n", " odespy.BackwardEuler(f, nonlinear_solver='Newton'),\n", " odespy.CrankNicolson(f, nonlinear_solver='Newton'),\n", " ]\n", "```" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The `vib_undamped_odespy.py` program makes two plots of the computed\n", "solutions with the various methods in the `solvers` list: one plot\n", "with $u(t)$ versus $t$, and one *phase plane plot* where $v$ is\n", "plotted against $u$. That is, the phase plane plot is the curve\n", "$(u(t),v(t))$ parameterized by $t$. Analytically, $u=I\\cos(\\omega t)$\n", "and $v=u^{\\prime}=-\\omega I\\sin(\\omega t)$. The exact curve\n", "$(u(t),v(t))$ is therefore an ellipse, which often looks like a circle\n", "in a plot if the axes are automatically scaled. The important feature,\n", "however, is that the exact curve $(u(t),v(t))$ is closed and repeats\n", "itself for every period. Not all numerical schemes are capable of\n", "doing that, meaning that the amplitude instead shrinks or grows with\n", "time.\n", "\n", "[Figure](#vib:ode1:1st:odespy:theta:phaseplane) show the\n", "results. Note that Odespy applies the label MidpointImplicit for what\n", "we have specified as `CrankNicolson` in the code (`CrankNicolson` is\n", "just a synonym for class `MidpointImplicit` in the Odespy code). The\n", "Forward Euler scheme in [Figure](#vib:ode1:1st:odespy:theta:phaseplane) has a pronounced spiral\n", "curve, pointing to the fact that the amplitude steadily grows, which\n", "is also evident in [Figure](#vib:ode1:1st:odespy:theta). The\n", "Backward Euler scheme has a similar feature, except that the spriral\n", "goes inward and the amplitude is significantly damped. The changing\n", "amplitude and the spiral form decreases with decreasing time step.\n", "The Crank-Nicolson scheme looks much more accurate. In fact, these\n", "plots tell that the Forward and Backward Euler schemes are not\n", "suitable for solving our ODEs with oscillating solutions.\n", "\n", "\n", "\n", "
\n", "\n", "

Comparison of classical schemes in the phase plane for two time step values.

\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "
\n", "\n", "

Comparison of solution curves for classical schemes.

\n", "\n", "\n", "\n", "\n", "\n", "\n", "## Runge-Kutta methods\n", "\n", "We may run two other popular standard methods for first-order ODEs,\n", "the 2nd- and 4th-order Runge-Kutta methods, to see how they\n", "perform. The figures below show the solutions with larger $\\Delta t$\n", "values than what was used in the previous two plots.\n", "\n", "\n", "\n", "
\n", "\n", "

Comparison of Runge-Kutta schemes in the phase plane.

\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "
\n", "\n", "

Comparison of Runge-Kutta schemes.

\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "The visual impression is that the 4th-order Runge-Kutta method is very\n", "accurate, under all circumstances in these tests, while the 2nd-order\n", "scheme suffers from amplitude errors unless the time step is very\n", "small.\n", "\n", "\n", "The corresponding results for the Crank-Nicolson scheme are shown in\n", "[Figure](#vib:ode1:1st:odespy:CN:long:phaseplane). It is clear that\n", "the Crank-Nicolson scheme outhinspace .erforms the 2nd-order Runge-Kutta\n", "method. Both schemes have the same order of accuracy $\\mathcal{O}{\\Delta\n", "t^2}$, but their differences in the accuracy that matters in a real\n", "physical application is very clearly pronounced in this example.\n", "[Exercise 13: Investigate the amplitude errors of many solvers](#vib:exer:undamped:odespy) invites you to investigate how\n", "the amplitude is computed by a series of famous methods for\n", "first-order ODEs.\n", "\n", "\n", "\n", "
\n", "\n", "

Long-time behavior of the Crank-Nicolson scheme in the phase plane.

\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "## Analysis of the Forward Euler scheme\n", "\n", "We may try to find exact solutions of the discrete equations\n", "([28](#vib:undamped:FE1))-([29](#vib:undamped:FE2)) in the Forward Euler\n", "method to better understand why this otherwise useful method has so\n", "bad performance for vibration ODEs. An \"ansatz\" for the solution of\n", "the discrete equations is" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "$$\n", "\\begin{align*}\n", "u^n &= IA^n,\\\\ \n", "v^n &= qIA^n,\n", "\\end{align*}\n", "$$" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "where $q$ and $A$ are scalars to be determined. We could have used a complex\n", "exponential form $e^{i\\tilde\\omega n\\Delta t}$ since we get\n", "oscillatory solutions, but the oscillations grow in the Forward Euler\n", "method, so the numerical frequency $\\tilde\\omega$ will be complex\n", "anyway (producing an exponentially growing amplitude). Therefore, it is\n", "easier to just work with potentially complex $A$ and $q$ as introduced\n", "above.\n", "\n", "The Forward Euler scheme leads to" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "$$\n", "\\begin{align*}\n", "A &= 1 + \\Delta t q,\\\\ \n", "A &= 1 - \\Delta t\\omega^2 q^{-1}\\thinspace .\n", "\\end{align*}\n", "$$" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We can easily eliminate $A$, get $q^2 + \\omega^2=0$, and solve for" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "$$\n", "q = \\pm i\\omega,\n", "$$" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "which gives" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "$$\n", "A = 1 \\pm \\Delta t i\\omega\\thinspace .\n", "$$" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We shall take the real part of $A^n$ as the solution. The two values\n", "of $A$ are complex conjugates, and the real part of $A^n$ will be the\n", "same for both roots. This is easy to realize if we rewrite the complex\n", "numbers in polar form, which is also convenient for further analysis\n", "and understanding. The polar form $re^{i\\theta}$ of a complex number\n", "$x+iy$ has $r=\\sqrt{x^2+y^2}$ and $\\theta = \\tan^{-1}(y/x)$. Hence,\n", "the polar form of the two values for $A$ becomes" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "$$\n", "1 \\pm \\Delta t i\\omega = \\sqrt{1+\\omega^2\\Delta t^2}e^{\\pm i\\tan^{-1}(\\omega\\Delta t)}\\thinspace .\n", "$$" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Now it is very easy to compute $A^n$:" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "$$\n", "(1 \\pm \\Delta t i\\omega)^n = (1+\\omega^2\\Delta t^2)^{n/2}e^{\\pm ni\\tan^{-1}(\\omega\\Delta t)}\\thinspace .\n", "$$" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Since $\\cos (\\theta n) = \\cos (-\\theta n)$, the real parts of the two\n", "numbers become the same. We therefore continue with the solution that has\n", "the plus sign.\n", "\n", "The general solution is $u^n = CA^n$, where $C$ is a constant\n", "determined from the initial condition: $u^0=C=I$. We have $u^n=IA^n$\n", "and $v^n=qIA^n$. The final solutions are just the real part of the\n", "expressions in polar form:" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "\n", "
\n", "\n", "$$\n", "\\begin{equation}\n", "u^n =\n", "I(1+\\omega^2\\Delta t^2)^{n/2}\\cos (n\\tan^{-1}(\\omega\\Delta t)),\n", "\\label{_auto17} \\tag{44}\n", "\\end{equation}\n", "$$" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "\n", "
\n", "\n", "$$\n", "\\begin{equation} \n", "v^n =- \\omega\n", "I(1+\\omega^2\\Delta t^2)^{n/2}\\sin (n\\tan^{-1}(\\omega\\Delta t))\\thinspace .\n", "\\label{_auto18} \\tag{45}\n", "\\end{equation}\n", "$$" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The expression $(1+\\omega^2\\Delta t^2)^{n/2}$ causes growth of\n", "the amplitude, since a number greater than one is raised to a positive\n", "exponent $n/2$. We can develop a series expression to better understand\n", "the formula for the amplitude. Introducing $p=\\omega\\Delta t$ as the\n", "key variable and using `sympy` gives" ] }, { "cell_type": "code", "execution_count": 15, "metadata": {}, "outputs": [ { "data": { "text/latex": [ "$\\displaystyle 1 + \\frac{n p^{2}}{2} + O\\left(p^{4}\\right)$" ], "text/plain": [ "1 + n*p**2/2 + O(p**4)" ] }, "execution_count": 15, "metadata": {}, "output_type": "execute_result" } ], "source": [ "from sympy import *\n", "p = symbols('p', real=True)\n", "n = symbols('n', integer=True, positive=True)\n", "amplitude = (1 + p**2)**(n/2)\n", "amplitude.series(p, 0, 4)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The amplitude goes like $1 + \\frac{1}{2} n\\omega^2\\Delta t^2$, clearly growing\n", "linearly in time (with $n$).\n", "\n", "We can also investigate the error in the angular frequency by a\n", "series expansion:" ] }, { "cell_type": "code", "execution_count": 16, "metadata": {}, "outputs": [ { "data": { "text/latex": [ "$\\displaystyle n \\left(p - \\frac{p^{3}}{3} + O\\left(p^{4}\\right)\\right)$" ], "text/plain": [ "n*(p - p**3/3 + O(p**4))" ] }, "execution_count": 16, "metadata": {}, "output_type": "execute_result" } ], "source": [ "n*atan(p).series(p, 0, 4)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "This means that the solution for $u^n$ can be written as" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "$$\n", "u^n = (1 + \\frac{1}{2} n\\omega^2\\Delta t^2 + \\mathcal{O}{\\Delta t^4})\n", "\\cos\\left(\\omega t - \\frac{1}{3}\\omega t\\Delta t^2 + \\mathcal{O}{\\Delta t^4}\\right)\n", "\\thinspace .\n", "$$" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The error in the angular frequency is of the same order as in the\n", "scheme ([7](#vib:ode1:step4)) for the second-order ODE, but the error\n", "in the amplitude is severe.\n", "\n", "\n", "# Energy considerations\n", "
\n", "\n", "\n", "The observations of various methods in the previous section can be\n", "better interpreted if we compute a quantity reflecting\n", "the total *energy of the system*. mathcal{I}_t turns out that this quantity," ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "$$\n", "E(t) = \\frac{1}{2}(u^{\\prime})^2 + \\frac{1}{2}\\omega^2u^2,\n", "$$" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "is *constant* for all $t$. Checking that $E(t)$ really remains constant\n", "brings evidence that the numerical computations are sound.\n", "mathcal{I}_t turns out that $E$ is proportional to the mechanical energy\n", "in the system. Conservation of energy is\n", "much used to check numerical simulations, so it is well invested time to\n", "dive into this subject.\n", "\n", "## Derivation of the energy expression\n", "
\n", "\n", "\n", "We start out with multiplying" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "$$\n", "u^{\\prime\\prime} + \\omega^2 u = 0,\n", "$$" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "by $u^{\\prime}$ and integrating from $0$ to $T$:" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "$$\n", "\\int_0^T u^{\\prime\\prime}u^{\\prime} dt + \\int_0^T\\omega^2 u u^{\\prime} dt = 0\\thinspace .\n", "$$" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Observing that" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "$$\n", "u^{\\prime\\prime}u^{\\prime} = \\frac{d}{dt}\\frac{1}{2}(u^{\\prime})^2,\\quad uu^{\\prime} = \\frac{d}{dt} {\\frac{1}{2}}u^2,\n", "$$" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "we get" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "$$\n", "\\int_0^T (\\frac{d}{dt}\\frac{1}{2}(u^{\\prime})^2 + \\frac{d}{dt} \\frac{1}{2}\\omega^2u^2)dt = E(T) - E(0)=0,\n", "$$" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "where we have introduced" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "\n", "
\n", "\n", "$$\n", "\\begin{equation}\n", "E(t) = \\frac{1}{2}(u^{\\prime})^2 + \\frac{1}{2}\\omega^2u^2\\thinspace .\n", "\\label{vib:model1:energy:balance1} \\tag{46}\n", "\\end{equation}\n", "$$" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The important result from this derivation is that the total energy\n", "is constant:" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "$$\n", "E(t) = E(0)\\thinspace .\n", "$$" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "**$E(t)$ is closely related to the system's energy.**\n", "\n", "The quantity $E(t)$ derived above is physically not the mechanical energy of a\n", "vibrating mechanical system, but the energy per unit mass. To see this,\n", "we start with Newton's second law $F=ma$ ($F$ is the sum of forces, $m$\n", "is the mass of the system, and $a$ is the acceleration).\n", "The displacement $u$ is related to $a$ through\n", "$a=u^{\\prime\\prime}$. With a spring force as the only force we have $F=-ku$, where\n", "$k$ is a spring constant measuring the stiffness of the spring.\n", "Newton's second law then implies the differential equation" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "$$\n", "-ku = mu^{\\prime\\prime}\\quad\\Rightarrow mu^{\\prime\\prime} + ku = 0\\thinspace .\n", "$$" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "This equation of motion can be turned into an energy balance equation\n", "by finding the work done by each term during a time interval $[0,T]$.\n", "To this end, we multiply the equation by $du=u^{\\prime}dt$ and integrate:" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "$$\n", "\\int_0^T muu^{\\prime}dt + \\int_0^T kuu^{\\prime}dt = 0\\thinspace .\n", "$$" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The result is" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "$$\n", "\\tilde E(t) = E_k(t) + E_p(t) = 0,\n", "$$" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "where" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "\n", "
\n", "\n", "$$\n", "\\begin{equation}\n", "E_k(t) = \\frac{1}{2}mv^2,\\quad v=u^{\\prime},\n", "\\label{vib:model1:energy:kinetic} \\tag{47}\n", "\\end{equation}\n", "$$" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "is the *kinetic energy* of the system, and" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "\n", "
\n", "\n", "$$\n", "\\begin{equation}\n", "E_p(t) = {\\frac{1}{2}}ku^2\n", "\\label{vib:model1:energy:potential} \\tag{48}\n", "\\end{equation}\n", "$$" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "is the *potential energy*. The sum $\\tilde E(t)$ is the total mechanical energy.\n", "The derivation demonstrates the famous energy principle that, under\n", "the right physical circumstances, any\n", "change in the kinetic energy is due to a change in potential energy\n", "and vice versa. (This principle breaks down when we introduce damping\n", "in the system, as we do in the section [Generalization: damping, nonlinearities, and\n", "excitation](vib_gen.ipynb#vib:model2).)\n", "\n", "The equation $mu^{\\prime\\prime}+ku=0$ can be divided by $m$ and written as\n", "$u^{\\prime\\prime} + \\omega^2u=0$ for $\\omega=\\sqrt{k/m}$. The energy expression\n", "$E(t)=\\frac{1}{2}(u^{\\prime})^2 + \\frac{1}{2}\\omega^2u^2$ derived earlier is then\n", "$\\tilde E(t)/m$, i.e., mechanical energy per unit mass.\n", "\n", "\n", "\n", "### Energy of the exact solution\n", "\n", "Analytically, we have $u(t)=I\\cos\\omega t$, if $u(0)=I$ and $u^{\\prime}(0)=0$,\n", "so we can easily check the energy evolution and confirm that $E(t)$\n", "is constant:" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "$$\n", "E(t) = {\\frac{1}{2}}I^2 (-\\omega\\sin\\omega t)^2\n", "+ \\frac{1}{2}\\omega^2 I^2 \\cos^2\\omega t\n", "= \\frac{1}{2}\\omega^2 (\\sin^2\\omega t + \\cos^2\\omega t) = \\frac{1}{2}\\omega^2\n", "\\thinspace .\n", "$$" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Growth of energy in the Forward Euler scheme\n", "\n", "mathcal{I}_t is easy to show that the energy in the Forward Euler scheme increases\n", "when stepping from time level $n$ to $n+1$." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "$$\n", "\\begin{align*}\n", "E^{n+1} &= \\frac{1}{2}(v^{n+1})^2 + \\frac{1}{2}\\omega^2 (u^{n+1})^2\\\\ \n", "&= \\frac{1}{2}(v^n - \\omega^2\\Delta t u^n)^2 + \\frac{1}{2}\\omega^2(u^n + \\Delta t v^n)^2\\\\ \n", "&= (1 + \\Delta t^2\\omega^2)E^n\\thinspace .\n", "\\end{align*}\n", "$$" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## An error measure based on energy\n", "
\n", "\n", "\n", "The constant energy is well expressed by its initial value $E(0)$, so that\n", "the error in mechanical energy can be computed as a mesh function by" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "\n", "
\n", "\n", "$$\n", "\\begin{equation}\n", "e_E^n = \\frac{1}{2}\\left(\\frac{u^{n+1}-u^{n-1}}{2\\Delta t}\\right)^2\n", "+ \\frac{1}{2}\\omega^2 (u^n)^2 - E(0),\n", "\\quad n=1,\\ldots,N_t-1,\n", "\\label{_auto19} \\tag{49}\n", "\\end{equation}\n", "$$" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "where" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "$$\n", "E(0) = {\\frac{1}{2}}V^2 + \\frac{1}{2}\\omega^2I^2,\n", "$$" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "if $u(0)=I$ and $u^{\\prime}(0)=V$. Note that we have used a centered\n", "approximation to $u^{\\prime}$: $u^{\\prime}(t_n)\\approx [D_{2t}u]^n$.\n", "\n", "A useful norm of the mesh function $e_E^n$ for the discrete mechanical\n", "energy can be the maximum absolute value of $e_E^n$:" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "$$\n", "||e_E^n||_{\\ell^\\infty} = \\max_{1\\leq n \n", "\n", " Method $T$ $\\Delta t$ $\\max \\left\\vert e_E^n\\right\\vert/e_E^0$ \n", "\n", "\n", " Forward Euler $1$ $0.025$ $1.678\\cdot 10^{0}$ \n", " Backward Euler $1$ $0.025$ $6.235\\cdot 10^{-1}$ \n", " Crank-Nicolson $1$ $0.025$ $1.221\\cdot 10^{-2}$ \n", " Runge-Kutta 2nd-order $1$ $0.025$ $6.076\\cdot 10^{-3}$ \n", " Runge-Kutta 4th-order $1$ $0.025$ $8.214\\cdot 10^{-3}$ \n", "\n", "\n", "However, after 10 periods, the picture is much more dramatic:\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "
Method $T$ $\\Delta t$ $\\max \\left\\vert e_E^n\\right\\vert/e_E^0$
Forward Euler $10$ $0.025$ $1.788\\cdot 10^{4}$
Backward Euler $10$ $0.025$ $1.000\\cdot 10^{0}$
Crank-Nicolson $10$ $0.025$ $1.221\\cdot 10^{-2}$
Runge-Kutta 2nd-order $10$ $0.025$ $6.250\\cdot 10^{-2}$
Runge-Kutta 4th-order $10$ $0.025$ $8.288\\cdot 10^{-3}$
\n", "The Runge-Kutta and Crank-Nicolson methods hardly change their energy\n", "error with $T$, while the error in the Forward Euler method grows to\n", "huge levels and a relative error of 1 in the Backward Euler method\n", "points to $E(t)\\rightarrow 0$ as $t$ grows large.\n", "\n", "Running multiple values of $\\Delta t$, we can get some insight into\n", "the convergence of the energy error:\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "
Method $T$ $\\Delta t$ $\\max \\left\\vert e_E^n\\right\\vert/e_E^0$
Forward Euler $10$ $0.05$ $1.120\\cdot 10^{8}$
Forward Euler $10$ $0.025$ $1.788\\cdot 10^{4}$
Forward Euler $10$ $0.0125$ $1.374\\cdot 10^{2}$
Backward Euler $10$ $0.05$ $1.000\\cdot 10^{0}$
Backward Euler $10$ $0.025$ $1.000\\cdot 10^{0}$
Backward Euler $10$ $0.0125$ $9.928\\cdot 10^{-1}$
Crank-Nicolson $10$ $0.05$ $4.756\\cdot 10^{-2}$
Crank-Nicolson $10$ $0.025$ $1.221\\cdot 10^{-2}$
Crank-Nicolson $10$ $0.0125$ $3.125\\cdot 10^{-3}$
Runge-Kutta 2nd-order $10$ $0.05$ $6.152\\cdot 10^{-1}$
Runge-Kutta 2nd-order $10$ $0.025$ $6.250\\cdot 10^{-2}$
Runge-Kutta 2nd-order $10$ $0.0125$ $7.631\\cdot 10^{-3}$
Runge-Kutta 4th-order $10$ $0.05$ $3.510\\cdot 10^{-2}$
Runge-Kutta 4th-order $10$ $0.025$ $8.288\\cdot 10^{-3}$
Runge-Kutta 4th-order $10$ $0.0125$ $2.058\\cdot 10^{-3}$
\n", "A striking fact from this table is that the error of the Forward Euler\n", "method is reduced by the same factor as $\\Delta t$ is reduced by,\n", "while the error in the Crank-Nicolson method has a reduction\n", "proportional to $\\Delta t^2$ (we cannot say anything for the Backward\n", "Euler method). However, for the RK2 method, halving $\\Delta t$ reduces\n", "the error by almost a factor of 10 (!), and for the RK4 method the\n", "reduction seems proportional to $\\Delta t^2$ only (and the trend is\n", "confirmed by running smaller time steps, so for $\\Delta t = 3.9\\cdot\n", "10^{-4}$ the relative error of RK2 is a factor 10 smaller than that of\n", "RK4!).\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "# The Euler-Cromer method\n", "
\n", "\n", "While the Runge-Kutta methods and the Crank-Nicolson scheme work well\n", "for the vibration equation modeled as a first-order ODE system, both\n", "were inferior to the straightforward centered difference scheme for\n", "the second-order equation $u^{\\prime\\prime}+\\omega^2u=0$. However,\n", "there is a similarly successful scheme available for the first-order\n", "system $u^{\\prime}=v$, $v^{\\prime}=-\\omega^2u$, to be presented below.\n", "The ideas of the scheme and their further developments have become\n", "very popular in particle and rigid body dynamics and hence are widely\n", "used by physicists.\n", "\n", "\n", "\n", "## Forward-backward discretization\n", "\n", "The idea is to apply a Forward Euler discretization to the first\n", "equation and a Backward Euler discretization to the second. In\n", "operator notation this is stated as" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "\n", "
\n", "\n", "$$\n", "\\begin{equation}\n", "\\lbrack D_t^+u = v\\rbrack^n,\n", "\\label{_auto20} \\tag{50}\n", "\\end{equation}\n", "$$" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "\n", "
\n", "\n", "$$\n", "\\begin{equation} \n", "\\lbrack D_t^-v = -\\omega^2 u\\rbrack^{n+1}\n", "\\thinspace .\n", "\\label{_auto21} \\tag{51}\n", "\\end{equation}\n", "$$" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We can write out the formulas and collect the unknowns on the left-hand side:" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "\n", "
\n", "\n", "$$\n", "\\begin{equation}\n", "u^{n+1} = u^n + \\Delta t v^n,\n", "\\label{vib:model2x2:EulerCromer:ueq1} \\tag{52}\n", "\\end{equation}\n", "$$" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "\n", "
\n", "\n", "$$\n", "\\begin{equation} \n", "v^{n+1} = v^n -\\Delta t \\omega^2u^{n+1}\n", "\\label{vib:model2x2:EulerCromer:veq1} \\tag{53}\n", "\\thinspace .\n", "\\end{equation}\n", "$$" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We realize that after $u^{n+1}$ has been computed from\n", "([52](#vib:model2x2:EulerCromer:ueq1)), it may be used directly\n", "in\n", "([53](#vib:model2x2:EulerCromer:veq1)) to compute $v^{n+1}$.\n", "\n", "In physics, it is more common to update the $v$ equation first, with a\n", "forward difference, and thereafter the $u$ equation, with a backward\n", "difference that applies the most recently computed $v$ value:" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "\n", "
\n", "\n", "$$\n", "\\begin{equation}\n", "v^{n+1} = v^n -\\Delta t \\omega^2u^{n},\n", "\\label{vib:model2x2:EulerCromer:veq1b} \\tag{54}\n", "\\end{equation}\n", "$$" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "\n", "
\n", "\n", "$$\n", "\\begin{equation} \n", "u^{n+1} = u^n + \\Delta t v^{n+1}\\thinspace .\n", "\\label{vib:model2x2:EulerCromer:ueq1b} \\tag{55}\n", "\\end{equation}\n", "$$" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The advantage of ordering the ODEs as in\n", "([54](#vib:model2x2:EulerCromer:veq1b))-([55](#vib:model2x2:EulerCromer:ueq1b))\n", "becomes evident when considering complicated models. Such models are\n", "included if we write our vibration ODE more generally as" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "$$\n", "u^{\\prime\\prime} + g(u, u^{\\prime}, t)=0\\thinspace .\n", "$$" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We can rewrite this second-order ODE as two first-order ODEs," ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "$$\n", "\\begin{align*}\n", "v^{\\prime} &= -g(u,v,t),\\\\ \n", "u^{\\prime} &= v\\thinspace .\n", "\\end{align*}\n", "$$" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "This rewrite allows the following scheme to be used:" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "$$\n", "\\begin{align*}\n", "v^{n+1} &= v^n -\\Delta t\\, g(u^n,v^n,t),\\\\ \n", "u^{n+1} &= u^n + \\Delta t\\, v^{n+1}\\thinspace .\n", "\\end{align*}\n", "$$" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We realize that the first update works well with any $g$ since old\n", "values $u^n$ and $v^n$ are used. Switching the equations would\n", "demand $u^{n+1}$ and $v^{n+1}$ values in $g$ and result in nonlinear\n", "algebraic equations to be solved at each time level.\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "The scheme\n", "([54](#vib:model2x2:EulerCromer:veq1b))-([55](#vib:model2x2:EulerCromer:ueq1b))\n", "goes under several names: forward-backward scheme, [semi-implicit\n", "Euler method](http://en.wikipedia.org/wiki/Semi-implicit_Euler_method),\n", "semi-explicit Euler, symplectic Euler,\n", "Newton-Stoermer-Verlet,\n", "and Euler-Cromer. We shall stick to the latter name.\n", "\n", "How does the Euler-Cromer method preserve the total energy?\n", "We may run the example from the section [An error measure based on energy](#vib:model1:energy:measure):\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "
Method $T$ $\\Delta t$ $\\max \\left\\vert e_E^n\\right\\vert/e_E^0$
Euler-Cromer $10$ $0.05$ $2.530\\cdot 10^{-2}$
Euler-Cromer $10$ $0.025$ $6.206\\cdot 10^{-3}$
Euler-Cromer $10$ $0.0125$ $1.544\\cdot 10^{-3}$
\n", "The relative error in the total energy decreases as $\\Delta t^2$,\n", "and the error level is slightly lower than for the Crank-Nicolson and\n", "Runge-Kutta methods.\n", "\n", "\n", "## Equivalence with the scheme for the second-order ODE\n", "
\n", "\n", "We shall now show that the Euler-Cromer scheme for the system of\n", "first-order equations is equivalent to the centered finite difference\n", "method for the second-order vibration ODE (!).\n", "\n", "We may eliminate the $v^n$ variable from\n", "([52](#vib:model2x2:EulerCromer:ueq1))-([53](#vib:model2x2:EulerCromer:veq1))\n", "or\n", "([54](#vib:model2x2:EulerCromer:veq1b))-([55](#vib:model2x2:EulerCromer:ueq1b)).\n", "The $v^{n+1}$ term in ([54](#vib:model2x2:EulerCromer:veq1b)) can\n", "be eliminated from ([55](#vib:model2x2:EulerCromer:ueq1b)):" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "\n", "
\n", "\n", "$$\n", "\\begin{equation}\n", "u^{n+1} = u^n + \\Delta t (v^n - \\omega^2\\Delta t u^n)\\thinspace .\n", "\\label{vib:model2x2:EulerCromer:elim1} \\tag{56}\n", "\\end{equation}\n", "$$" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The $v^{n}$ quantity can be expressed by $u^n$ and $u^{n-1}$\n", "using ([55](#vib:model2x2:EulerCromer:ueq1b)):" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "$$\n", "v^{n} = \\frac{u^n - u^{n-1}}{\\Delta t},\n", "$$" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "and when this is inserted in ([56](#vib:model2x2:EulerCromer:elim1)) we get" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "\n", "
\n", "\n", "$$\n", "\\begin{equation}\n", "u^{n+1} = 2u^n - u^{n-1} - \\Delta t^2 \\omega^2u^{n},\n", "\\label{_auto22} \\tag{57}\n", "\\end{equation}\n", "$$" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "which is nothing but the centered scheme ([7](#vib:ode1:step4))! The\n", "two seemingly different numerical methods are mathematically\n", "equivalent. Consequently, the previous analysis of\n", "([7](#vib:ode1:step4)) also applies to the Euler-Cromer method. In\n", "particular, the amplitude is constant, given that the stability\n", "criterion is fulfilled, but there is always an angular frequency error\n", "([19](#vib:ode1:tildeomega:series)). [Exercise 18: Analysis of the Euler-Cromer scheme](#vib:exer:EulerCromer:analysis) gives guidance on how to derive the\n", "exact discrete solution of the two equations in the Euler-Cromer\n", "method.\n", "\n", "Although the Euler-Cromer scheme and the method ([7](#vib:ode1:step4))\n", "are equivalent, there could be differences in the way they handle the\n", "initial conditions. Let us look into this topic. The initial\n", "condition $u^{\\prime}=0$ means $u^{\\prime}=v=0$. From\n", "([54](#vib:model2x2:EulerCromer:veq1b)) we get" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "$$\n", "v^1=v^0-\\Delta t\\omega^2 u^0\n", "=\\Delta t\\omega^2 u^0,\n", "$$" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "and from ([55](#vib:model2x2:EulerCromer:ueq1b))\n", "it follows that" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "$$\n", "u^1=u^0 + \\Delta t v^1 = u^0 - \\omega^2\\Delta t^2 u^0\\thinspace .\n", "$$" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "When we previously used a centered approximation of $u^{\\prime}(0)=0$\n", "combined with the discretization ([7](#vib:ode1:step4)) of the\n", "second-order ODE, we got a slightly different result: $u^1=u^0 -\n", "\\frac{1}{2}\\omega^2\\Delta t^2 u^0$. The difference is\n", "$\\frac{1}{2}\\omega^2\\Delta t^2 u^0$, which is of second order in\n", "$\\Delta t$, seemingly consistent with the overall error in the scheme\n", "for the differential equation model.\n", "\n", "A different view can also be taken. If we approximate\n", "$u^{\\prime}(0)=0$ by a backward difference, $(u^0-u^{-1})/\\Delta t\n", "=0$, we get $u^{-1}=u^0$, and when combined with\n", "([7](#vib:ode1:step4)), it results in $u^1=u^0 - \\omega^2\\Delta t^2\n", "u^0$. This means that the Euler-Cromer method based on\n", "([55](#vib:model2x2:EulerCromer:ueq1b))-([54](#vib:model2x2:EulerCromer:veq1b))\n", "corresponds to using only a first-order approximation to the initial\n", "condition in the method from the section [A centered finite difference scheme](#vib:ode1:fdm).\n", "\n", "Correspondingly, using the formulation\n", "([52](#vib:model2x2:EulerCromer:ueq1))-([53](#vib:model2x2:EulerCromer:veq1))\n", "with $v^n=0$ leads to $u^1=u^0$, which can be interpreted as using a\n", "forward difference approximation for the initial condition\n", "$u^{\\prime}(0)=0$. Both Euler-Cromer formulations lead to slightly\n", "different values for $u^1$ compared to the method in the section [A centered finite difference scheme](#vib:ode1:fdm). The error is $\\frac{1}{2}\\omega^2\\Delta t^2 u^0$.\n", "\n", "## Implementation\n", "
\n", "\n", "### Solver function\n", "\n", "The function below, found in [`vib_undamped_EulerCromer.py`](https://github.com/devitocodes/devito_book/blob/master/fdm-devito-notebooks/01_vib/src-vib/vib_undamped_EulerCromer.py), implements the Euler-Cromer scheme\n", "([54](#vib:model2x2:EulerCromer:veq1b))-([55](#vib:model2x2:EulerCromer:ueq1b)):" ] }, { "cell_type": "code", "execution_count": 50, "metadata": {}, "outputs": [], "source": [ "import numpy as np\n", "from devito import Dimension, TimeFunction, Eq, solve, Operator, Constant" ] }, { "cell_type": "code", "execution_count": 51, "metadata": {}, "outputs": [], "source": [ "# %load -s solver, src-vib/vib_undamped_EulerCromer.py\n", "def solver(I, w, dt, T):\n", " \"\"\"\n", " Solve u'=v, v' = - w**2*u for t in (0,T], u(0)=I and v(0)=0,\n", " by an Euler-Cromer method.\n", " \"\"\"\n", " dt = float(dt)\n", " Nt = int(round(T/dt))\n", " \n", " t = Dimension('t', spacing=Constant('h_t'))\n", " v = TimeFunction(name='v', dimensions=(t,), shape=(Nt+1,), space_order=2)\n", " u = TimeFunction(name='u', dimensions=(t,), shape=(Nt+1,), space_order=2)\n", "\n", " v.data[:] = 0 \n", " u.data[:] = I\n", "\n", " eq_v = Eq(v.dt, -(w**2)*u)\n", " eq_u = Eq(u.dt, v.forward)\n", " \n", " stencil_v = solve(eq_v, v.forward)\n", " stencil_u = solve(eq_u, u.forward)\n", " \n", " update_v = Eq(v.forward, stencil_v)\n", " update_u = Eq(u.forward, stencil_u)\n", " \n", "\n", " op = Operator([update_v, update_u])\n", " op.apply(h_t=dt, t_M=Nt-1)\n", "\n", " return u.data, v.data, np.linspace(0, Nt*dt, Nt+1)\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We can now compare the output from this function with the exact solution to the original second-order vibration ODE using our `visualize` function from before:" ] }, { "cell_type": "code", "execution_count": 53, "metadata": {}, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "Operator `Kernel` run in 0.01 s\n" ] }, { "data": { "image/png": "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\n", "text/plain": [ "
" ] }, "metadata": { "needs_background": "light" }, "output_type": "display_data" } ], "source": [ "I = 1\n", "w = 2*np.pi\n", "dt = 0.05\n", "num_periods = 5\n", "P = 2*np.pi/w # one period\n", "T = P*num_periods\n", "u, v, t = solver(I, w, dt, T)\n", "\n", "visualize(u, t, I, w)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Verification\n", "\n", "Since the Euler-Cromer scheme is equivalent to the finite difference\n", "method for the second-order ODE $u^{\\prime\\prime}+\\omega^2u=0$ (see the section [Equivalence with the scheme for the second-order ODE](#vib:model2x2:EulerCromer:equiv)), the performance of the above\n", "`solver` function is the same as for the `solver` function in the section [Implementation](#vib:impl1). The only difference is the formula for the first time\n", "step, as discussed above. This deviation in the Euler-Cromer scheme\n", "means that the discrete solution listed in the section [Exact discrete solution](#vib:ode1:analysis:sol) is not a solution of the Euler-Cromer\n", "scheme!\n", "\n", "To verify the implementation of the Euler-Cromer method we can adjust\n", "`v[1]` so that the computer-generated values can be compared with the\n", "formula ([20](#vib:ode1:un:exact)) from in the section [Exact discrete solution](#vib:ode1:analysis:sol). This adjustment is done in an alternative\n", "solver function, `solver_ic_fix` in [`vib_undamped_EulerCromer.py`](https://github.com/devitocodes/devito_book/blob/master/fdm-devito-notebooks/01_vib/src-vib/vib_undamped_EulerCromer.py). Since we now\n", "have an exact solution of the discrete equations available, we can\n", "write a test function `test_solver` for checking the equality of\n", "computed values with the formula ([20](#vib:ode1:un:exact)):" ] }, { "cell_type": "code", "execution_count": 54, "metadata": {}, "outputs": [], "source": [ "# %load -s test_solver, src-vib/vib_undamped_EulerCromer.py\n", "def test_solver():\n", " \"\"\"\n", " Test solver with fixed initial condition against\n", " equivalent scheme for the 2nd-order ODE u'' + u = 0.\n", " \"\"\"\n", " I = 1.2; w = 2.0; T = 5\n", " dt = 2/w # longest possible time step\n", " u, v, t = solver_ic_fix(I, w, dt, T)\n", " from vib_undamped import solver as solver2 # 2nd-order ODE\n", " u2, t2 = solver2(I, w, dt, T)\n", " error = np.abs(u - u2).max()\n", " tol = 1E-14\n", " assert error < tol\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Another function, `demo`, visualizes the difference between the\n", "Euler-Cromer scheme and the scheme ([7](#vib:ode1:step4)) for the\n", "second-oder ODE, arising from the mismatch in the first time level.\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Convergence rates\n", "\n", "We may use the `convergence_rates` function in the file\n", "[`vib_undamped.py`](https://github.com/devitocodes/devito_book/blob/master/fdm-devito-notebooks/01_vib/src-vib/vib_undamped.py) to investigate the convergence rate of the\n", "Euler-Cromer method, see the `convergence_rate` function in the file\n", "[`vib_undamped_EulerCromer.py`](https://github.com/devitocodes/devito_book/blob/master/fdm-devito-notebooks/01_vib/src-vib/vib_undamped_EulerCromer.py). Since we could eliminate $v$ to get a\n", "scheme for $u$ that is equivalent to the finite difference method for\n", "the second-order equation in $u$, we would expect the convergence\n", "rates to be the same, i.e., $r = 2$. However,\n", "measuring the convergence rate of $u$ in the Euler-Cromer scheme shows\n", "that $r = 1$ only! Adjusting the initial condition\n", "does not change the rate. Adjusting $\\omega$, as outlined in the section [The error in the numerical frequency](#vib:ode1:analysis:numfreq), gives a 4th-order method there, while\n", "there is no increase in the measured rate in the Euler-Cromer\n", "scheme. It is obvious that the Euler-Cromer scheme is dramatically\n", "much better than the two other first-order methods, Forward Euler and\n", "Backward Euler, but this is not reflected in the convergence rate of\n", "$u$.\n", "\n", "\n", "\n", "\n", "\n", "\n", "## The Stoermer-Verlet algorithm\n", "
\n", "\n", "\n", "\n", "\n", "Another very popular algorithm for vibration problems, especially\n", "for long time simulations, is the\n", "Stoermer-Verlet\n", "algorithm. It has become *the* method among physicists\n", "for molecular simulations as well as particle and rigid body dynamics.\n", "\n", "The method can be derived by applying the Euler-Cromer idea\n", "twice, in a symmetric fashion, during the interval $[t_n,t_{n+1}]$:\n", "\n", "1. solve $v^{\\prime}=-\\omega u$ by a Forward Euler step in $[t_n,t_{n+\\frac{1}{2}}]$\n", "\n", "2. solve $u^{\\prime}=v$ by a Backward Euler step in $[t_n,t_{n+\\frac{1}{2}}]$\n", "\n", "3. solve $u^{\\prime}=v$ by a Forward Euler step in $[t_{n+\\frac{1}{2}}, t_{n+1}]$\n", "\n", "4. solve $v^{\\prime}=-\\omega u$ by a Backward Euler step in $[t_{n+\\frac{1}{2}}, t_{n+1}]$\n", "\n", "With mathematics," ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "$$\n", "\\begin{align*}\n", "\\frac{v^{n+\\frac{1}{2}}-v^n}{\\frac{1}{2}\\Delta t} &= -\\omega^2 u^n,\\\\ \n", "\\frac{u^{n+\\frac{1}{2}}-u^n}{\\frac{1}{2}\\Delta t} &= v^{n+\\frac{1}{2}},\\\\ \n", "\\frac{u^{n+1}-u^{n+\\frac{1}{2}}}{\\frac{1}{2}\\Delta t} &= v^{n+\\frac{1}{2}},\\\\ \n", "\\frac{v^{n+1}-v^{n+\\frac{1}{2}}}{\\frac{1}{2}\\Delta t} &= -\\omega^2 u^{n+1}\\thinspace .\n", "\\end{align*}\n", "$$" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The two steps in the middle can be combined to" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "$$\n", "\\frac{u^{n+1}-u^{n}}{\\Delta t} = v^{n+\\frac{1}{2}},\n", "$$" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "and consequently" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "\n", "
\n", "\n", "$$\n", "\\begin{equation}\n", "v^{n+\\frac{1}{2}} = v^n - \\frac{1}{2}\\Delta t \\omega^2 u^n,\n", "\\label{_auto23} \\tag{58}\n", "\\end{equation}\n", "$$" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "\n", "
\n", "\n", "$$\n", "\\begin{equation} \n", "u^{n+1} = u^{n} + \\Delta t v^{n+\\frac{1}{2}},\n", "\\label{_auto24} \\tag{59}\n", "\\end{equation}\n", "$$" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "\n", "
\n", "\n", "$$\n", "\\begin{equation} \n", "v^{n+1} = v^{n+\\frac{1}{2}} - \\frac{1}{2}\\Delta t \\omega^2 u^{n+1}\\thinspace .\n", "\\label{_auto25} \\tag{60}\n", "\\end{equation}\n", "$$" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Writing the last equation as $v^n = v^{n-\\frac{1}{2}} - \\frac{1}{2}\\Delta\n", "t\\omega^2 u^n$ and using this $v^n$ in the first equation gives\n", "$v^{n+\\frac{1}{2}} = v^{n-\\frac{1}{2}} - \\Delta t\\omega^2 u^n$, and the scheme can\n", "be written as two steps:" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "\n", "
\n", "\n", "$$\n", "\\begin{equation}\n", "v^{n+\\frac{1}{2}} = v^{n-\\frac{1}{2}} - \\Delta t \\omega^2 u^n,\n", "\\label{vib:model2x2:StormerVerlet:eqv} \\tag{61}\n", "\\end{equation}\n", "$$" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "\n", "
\n", "\n", "$$\n", "\\begin{equation} \n", "u^{n+1} = u^{n} + \\Delta t v^{n+\\frac{1}{2}},\n", "\\label{vib:model2x2:StormerVerlet:equ} \\tag{62}\n", "\\end{equation}\n", "$$" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "which is nothing but straightforward centered differences for the\n", "$2\\times 2$ ODE system on a *staggered mesh*, see\n", "the section [The Euler-Cromer scheme on a staggered mesh](#vib:model2x2:staggered). We have thus seen that\n", "four different reasonings (discretizing $u^{\\prime\\prime}+\\omega^2 u$\n", "directly, using Euler-Cromer, using\n", "Stoermer-Verlet,\n", "and using centered differences for the $2\\times 2$ system on a staggered\n", "mesh) all end up with the same equations! The main difference is that\n", "the traditional Euler-Cromer displays first-order convergence in $\\Delta t$\n", "(due to less symmetry in the way $u$ and $v$ are treated)\n", "while the others are $\\mathcal{O}{\\Delta t^2}$ schemes.\n", "\n", "\n", "The most numerically stable scheme, with respect to accumulation of\n", "rounding errors, is\n", "([61](#vib:model2x2:StormerVerlet:eqv))-([62](#vib:model2x2:StormerVerlet:equ)).\n", "It has, according to [[Hairer_Wanner_Norsett_bookI]](#Hairer_Wanner_Norsett_bookI), better\n", "properties in this regard than the direct scheme for the second-order\n", "ODE.\n", "\n", "\n", "\n", "# Staggered mesh\n", "\n", "A more intuitive discretization than the Euler-Cromer method, yet\n", "equivalent, employs solely centered differences in a natural way\n", "for the $2\\times 2$ first-order ODE system.\n", "The scheme is in fact fully equivalent to the second-order scheme for\n", "$u''+\\omega u=0$, also for the first time step.\n", "Such a scheme needs to operate on a *staggered mesh* in time.\n", "Staggered meshes are very popular in many physical application, maybe foremost\n", "fluid dynamics and electromagnetics, so the topic is important to learn.\n", "\n", "\n", "## The Euler-Cromer scheme on a staggered mesh\n", "
\n", "\n", "\n", "In a staggered mesh, the unknowns are sought at different points in\n", "the mesh. Specifically, $u$ is sought at\n", "integer time points $t_n$ and $v$ is sought at $t_{n+1/2}$\n", "*between* two $u$ points.\n", "The unknowns are then $u^1, v^{3/2}, u^2, v^{5/2}$, and so on.\n", "We typically use the notation\n", "$u^n$ and $v^{n+\\frac{1}{2}}$ for the two unknown mesh functions.\n", "[Figure](#staggered:EC:fig1) presents a graphical sketch of two\n", "mesh functions $u$ and $v$ on a staggered mesh.\n", "\n", "\n", "\n", "
\n", "\n", "

Examples on mesh functions on a staggered mesh in time.

\n", "\n", "\n", "\n", "\n", "\n", "\n", "On a staggered mesh it is natural to\n", "use centered difference approximations, expressed\n", "in operator notation as" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "\n", "
\n", "\n", "$$\n", "\\begin{equation}\n", "\\lbrack D_t u = v\\rbrack^{n+\\frac{1}{2}},\n", "\\label{_auto26} \\tag{63}\n", "\\end{equation}\n", "$$" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "\n", "
\n", "\n", "$$\n", "\\begin{equation} \n", "\\lbrack D_t v = -\\omega^2 u\\rbrack^{n+1}\n", "\\thinspace .\n", "\\label{_auto27} \\tag{64}\n", "\\end{equation}\n", "$$" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "or if we switch the sequence of the equations:" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "\n", "
\n", "\n", "$$\n", "\\begin{equation}\n", "\\lbrack D_t v = -\\omega^2 u\\rbrack^{n},\n", "\\label{_auto28} \\tag{65}\n", "\\end{equation}\n", "$$" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "\n", "
\n", "\n", "$$\n", "\\begin{equation} \n", "\\lbrack D_t u = v\\rbrack^{n+\\frac{1}{2}}\n", "\\thinspace .\n", "\\label{_auto29} \\tag{66}\n", "\\end{equation}\n", "$$" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Writing out the formulas gives" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "\n", "
\n", "\n", "$$\n", "\\begin{equation}\n", "v^{n+\\frac{1}{2}} = v^{n-\\frac{1}{2}} -\\Delta t \\omega^2u^{n}\n", "\\label{vib:model2x2:EulerCromer:veq1s2} \\tag{67},\n", "\\end{equation}\n", "$$" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "\n", "
\n", "\n", "$$\n", "\\begin{equation} \n", "u^{n+1} = u^{n} + \\Delta t v^{n+\\frac{1}{2}}\n", "\\label{vib:model2x2:EulerCromer:ueq1s2} \\tag{68}\n", "\\thinspace .\n", "\\end{equation}\n", "$$" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "\n", "\n", "\n", "\n", "We can eliminate the $v$\n", "values and get back the centered scheme based on the second-order\n", "differential equation $u^{\\prime\\prime} +\\omega^2 u = 0$,\n", "so all these three schemes are equivalent.\n", "However, they differ somewhat in the treatment of the initial\n", "conditions.\n", "\n", "Suppose we have $u(0)=I$ and $u'(0)=v(0)=0$ as mathematical\n", "initial conditions. This means $u^0=I$ and" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "$$\n", "v(0)\\approx \\frac{1}{2}(v^{-\\frac{1}{2}} + v^{\\frac{1}{2}}) = 0,\n", "\\quad\\Rightarrow\\quad v^{-\\frac{1}{2}} =- v^\\frac{1}{2}\\thinspace .\n", "$$" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Using the discretized equation ([67](#vib:model2x2:EulerCromer:veq1s2)) for\n", "$n=0$ yields" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "$$\n", "v^\\frac{1}{2} = v^{-\\frac{1}{2}} -\\Delta t\\omega^2 I,\n", "$$" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "and eliminating $v^{-\\frac{1}{2}} =- v^{\\frac{1}{2}}$\n", "results in" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "$$\n", "v^\\frac{1}{2} = -\\frac{1}{2}\\Delta t\\omega^2I,\n", "$$" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "and" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "$$\n", "u^1 = u^0 - \\frac{1}{2}\\Delta t^2\\omega^2 I,\n", "$$" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "which is exactly the same equation for $u^1$ as we had in the\n", "centered scheme based on the second-order differential equation\n", "(and hence corresponds to a centered difference approximation of\n", "the initial condition for $u'(0)$).\n", "The conclusion is that a staggered mesh is fully equivalent with\n", "that scheme, while the forward-backward version gives a slight\n", "deviation in the computation of $u^1$.\n", "\n", "We can redo the derivation of the initial conditions when $u'(0)=V$:" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "$$\n", "v(0)\\approx \\frac{1}{2}(v^{-\\frac{1}{2}} + v^{\\frac{1}{2}}) = V,\n", "\\quad\\Rightarrow\\quad v^{-\\frac{1}{2}} = 2V - v^\\frac{1}{2}\\thinspace .\n", "$$" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Using this $v^{-\\frac{1}{2}}$ in" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "$$\n", "v^\\frac{1}{2} = v^{-\\frac{1}{2}} -\\Delta t\\omega^2 I,\n", "$$" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "then gives $v^\\frac{1}{2} = V - \\frac{1}{2}\\Delta t\\omega^2 I$.\n", "The general initial conditions are therefore" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "\n", "
\n", "\n", "$$\n", "\\begin{equation}\n", "u^0 = I,\n", "\\label{vib:ode2:staggered:u0} \\tag{69}\n", "\\end{equation}\n", "$$" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "\n", "
\n", "\n", "$$\n", "\\begin{equation} \n", "v^\\frac{1}{2} = V - \\frac{1}{2}\\Delta t\\omega^2I\n", "\\label{vib:ode2:staggered:v0} \\tag{70}\\thinspace .\n", "\\end{equation}\n", "$$" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Implementation of the scheme on a staggered mesh\n", "\n", "The algorithm goes like this:\n", "\n", "1. Set the initial values ([69](#vib:ode2:staggered:u0)) and\n", " ([70](#vib:ode2:staggered:v0)).\n", "\n", "2. For $n=1,2,\\ldots$:\n", "\n", "a. Compute $u^{n}$ from ([68](#vib:model2x2:EulerCromer:ueq1s2)).\n", "\n", "b. Compute $v^{n+\\frac{1}{2}}$ from ([67](#vib:model2x2:EulerCromer:veq1s2)).\n", "\n", "\n", "### Implementation with integer indices\n", "\n", "Translating the schemes ([68](#vib:model2x2:EulerCromer:ueq1s2))\n", "and ([67](#vib:model2x2:EulerCromer:veq1s2)) to computer code\n", "faces the problem of how to store and access $v^{n+\\frac{1}{2}}$,\n", "since arrays only allow integer indices with base 0.\n", "We must then introduce a convention: $v^{1+\\frac{1}{2}}$ is stored\n", "in `v[n]` while $v^{1-\\frac{1}{2}}$ is stored in `v[n-1]`.\n", "We can then write the algorithm in Python as" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# %load -s solver_v1, src-vib/vib_undamped_staggered.py\n", "def solver_v1(I, w, dt, T):\n", " \"\"\"\n", " Solve u'=v, v' = - w**2*u for t in (0,T], u(0)=I and v(0)=0,\n", " by a central finite difference method with time step dt.\n", " \"\"\"\n", " dt = float(dt)\n", " Nt = int(round(T/dt))\n", " \n", " t = Dimension('t', spacing=Constant('h_t'))\n", " u = TimeFunction(name='u', dimensions=(t,), shape=(Nt+1,), space_order=2)\n", " v = TimeFunction(name='v', dimensions=(t,), shape=(Nt+1,), space_order=2)\n", "\n", " u.data[:] = I\n", " v.data[:] = 0 - 0.5*dt*w**2*u.data[:]\n", " \n", " eq_u = Eq(u.dt, v)\n", " eq_v = Eq(v.dt, -(w**2)*u.forward)\n", " \n", " stencil_u = solve(eq_u, u.forward)\n", " stencil_v = solve(eq_v, v.forward)\n", " \n", " update_u = Eq(u.forward, stencil_u)\n", " update_v = Eq(v.forward, stencil_v)\n", " \n", " op = Operator([update_u, update_v])\n", " op.apply(h_t=dt, t_M=Nt-1)\n", "\n", " t_mesh = np.linspace(0, Nt*dt, Nt+1) # mesh for u\n", " t_v_mesh = (t_mesh + dt/2)[:-1] # mesh for v\n", "\n", " return u.data, t_mesh, v.data, t_v_mesh\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Note that $u$ and $v$ are returned together with the mesh points such\n", "that the complete mesh function for $u$ is described by `u` and `t_mesh`,\n", "while `v` and `t_v_mesh` represent the mesh function for $v$.\n", "\n", "Once again, we can compare with the exact solution using the `visualize` function:" ] }, { "cell_type": "code", "execution_count": 66, "metadata": {}, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "Operator `Kernel` run in 0.01 s\n" ] }, { "data": { "image/png": "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\n", "text/plain": [ "
" ] }, "metadata": { "needs_background": "light" }, "output_type": "display_data" } ], "source": [ "I = 1\n", "w = 2*np.pi\n", "dt = 0.05\n", "num_periods = 5\n", "P = 2*np.pi/w # one period\n", "T = P*num_periods\n", "u, t, v, t_v = solver_v1(I, w, dt, T)\n", "\n", "visualize(u, t, I, w)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Verification of this code is easy as we can just compare the computed\n", "`u` with the `u` produced by the `solver` function in\n", "`vib_undamped.py` (which solves $u''+\\omega^2u=0$ directly). The\n", "values should coincide to machine precision since the two numerical\n", "methods are mathematically equivalent. We refer to the file\n", "[`vib_undamped_staggered.py`](https://github.com/devitocodes/devito_book/blob/master/fdm-devito-notebooks/01_vib/src-vib/vib_undamped_staggered.py)\n", "for the details of a unit test (`test_staggered`) that checks this property.\n", "\n", "# Exercises and Problems\n", "\n", "\n", "\n", "\n", "\n", "\n", "## Problem 1: Use linear/quadratic functions for verification\n", "
\n", "\n", "Consider the ODE problem" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "$$\n", "u^{\\prime\\prime} + \\omega^2u=f(t), \\quad u(0)=I,\\ u^{\\prime}(0)=V,\\ t\\in(0,T]\\thinspace .\n", "$$" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "**a)**\n", "Discretize this equation according to $[D_tD_t u + \\omega^2 u = f]^n$ and\n", "derive the equation for the first time step ($u^1$).\n", "\n", "\n", "\n", "**Solution.**\n", "For the requested discretization, we get" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "$$\n", "\\frac{u^{n+1} - 2u^n + u^{n-1}}{\\Delta t^2} + \\omega^2u^n = f^n\\thinspace .\n", "$$" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "To derive the equation for $u^1$, we first find the expression for $u^{n+1}$ from the\n", "discretized form of the equation. Isolating $u^{n+1}$, we get" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "$$\n", "u^{n+1} = \\left(2 - (\\Delta t\\omega)^2\\right)u^n - u^{n-1} + \\Delta t^2 f^n\\thinspace .\n", "$$" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "With $n = 0$, this expression gives" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "$$\n", "u^1 = \\left(2 - (\\Delta t\\omega)^2\\right)u^0 - u^{-1} + \\Delta t^2 f^n\\thinspace .\n", "$$" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Here, however, we get a problem with $u^{-1}$, which appears on the right hand side.\n", "To get around that problem, we realize that the initial condition $u^{\\prime} = V$ might\n", "be approximated by use of a centered difference approximation as" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "$$\n", "\\frac{u^1 - u^{-1}}{2\\Delta t} = V,\n", "$$" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "which means that" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "$$\n", "u^{-1} = u^1 - 2\\Delta t V\\thinspace .\n", "$$" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Inserting this expression for $u^{-1}$ into the expression for $u^1$, we get" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "$$\n", "u^1 = \\left(2 - (\\Delta t\\omega)^2\\right)u^0 - (u^1 - 2\\Delta t V) + \\Delta t^2 f^n\\thinspace .\n", "$$" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Finally, after isolating $u^1$ on the left hand side, we arrive at\n", "\\[ u^1 = \\left(1 - \\frac{1}{2}(\\Delta t\\omega)^2\\right)u^0 + \\Delta t V + \\frac{1}{2}\\Delta t^2 f^n\\thinspace .\\]\n", "\n", "\n", "\n", "**b)**\n", "For verification purposes, we use the method of manufactured solutions (MMS) with the\n", "choice of $\\uex(t)= ct+d$. Find restrictions on $c$ and $d$ from\n", "the initial conditions. Compute the corresponding source term $f$.\n", "Show that $[D_tD_t t]^n=0$ and use the fact\n", "that the $D_tD_t$ operator is linear,\n", "$[D_tD_t (ct+d)]^n = c[D_tD_t t]^n + [D_tD_t d]^n = 0$, to show that\n", "$\\uex$ is also a perfect solution of the discrete equations.\n", "\n", "\n", "\n", "**Solution.**\n", "The initial conditions $u(0)=I$ and $u^{\\prime}(0)=V$ give demands\n", "$\\uex(0)=I$ and $\\uex^{\\prime}(0)=V$, which imply that\n", "$d = I$ and {c = V}.\n", "\n", "To compute the source term $f$, we insert the chosen solution $\\uex$ into\n", "the ODE. This gives" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "$$\n", "0 + \\omega^2(ct+d)=f(t),\n", "$$" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "which implies that" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "$$\n", "f(t)=\\omega^2(Vt+I)\\thinspace .\n", "$$" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "To show that $[D_tD_t t]^n=0$, we proceed as" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "$$\n", "[D_tD_t t]^n = \\frac{t^{n+1} - 2t^n + t^{n-1}}{\\Delta t^2}, \\nonumber\n", "$$" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "$$\n", "= \\frac{(n+1)\\Delta t - 2n\\Delta t + (n-1)\\Delta t}{\\Delta t^2}, \\nonumber\n", "$$" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "$$\n", "= \\frac{n\\Delta t + \\Delta t - 2n\\Delta t + n\\Delta t - \\Delta t}{\\Delta t^2}, \\nonumber\n", "$$" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "$$\n", "= 0\\thinspace . \\nonumber\n", "$$" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Finally, we show that the chosen $\\uex$ is also a perfect solution of the discrete equations.\n", "If we start by inserting $\\uex$ into" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "$$\n", "[D_tD_t u + \\omega^2u = f]^n,\n", "$$" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "as well as the expression found for $f$.\n", "We get" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "$$\n", "[D_tD_t (Vt+I) + \\omega^2(Vt+I) = \\omega^2(Vt+I)]^n,\n", "$$" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "which can be rewritten as" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "$$\n", "[D_tD_t (Vt+I)]^n + [\\omega^2(Vt+I)]^n = [\\omega^2(Vt+I)]^n\\thinspace .\n", "$$" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Now, since the first term here is zero, we see that the discrete equation is\n", "fulfilled exactly for the chosen $\\uex$ function.\n", "\n", "\n", "\n", "**c)**\n", "Use `sympy` to do the symbolic calculations above. Here is a\n", "sketch of the program `vib_undamped_verify_mms.py`:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# NBVAL_SKIP\n", "import sympy as sym\n", "V, t, I, w, dt = sym.symbols('V t I w dt') # global symbols\n", "f = None # global variable for the source term in the ODE\n", "\n", "def ode_source_term(u):\n", " \"\"\"Return the terms in the ODE that the source term\n", " must balance, here u'' + w**2*u.\n", " u is symbolic Python function of t.\"\"\"\n", " return sym.diff(u(t), t, t) + w**2*u(t)\n", "\n", "def residual_discrete_eq(u):\n", " \"\"\"Return the residual of the discrete eq. with u inserted.\"\"\"\n", " R = ...\n", " return sym.simplify(R)\n", "\n", "def residual_discrete_eq_step1(u):\n", " \"\"\"Return the residual of the discrete eq. at the first\n", " step with u inserted.\"\"\"\n", " R = ...\n", " return sym.simplify(R)\n", "\n", "def DtDt(u, dt):\n", " \"\"\"Return 2nd-order finite difference for u_tt.\n", " u is a symbolic Python function of t.\n", " \"\"\"\n", " return ...\n", "\n", "def main(u):\n", " \"\"\"\n", " Given some chosen solution u (as a function of t, implemented\n", " as a Python function), use the method of manufactured solutions\n", " to compute the source term f, and check if u also solves\n", " the discrete equations.\n", " \"\"\"\n", " print '=== Testing exact solution: %s ===' % u\n", " print \"Initial conditions u(0)=%s, u'(0)=%s:\" % \\\n", " (u(t).subs(t, 0), sym.diff(u(t), t).subs(t, 0))\n", "\n", " # Method of manufactured solution requires fitting f\n", " global f # source term in the ODE\n", " f = sym.simplify(ode_lhs(u))\n", "\n", " # Residual in discrete equations (should be 0)\n", " print 'residual step1:', residual_discrete_eq_step1(u)\n", " print 'residual:', residual_discrete_eq(u)\n", "\n", "def linear():\n", " main(lambda t: V*t + I)\n", "\n", "if __name__ == '__main__':\n", " linear()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Fill in the various functions such that the calls in the `main`\n", "function works.\n", "\n", "\n", "\n", "**Solution.**\n", "This part of the code goes as follows:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# NBVAL_SKIP\n", "import sympy as sym\n", "import numpy as np\n", "\n", "V, t, I, w, dt = sym.symbols('V t I w dt') # global symbols\n", "f = None # global variable for the source term in the ODE\n", "\n", "def ode_source_term(u):\n", " \"\"\"Return the terms in the ODE that the source term\n", " must balance, here u'' + w**2*u.\n", " u is symbolic Python function of t.\"\"\"\n", " return sym.diff(u(t), t, t) + w**2*u(t)\n", "\n", "def residual_discrete_eq(u):\n", " \"\"\"Return the residual of the discrete eq. with u inserted.\"\"\"\n", " R = DtDt(u, dt) + w**2*u(t) - f\n", " return sym.simplify(R)\n", "\n", "def residual_discrete_eq_step1(u):\n", " \"\"\"Return the residual of the discrete eq. at the first\n", " step with u inserted.\"\"\"\n", " half = sym.Rational(1,2)\n", " R = u(t+dt) - I - dt*V - \\\n", " half*dt**2*f.subs(t, 0) + half*dt**2*w**2*I\n", " R = R.subs(t, 0) # t=0 in the rhs of the first step eq.\n", " return sym.simplify(R)\n", "\n", "def DtDt(u, dt):\n", " \"\"\"Return 2nd-order finite difference for u_tt.\n", " u is a symbolic Python function of t.\n", " \"\"\"\n", " return (u(t+dt) - 2*u(t) + u(t-dt))/dt**2\n", "\n", "def main(u):\n", " \"\"\"\n", " Given some chosen solution u (as a function of t, implemented\n", " as a Python function), use the method of manufactured solutions\n", " to compute the source term f, and check if u also solves\n", " the discrete equations.\n", " \"\"\"\n", " print '=== Testing exact solution: %s ===' % u(t)\n", " print \"Initial conditions u(0)=%s, u'(0)=%s:\" % \\\n", " (u(t).subs(t, 0), sym.diff(u(t), t).subs(t, 0))\n", "\n", " # Method of manufactured solution requires fitting f\n", " global f # source term in the ODE\n", " f = sym.simplify(ode_source_term(u))\n", "\n", " # Residual in discrete equations (should be 0)\n", " print 'residual step1:', residual_discrete_eq_step1(u)\n", " print 'residual:', residual_discrete_eq(u)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "\n", "\n", "**d)**\n", "The purpose now is to choose a quadratic function\n", "$\\uex = bt^2 + ct + d$ as exact solution. Extend the `sympy`\n", "code above with a function `quadratic` for fitting `f` and checking\n", "if the discrete equations are fulfilled. (The function is very similar\n", "to `linear`.)\n", "\n", "\n", "\n", "\n", "\n", "\n", "**Solution.**\n", "Yes, a quadratic function will fulfill the discrete equations exactly.\n", "The implementation becomes" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# NBVAL_SKIP\n", "def quadratic():\n", " \"\"\"Test quadratic function q*t**2 + V*t + I.\"\"\"\n", " q = sym.Symbol('q') # arbitrary constant in t**2 term\n", " u_e = lambda t: q*t**2 + V*t + I\n", " main(u_e)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Calling `quadratic()` shows that the residual vanishes, and the quadratic\n", "function is an exact solution of the discrete equations.\n", "\n", "\n", "\n", "**e)**\n", "Will a polynomial of degree three fulfill the discrete equations?\n", "\n", "\n", "\n", "**Solution.**\n", "We can easily make a test:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# NBVAL_SKIP\n", "def cubic():\n", " r, q = sym.symbols('r q')\n", " main(lambda t: r*t**3 + q*t**2 + V*t + I)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "When running the final code presented below, the printout shows that the\n", "step1 residual for the cubic function is not zero.\n", "\n", "\n", "\n", "**f)**\n", "Implement a `solver` function for computing the numerical\n", "solution of this problem.\n", "\n", "\n", "\n", "**Solution.**\n", "The `solver` function may take the form" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# NBVAL_SKIP\n", "def solver(I, V, f, w, dt, T):\n", " \"\"\"\n", " Solve u'' + w**2*u = f for t in (0,T], u(0)=I and u'(0)=V,\n", " by a central finite difference method with time step dt.\n", " f(t) is a callable Python function.\n", " \"\"\"\n", " dt = float(dt)\n", " Nt = int(round(T/dt))\n", " u = np.zeros(Nt+1)\n", " t = np.linspace(0, Nt*dt, Nt+1)\n", "\n", " u[0] = I\n", " u[1] = u[0] - 0.5*dt**2*w**2*u[0] + 0.5*dt**2*f(t[0]) + dt*V\n", " for n in range(1, Nt):\n", " u[n+1] = 2*u[n] - u[n-1] - dt**2*w**2*u[n] + dt**2*f(t[n])\n", " return u, t" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We can verify the implementation by the following test function:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# NBVAL_SKIP\n", "def test_quadratic_exact_solution():\n", " \"\"\"Verify solver function via quadratic solution.\"\"\"\n", " # Transform global symbolic variables to functions and numbers\n", " # for numerical computations\n", " global p, V, I, w\n", " p, V, I, w = 2.3, 0.9, 1.2, 1.5\n", " global f, t\n", " u_e = lambda t: p*t**2 + V*t + I # use p, V, I, w as numbers\n", " f = ode_source_term(u_e) # fit source term\n", " f = sym.lambdify(t, f) # make function numerical\n", "\n", " dt = 2./w\n", " u, t = solver(I=I, V=V, f=f, w=w, dt=dt, T=3)\n", " u_e = u_e(t)\n", " error = np.abs(u - u_e).max()\n", " tol = 1E-12\n", " assert error < tol\n", " print 'Error in computing a quadratic solution:', error" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "\n", "\n", "\n", "**g)**\n", "Write a test function for checking that the quadratic solution\n", "is computed correctly (to machine precision, but the\n", "round-off errors accumulate and increase with $T$) by the `solver`\n", "function.\n", "\n", "\n", "\n", "**Solution.**\n", "Here is the complete code for this exercise:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# NBVAL_SKIP\n", "import sympy as sym\n", "import numpy as np\n", "\n", "V, t, I, w, dt = sym.symbols('V t I w dt') # global symbols\n", "f = None # global variable for the source term in the ODE\n", "\n", "def ode_source_term(u):\n", " \"\"\"Return the terms in the ODE that the source term\n", " must balance, here u'' + w**2*u.\n", " u is symbolic Python function of t.\"\"\"\n", " return sym.diff(u(t), t, t) + w**2*u(t)\n", "\n", "def residual_discrete_eq(u):\n", " \"\"\"Return the residual of the discrete eq. with u inserted.\"\"\"\n", " R = DtDt(u, dt) + w**2*u(t) - f\n", " return sym.simplify(R)\n", "\n", "def residual_discrete_eq_step1(u):\n", " \"\"\"Return the residual of the discrete eq. at the first\n", " step with u inserted.\"\"\"\n", " half = sym.Rational(1,2)\n", " R = u(t+dt) - I - dt*V - \\\n", " half*dt**2*f.subs(t, 0) + half*dt**2*w**2*I\n", " R = R.subs(t, 0) # t=0 in the rhs of the first step eq.\n", " return sym.simplify(R)\n", "\n", "def DtDt(u, dt):\n", " \"\"\"Return 2nd-order finite difference for u_tt.\n", " u is a symbolic Python function of t.\n", " \"\"\"\n", " return (u(t+dt) - 2*u(t) + u(t-dt))/dt**2\n", "\n", "def main(u):\n", " \"\"\"\n", " Given some chosen solution u (as a function of t, implemented\n", " as a Python function), use the method of manufactured solutions\n", " to compute the source term f, and check if u also solves\n", " the discrete equations.\n", " \"\"\"\n", " print '=== Testing exact solution: %s ===' % u(t)\n", " print \"Initial conditions u(0)=%s, u'(0)=%s:\" % \\\n", " (u(t).subs(t, 0), sym.diff(u(t), t).subs(t, 0))\n", "\n", " # Method of manufactured solution requires fitting f\n", " global f # source term in the ODE\n", " f = sym.simplify(ode_source_term(u))\n", "\n", " # Residual in discrete equations (should be 0)\n", " print 'residual step1:', residual_discrete_eq_step1(u)\n", " print 'residual:', residual_discrete_eq(u)\n", "\n", "\n", "def linear():\n", " \"\"\"Test linear function V*t+I: u(0)=I, u'(0)=V.\"\"\"\n", " main(lambda t: V*t + I)\n", "\n", "def quadratic():\n", " \"\"\"Test quadratic function q*t**2 + V*t + I.\"\"\"\n", " q = sym.Symbol('q') # arbitrary constant in t**2 term\n", " u_e = lambda t: q*t**2 + V*t + I\n", " main(u_e)\n", "\n", "def cubic():\n", " r, q = sym.symbols('r q')\n", " main(lambda t: r*t**3 + q*t**2 + V*t + I)\n", "\n", "def solver(I, V, f, w, dt, T):\n", " \"\"\"\n", " Solve u'' + w**2*u = f for t in (0,T], u(0)=I and u'(0)=V,\n", " by a central finite difference method with time step dt.\n", " f(t) is a callable Python function.\n", " \"\"\"\n", " dt = float(dt)\n", " Nt = int(round(T/dt))\n", " u = np.zeros(Nt+1)\n", " t = np.linspace(0, Nt*dt, Nt+1)\n", "\n", " u[0] = I\n", " u[1] = u[0] - 0.5*dt**2*w**2*u[0] + 0.5*dt**2*f(t[0]) + dt*V\n", " for n in range(1, Nt):\n", " u[n+1] = 2*u[n] - u[n-1] - dt**2*w**2*u[n] + dt**2*f(t[n])\n", " return u, t\n", "\n", "def test_quadratic_exact_solution():\n", " \"\"\"Verify solver function via quadratic solution.\"\"\"\n", " # Transform global symbolic variables to functions and numbers\n", " # for numerical computations\n", " global p, V, I, w\n", " p, V, I, w = 2.3, 0.9, 1.2, 1.5\n", " global f, t\n", " u_e = lambda t: p*t**2 + V*t + I # use p, V, I, w as numbers\n", " f = ode_source_term(u_e) # fit source term\n", " f = sym.lambdify(t, f) # make function numerical\n", "\n", " dt = 2./w\n", " u, t = solver(I=I, V=V, f=f, w=w, dt=dt, T=3)\n", " u_e = u_e(t)\n", " error = np.abs(u - u_e).max()\n", " tol = 1E-12\n", " assert error < tol\n", " print 'Error in computing a quadratic solution:', error\n", "\n", "if __name__ == '__main__':\n", " linear()\n", " quadratic()\n", " cubic()\n", " test_quadratic_exact_solution()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "\n", "\n", "Filename: `vib_undamped_verify_mms`.\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "## Exercise 2: Show linear growth of the phase with time\n", "
\n", "\n", "Consider an exact solution $I\\cos (\\omega t)$ and an\n", "approximation $I\\cos(\\tilde\\omega t)$.\n", "Define the phase error as the time lag between the peak $I$\n", "in the exact solution and the corresponding peak in the approximation\n", "after $m$ periods of oscillations. Show that this phase error\n", "is linear in $m$.\n", "\n", "\n", "\n", "**Solution.**\n", "From ([19](#vib:ode1:tildeomega:series)) we have that" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "$$\n", "\\tilde\\omega = \\omega\\left( 1 + \\frac{1}{24}\\omega^2\\Delta t^2\\right)\n", "+ \\mathcal{O}{\\Delta t^4}\n", "\\thinspace .\n", "\\nonumber\n", "$$" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Dropping the $\\mathcal{O}{\\Delta t^4}$ term, and since $\\omega=\\frac{2\\pi}{P}$ and $\\tilde\\omega=\\frac{2\\pi}{\\tilde P}$, we have that" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "$$\n", "\\frac{2\\pi}{\\tilde P} \\approx \\frac{2\\pi}{P}\\left( 1 + \\frac{1}{24}\\omega^2\\Delta t^2\\right)\n", "\\thinspace .\n", "\\nonumber\n", "$$" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Now, $2\\pi$ cancels and the remaining equation may be rewritten as" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "$$\n", "P - \\tilde P \\approx \\frac{1}{24}\\omega^2\\Delta t^2\n", "\\thinspace .\n", "\\nonumber\n", "$$" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "This implies that the periods differ by a constant. Since the exact and the numerical\n", "solution start out identically, the phase error $P - \\tilde P$ will become\n", "$m\\frac{1}{24}\\omega^2\\Delta t^2$ after $m$ periods, i.e. the phase error is linear in $m$.\n", "\n", "\n", "Filename: `vib_phase_error_growth`.\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "## Exercise 3: Improve the accuracy by adjusting the frequency\n", "
\n", "\n", "According to ([19](#vib:ode1:tildeomega:series)), the numerical\n", "frequency deviates from the exact frequency by a (dominating) amount\n", "$\\omega^3\\Delta t^2/24 >0$. Replace the `w` parameter in the algorithm\n", "in the `solver` function in `vib_undamped.py` by `w*(1 -\n", "(1./24)*w**2*dt**2` and test how this adjustment in the numerical\n", "algorithm improves the accuracy (use $\\Delta t =0.1$ and simulate\n", "for 80 periods, with and without adjustment of $\\omega$).\n", "\n", "\n", "\n", "**Solution.**\n", "We may take a copy of the `vib_undamped.py` file and edit the `solver`\n", "function to" ] }, { "cell_type": "code", "execution_count": 17, "metadata": {}, "outputs": [], "source": [ "# NBVAL_SKIP\n", "from numpy import *\n", "from matplotlib.pyplot import *\n", "\n", "def solver(I, w, dt, T, adjust_w=True):\n", " \"\"\"\n", " Solve u'' + w**2*u = 0 for t in (0,T], u(0)=I and u'(0)=0,\n", " by a central finite difference method with time step dt.\n", " \"\"\"\n", " dt = float(dt)\n", " Nt = int(round(T/dt))\n", " u = zeros(Nt+1)\n", " t = linspace(0, Nt*dt, Nt+1)\n", " if adjust_w:\n", " w = w*(1 - 1./24*w**2*dt**2)\n", "\n", " u[0] = I\n", " u[1] = u[0] - 0.5*dt**2*w**2*u[0]\n", " for n in range(1, Nt):\n", " u[n+1] = 2*u[n] - u[n-1] - dt**2*w**2*u[n]\n", " return u, t" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The modified code was run for 80 periods with, and without,\n", "the given adjustment of $\\omega$. A substantial difference in accuracy\n", "was observed between the two,\n", "showing that the frequency adjustment improves the situation.\n", "\n", "\n", "Filename: `vib_adjust_w`.\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "## Exercise 4: See if adaptive methods improve the phase error\n", "
\n", "\n", "Adaptive methods for solving ODEs aim at adjusting $\\Delta t$ such\n", "that the error is within a user-prescribed tolerance. Implement the\n", "equation $u^{\\prime\\prime}+u=0$ in the [Odespy](https://github.com/hplgit/odespy)\n", "software. Use the example from Section 3.2.11 in [[Langtangen_decay]](#Langtangen_decay). \n", "Run the scheme with a very low\n", "tolerance (say $10^{-14}$) and for a long time, check the number of\n", "time points in the solver's mesh (`len(solver.t_all)`), and compare\n", "the phase error with that produced by the simple finite difference\n", "method from the section [A centered finite difference scheme](#vib:ode1:fdm) with the same number of (equally\n", "spaced) mesh points. The question is whether it pays off to use an\n", "adaptive solver or if equally many points with a simple method gives\n", "about the same accuracy.\n", "\n", "\n", "\n", "**Solution.**\n", "Here is a code where we define the test problem, solve it by the\n", "Dormand-Prince adaptive method from Odespy, and then call `solver`" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "```python\n", "import odespy\n", "import numpy as np\n", "import sys\n", "#import matplotlib.pyplot as plt\n", "import scitools.std as plt\n", "\n", "def f(s, t):\n", " u, v = s\n", " return np.array([v, -u])\n", "\n", "def u_exact(t):\n", " return I*np.cos(w*t)\n", "\n", "I = 1; V = 0; u0 = np.array([I, V])\n", "w = 1; T = 50\n", "tol = float(sys.argv[1])\n", "solver = odespy.DormandPrince(f, atol=tol, rtol=0.1*tol)\n", "\n", "Nt = 1 # just one step - let scheme find its intermediate points\n", "t_mesh = np.linspace(0, T, Nt+1)\n", "t_fine = np.linspace(0, T, 10001)\n", "\n", "solver.set_initial_condition(u0)\n", "u, t = solver.solve(t_mesh)\n", "\n", "# u and t will only consist of [I, u^Nt] and [0,T], i.e. 2 values\n", "# each, while solver.u_all and solver.t_all contain all computed\n", "# points. solver.u_all is a list with arrays, one array (with 2\n", "# values) for each point in time.\n", "u_adaptive = np.array(solver.u_all)\n", "\n", "# For comparison, we solve also with simple FDM method\n", "import sys, os\n", "sys.path.insert(0, os.path.join(os.pardir, 'src-vib'))\n", "from vib_undamped import solver as simple_solver\n", "Nt_simple = len(solver.t_all)\n", "dt = float(T)/Nt_simple\n", "u_simple, t_simple = simple_solver(I, w, dt, T)\n", "\n", "# Compare in plot: adaptive, constant dt, exact\n", "plt.plot(solver.t_all, u_adaptive[:,0], 'k-')\n", "plt.hold('on')\n", "plt.plot(t_simple, u_simple, 'r--')\n", "plt.plot(t_fine, u_exact(t_fine), 'b-')\n", "plt.legend(['tol=%.0E' % tol, 'u simple', 'exact'])\n", "plt.savefig('tmp_odespy_adaptive.png')\n", "plt.savefig('tmp_odespy_adaptive.pdf')\n", "plt.show()\n", "raw_input()\n", "```" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The program may produce the plots seen in\n", "the figure below,\n", "which shows how the adaptive solution clearly outhinspace .erforms the simpler method,\n", "regardless of the accuracy level.\n", "\n", "\n", "\n", "\n", "

\n", "\n", "\n", "\n", "\n", "\n", "\n", "Filename: `vib_undamped_adaptive`.\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "## Exercise 5: Use a Taylor polynomial to compute $u^1$\n", "
\n", "\n", "As an alternative to computing $u^1$ by ([8](#vib:ode1:step4b)),\n", "one can use a Taylor polynomial with three terms:" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "$$\n", "u(t_1) \\approx u(0) + u^{\\prime}(0)\\Delta t + {\\frac{1}{2}}u^{\\prime\\prime}(0)\\Delta t^2\n", "$$" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "With $u^{\\prime\\prime}=-\\omega^2 u$ and $u^{\\prime}(0)=0$, show that this method also leads to\n", "([8](#vib:ode1:step4b)). Generalize the condition on $u^{\\prime}(0)$ to\n", "be $u^{\\prime}(0)=V$ and compute $u^1$ in this case with both methods.\n", "\n", "\n", "\n", "**Solution.**\n", "With $u^{\\prime\\prime}(0)=-\\omega^2 u(0)$ and $u^{\\prime}(0)=0$, the given Taylor series\n", "becomes" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "$$\n", "u(t_1) \\approx u(0) + {\\frac{1}{2}}(-\\omega^2 u(0))\\Delta t^2\n", "$$" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "which may be written as" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "$$\n", "u^1 \\approx u^0 - {\\frac{1}{2}}\\Delta t^2\\omega^2 u^0\n", "$$" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "but this is nothing but ([8](#vib:ode1:step4b)).\n", "\n", "Now, consider $u^{\\prime}(0)=V$.\n", "With a centered difference approximation, this initial condition becomes" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "$$\n", "\\frac{u^1 - u^{-1}}{2\\Delta t} \\approx V\n", "$$" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "which implies that" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "$$\n", "u^{-1} \\approx u^1 - 2\\Delta t V\n", "$$" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "When $n=0$, ([7](#vib:ode1:step4)) reads" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "$$\n", "u^1 = 2u^0 - u^{-1} - \\Delta t^2\\omega^2 u^0\n", "$$" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Inserting the expression for $u^{-1}$, we get" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "$$\n", "u^1 = 2u^0 - (u^1 - 2\\Delta t V) - \\Delta t^2\\omega^2 u^0\n", "$$" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "which implies that" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "$$\n", "u^1 = u^0 + \\Delta t V - \\frac{1}{2}\\Delta t^2\\omega^2 u^0\n", "$$" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "With the Taylor series approach, we now get" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "$$\n", "u(t_1) \\approx u(0) + V\\Delta t + {\\frac{1}{2}}(-\\omega^2 u(0))\\Delta t^2\n", "$$" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "which also gives" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "$$\n", "u^1 = u^0 + \\Delta t V - \\frac{1}{2}\\Delta t^2\\omega^2 u^0\n", "$$" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "\n", "Filename: `vib_first_step`.\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "## Problem 6: Derive and investigate the velocity Verlet method\n", "\n", "The velocity Verlet method for $u^{\\prime\\prime} + \\omega^2u=0$ is\n", "based on the following ideas:\n", "\n", "1. step $u$ forward from $t_n$ to $t_{n+1}$ using a three-term Taylor\n", " series,\n", "\n", "2. replace $u^{\\prime\\prime}$ by $-\\omega^2u$\n", "\n", "3. discretize $v^{\\prime}=-\\omega^2u$ by a Crank-Nicolson method.\n", "\n", "Derive the scheme, implement it, and determine empirically the convergence rate.\n", "\n", "\n", "\n", "**Solution.**\n", "Stepping $u$ forward from $t_n$ to $t_{n+1}$ using a three-term Taylor\n", "series gives" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "$$\n", "u(t_{n+1}) = u(t_n) + u^{\\prime}(t_n)\\Delta t + \\frac{1}{2}u^{\\prime\\prime}(t_n)\\Delta t^2\\thinspace .\n", "$$" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Using $u^{\\prime}=v$ and $u^{\\prime\\prime}=-\\omega^2u$, we get the updating formula" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "$$\n", "u^{n+1} = u^n + v^n\\Delta t - \\frac{1}{2}\\Delta t^2\\omega^2u^n\\thinspace .\n", "$$" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Second, the first-order equation for $v$," ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "$$\n", "v^{\\prime}=-\\omega^2u,\n", "$$" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "is discretized by a centered difference in a Crank-Nicolson fashion at\n", "$t_{n+\\frac{1}{2}}$:" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "$$\n", "\\frac{v^{n+1}-v^n}{\\Delta t} = -\\omega^2\\frac{1}{2}(u^n + u^{n+1})\\thinspace .\n", "$$" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "To summarize, we have the scheme" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "\n", "
\n", "\n", "$$\n", "\\begin{equation}\n", "u^{n+1} = u^n + v^n\\Delta t - \\frac{1}{2}\\Delta t^2\\omega^2u^n,\n", "\\label{_auto30} \\tag{71}\n", "\\end{equation}\n", "$$" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "\n", "
\n", "\n", "$$\n", "\\begin{equation} \n", "v^{n+1} = v^n -\\frac{1}{2}\\Delta t\\omega^2 (u^n + u^{n+1}),\n", "\\label{_auto31} \\tag{72}\n", "\\end{equation}\n", "$$" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "known as the velocity Verlet algorithm.\n", "Observe that this scheme is explicit since $u^{n+1}$ in the second\n", "equation is already computed by the first equation.\n", "\n", "The algorithm can be straightforwardly implemented as shown below:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# NBVAL_SKIP\n", "from vib_undamped import convergence_rates, main\n", "\n", "def solver(I, w, dt, T, return_v=False):\n", " \"\"\"\n", " Solve u'=v, v'=-w**2*u for t in (0,T], u(0)=I and v(0)=0,\n", " by the velocity Verlet method with time step dt.\n", " \"\"\"\n", " dt = float(dt)\n", " Nt = int(round(T/dt))\n", " u = np.zeros(Nt+1)\n", " v = np.zeros(Nt+1)\n", " t = np.linspace(0, Nt*dt, Nt+1)\n", "\n", " u[0] = I\n", " v[0] = 0\n", " for n in range(Nt):\n", " u[n+1] = u[n] + v[n]*dt - 0.5*dt**2*w**2*u[n]\n", " v[n+1] = v[n] - 0.5*dt*w**2*(u[n] + u[n+1])\n", " if return_v:\n", " return u, v, t\n", " else:\n", " # Return just u and t as in the vib_undamped.py's solver\n", " return u, t" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We provide the option that this `solver` function returns the same data\n", "as the `solver` function from the section [Making a solver function](#vib:impl1:solver) (if `return_v`\n", "is `False`), but alternatively, it may return `v` along with `u` and `t`.\n", "\n", "The error in the Taylor series expansion behind the first equation\n", "is $\\mathcal{O}{\\Delta t^3}$, while the error\n", "in the central difference for $v$ is $\\mathcal{O}{\\Delta t^2}$. The overall\n", "error is then no better than $\\mathcal{O}{\\Delta t^2}$, which can be verified\n", "empirically using the `convergence_rates` function from\n", "the section [Verification](#vib:ode1:verify):" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# NBVAL_SKIP\n", "import vib_undamped_velocity_Verlet as m\n", "m.convergence_rates(4, solver_function=m.solver)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The output confirms that the overall convergence rate is 2.\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "## Problem 7: Find the minimal resolution of an oscillatory function\n", "
\n", "\n", "\n", "\n", "\n", "Sketch the function on a given mesh which has the highest possible\n", "frequency. That is, this oscillatory \"cos-like\" function has its\n", "maxima and minima at every two grid points. Find an expression for\n", "the frequency of this function, and use the result to find the largest\n", "relevant value of $\\omega\\Delta t$ when $\\omega$ is the frequency\n", "of an oscillating function and $\\Delta t$ is the mesh spacing.\n", "\n", "\n", "\n", "**Solution.**\n", "The smallest period must be $2\\Delta t$. Since the period $P$ is related\n", "to the angular frequency $\\omega$ by $P=2\\pi/\\omega$, it means that\n", "$\\omega = \\frac{2\\pi}{2\\Delta t} = \\frac{\\pi}{\\Delta t}$ is the smallest\n", "meaningful angular frequency.\n", "This further means that the largest value for $\\omega\\Delta t$ is $\\pi$.\n", "\n", "\n", "Filename: `vib_largest_wdt`.\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "## Exercise 8: Visualize the accuracy of finite differences for a cosine function\n", "
\n", "\n", "\n", "\n", "\n", "We introduce the error fraction" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "$$\n", "E = \\frac{[D_tD_t u]^n}{u^{\\prime\\prime}(t_n)}\n", "$$" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "to measure the error in the finite difference approximation $D_tD_tu$ to\n", "$u^{\\prime\\prime}$.\n", "Compute $E$\n", "for the specific choice of a cosine/sine function of the\n", "form $u=\\exp{(i\\omega t)}$ and show that" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "$$\n", "E = \\left(\\frac{2}{\\omega\\Delta t}\\right)^2\n", "\\sin^2(\\frac{\\omega\\Delta t}{2})\n", "\\thinspace .\n", "$$" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Plot $E$ as a function of $p=\\omega\\Delta t$. The relevant\n", "values of $p$ are $[0,\\pi]$ (see [Problem 7: Find the minimal resolution of an oscillatory function](#vib:exer:wdt:limit)\n", "for why $p>\\pi$ does not make sense).\n", "The deviation of the curve from unity visualizes the error in the\n", "approximation. Also expand $E$ as a Taylor polynomial in $p$ up to\n", "fourth degree (use, e.g., `sympy`).\n", "\n", "\n", "\n", "**Solution.**" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "$$\n", "E = \\frac{[D_tD_t u]^n}{u^{\\prime\\prime}(t_n)}, \\nonumber\n", "$$" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "$$\n", "= \\frac{u^{n+1} - 2u^n + u^{n-1}}{u^{\\prime\\prime}(t_n)\\Delta t^2}, \\nonumber\n", "$$" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Since $u(t)=\\exp{(i\\omega t)}$, we have that $u^{\\prime}(t)=i\\omega\\exp{(i\\omega t)}$\n", "and $u^{\\prime\\prime}(t)=(i\\omega)^2\\exp{(i\\omega t)}=-\\omega^2\\exp{(i\\omega t)}$, so we may proceed with $E$ as" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "$$\n", "E = \\frac{e^{i\\omega(t_n+\\Delta t)} - 2e^{i\\omega t_n} + e^{i\\omega(t_n-\\Delta t)}}{-\\omega^2e^{i\\omega t_n}\\Delta t^2}, \\nonumber\n", "$$" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "$$\n", "=\\frac{e^{i\\omega t_n}e^{i\\omega \\Delta t} -2e^{i\\omega t_n} + e^{i\\omega t_n}e^{-i\\omega\\Delta t}}{-\\omega^2e^{i\\omega t_n}\\Delta t^2}, \\nonumber\n", "$$" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "$$\n", "= \\frac{e^{i\\omega\\Delta t} - 2 + e^{-i\\omega\\Delta t}}{-\\omega^2 \\Delta t^2}, \\nonumber\n", "$$" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "$$\n", "= \\frac{1}{-\\omega^2 \\Delta t^2}\\frac{4}{4}\\left(e^{i\\omega\\Delta t} - 2 + e^{-i\\omega\\Delta t}\\right), \\nonumber\n", "$$" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "$$\n", "= \\left(\\frac{2}{\\omega\\Delta t}\\right)^2 \\left(-\\frac{e^{i\\omega\\Delta t} - 2 + e^{-i\\omega\\Delta t}}{4}\\right), \\nonumber\n", "$$" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "$$\n", "= \\left(\\frac{2}{\\omega\\Delta t}\\right)^2 \\left(-\\frac{1}{2}\\left(\\frac{1}{2}e^{i\\omega\\Delta t} + e^{-i\\omega\\Delta t} - 1\\right)\\right), \\nonumber\n", "$$" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "$$\n", "= \\left(\\frac{2}{\\omega\\Delta t}\\right)^2 \\left(-\\frac{1}{2}\\left( \\cos(\\omega\\Delta t) - 1\\right)\\right). \\nonumber\n", "$$" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Now, since $\\cos(\\omega\\Delta t)=1-2\\sin^2\\left(\\frac{\\omega\\Delta t}{2}\\right)$, we finally get" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "$$\n", "E = \\left(\\frac{2}{\\omega\\Delta t}\\right)^2 \\left(-\\frac{1}{2}\\left( \\left(1-2\\sin^2\\left(\\frac{\\omega\\Delta t}{2}\\right)\\right) - 1\\right)\\right), \\nonumber\n", "$$" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "$$\n", "= \\left(\\frac{2}{\\omega\\Delta t}\\right)^2 \\sin^2\\left(\\frac{\\omega\\Delta t}{2}\\right). \\nonumber\n", "$$" ] }, { "cell_type": "code", "execution_count": 19, "metadata": {}, "outputs": [ { "ename": "SyntaxError", "evalue": "Missing parentheses in call to 'print'. Did you mean print(E_series)? (, line 16)", "output_type": "error", "traceback": [ "\u001b[0;36m File \u001b[0;32m\"\"\u001b[0;36m, line \u001b[0;32m16\u001b[0m\n\u001b[0;31m print E_series\u001b[0m\n\u001b[0m ^\u001b[0m\n\u001b[0;31mSyntaxError\u001b[0m\u001b[0;31m:\u001b[0m Missing parentheses in call to 'print'. Did you mean print(E_series)?\n" ] } ], "source": [ "# NBVAL_SKIP\n", "import matplotlib.pyplot as plt\n", "import numpy as np\n", "import sympy as sym\n", "\n", "def E_fraction(p):\n", " return (2./p)**2*(np.sin(p/2.))**2\n", "\n", "a = 0; b = np.pi\n", "p = np.linspace(a, b, 100)\n", "E_values = np.zeros(len(p))\n", "\n", "# create 4th degree Taylor polynomial (also plotted)\n", "p_ = sym.symbols('p_')\n", "E = (2./p_)**2*(sym.sin(p_/2.))**2\n", "E_series = E.series(p_, 0, 4).removeO()\n", "print E_series\n", "E_pyfunc = sym.lambdify([p_], E_series, modules='numpy')\n", "\n", "# To avoid division by zero when p is 0, we rather take the limit\n", "E_values[0] = sym.limit(E, p_, 0, dir='+') # ...when p --> 0, E --> 1\n", "E_values[1:] = E_fraction(p[1:])\n", "\n", "plt.plot(p, E_values, 'k-', p, E_pyfunc(p), 'k--')\n", "plt.xlabel('p'); plt.ylabel('Error fraction')\n", "plt.legend(['E', 'E Taylor'])\n", "plt.savefig('tmp_error_fraction.png')\n", "plt.savefig('tmp_error_fraction.pdf')\n", "plt.show()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "From the plot seen below, we realize\n", "how the error fraction $E$ deviates from unity as $p$ grows.\n", "\n", "\n", "\n", "\n", "

\n", "\n", "\n", "\n", "\n", "\n", "\n", "Filename: `vib_plot_fd_exp_error`.\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "## Exercise 9: Verify convergence rates of the error in energy\n", "
\n", "\n", "We consider the ODE problem $u^{\\prime\\prime} + \\omega^2u=0$, $u(0)=I$, $u^{\\prime}(0)=V$,\n", "for $t\\in (0,T]$. The total energy of the solution\n", "$E(t)=\\frac{1}{2}(u^{\\prime})^2 + \\frac{1}{2}\\omega^2 u^2$ should stay\n", "constant.\n", "The error in energy can be computed as explained in\n", "the section [Energy considerations](#vib:model1:energy).\n", "\n", "Make a test function in a separate file, where code from\n", "`vib_undamped.py` is imported, but the `convergence_rates` and\n", "`test_convergence_rates` functions are copied and modified to also\n", "incorporate computations of the error in energy and the convergence\n", "rate of this error. The expected rate is 2, just as for the solution\n", "itself.\n", "\n", "\n", "\n", "**Solution.**\n", "The complete code with test functions goes as follows." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# NBVAL_SKIP\n", "import os, sys\n", "sys.path.insert(0, os.path.join(os.pardir, 'src-vib'))\n", "from vib_undamped import solver, u_exact, visualize\n", "import numpy as np\n", "\n", "def convergence_rates(m, solver_function, num_periods=8):\n", " \"\"\"\n", " Return m-1 empirical estimates of the convergence rate\n", " based on m simulations, where the time step is halved\n", " for each simulation.\n", " solver_function(I, w, dt, T) solves each problem, where T\n", " is based on simulation for num_periods periods.\n", " \"\"\"\n", " from math import pi\n", " w = 0.35; I = 0.3 # just chosen values\n", " P = 2*pi/w # period\n", " dt = P/30 # 30 time step per period 2*pi/w\n", " T = P*num_periods\n", " energy_const = 0.5*I**2*w**2 # initial energy when V = 0\n", "\n", " dt_values = []\n", " E_u_values = [] # error in u\n", " E_energy_values = [] # error in energy\n", " for i in range(m):\n", " u, t = solver_function(I, w, dt, T)\n", " u_e = u_exact(t, I, w)\n", " E_u = np.sqrt(dt*np.sum((u_e-u)**2))\n", " E_u_values.append(E_u)\n", " energy = 0.5*((u[2:] - u[:-2])/(2*dt))**2 + \\\n", " 0.5*w**2*u[1:-1]**2\n", " E_energy = energy - energy_const\n", " E_energy_norm = np.abs(E_energy).max()\n", " E_energy_values.append(E_energy_norm)\n", " dt_values.append(dt)\n", " dt = dt/2\n", "\n", " r_u = [np.log(E_u_values[i-1]/E_u_values[i])/\n", " np.log(dt_values[i-1]/dt_values[i])\n", " for i in range(1, m, 1)]\n", " r_E = [np.log(E_energy_values[i-1]/E_energy_values[i])/\n", " np.log(dt_values[i-1]/dt_values[i])\n", " for i in range(1, m, 1)]\n", " return r_u, r_E\n", "\n", "def test_convergence_rates():\n", " r_u, r_E = convergence_rates(\n", " m=5,\n", " solver_function=solver,\n", " num_periods=8)\n", " # Accept rate to 1 decimal place\n", " tol = 0.1\n", " assert abs(r_u[-1] - 2.0) < tol\n", " assert abs(r_E[-1] - 2.0) < tol\n", "\n", "if __name__ == '__main__':\n", " test_convergence_rates()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "\n", "Filename: `test_error_conv`.\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "## Exercise 10: Use linear/quadratic functions for verification\n", "
\n", "\n", "This exercise is a generalization of [Problem 1: Use linear/quadratic functions for verification](#vib:exer:undamped:verify:linquad) to the extended model problem\n", "([vib:ode2](#vib:ode2)) where the damping term is either linear or quadratic.\n", "Solve the various subproblems and see how the results and problem\n", "settings change with the generalized ODE in case of linear or\n", "quadratic damping. By modifying the code from [Problem 1: Use linear/quadratic functions for verification](#vib:exer:undamped:verify:linquad), `sympy` will do most\n", "of the work required to analyze the generalized problem.\n", "\n", "\n", "\n", "**Solution.**\n", "With a linear spring force, i.e. $s(u)=cu$ (for constant $c$),\n", "our model problem becomes" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "$$\n", "mu^{\\prime\\prime} + f(u^{\\prime}) + cu = F(t),\\quad u(0)=I,\\ u^{\\prime}(0)=V,\\ t\\in (0,T]\n", "\\thinspace .\n", "\\nonumber\n", "$$" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "First we consider linear damping, i.e., when $f(u^{\\prime}) =\n", "bu^{\\prime}$, and follow the text in the section [vib:ode2:fdm:flin](#vib:ode2:fdm:flin). Discretizing the equation according to" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "$$\n", "[mD_tD_t u + f(D_{2t}u) + cu = F]^n,\n", "\\nonumber\n", "$$" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "implies that" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "$$\n", "m\\frac{u^{n+1}-2u^n + u^{n-1}}{\\Delta t^2}\n", "+ b\\frac{u^{n+1}-u^{n-1}}{2\\Delta t} + cu^n = F^n.\n", "\\nonumber\n", "$$" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The explicit formula for $u$ at each\n", "new time level then becomes" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "$$\n", "u^{n+1} = (2mu^n + (\\frac{b}{2}\\Delta t - m)u^{n-1} +\n", "\\Delta t^2(F^n - cu^n))(m + \\frac{b}{2}\\Delta t)^{-1}\n", "\\nonumber\n", "\\thinspace .\n", "$$" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "For the first time step, we use $n=0$ and a centered difference approximation for the\n", "initial condition on the derivative. This gives" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "$$\n", "u^1 = u^0 + \\Delta t\\, V\n", "+ \\frac{\\Delta t^2}{2m}(-bV - cu^0 + F^0)\n", "\\thinspace .\n", "\\nonumber\n", "$$" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Next, we consider quadratic damping, i.e., when\n", "$f(u^{\\prime})=bu^{\\prime}|u^{\\prime}|$, and follow the\n", "text in the chapter [vib:ode2:fdm:fquad](#vib:ode2:fdm:fquad). Discretizing the\n", "equation according to" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "$$\n", "[mD_tD_t u + bD_{2t}u|D_{2t}u| + cu = F]^n\\thinspace .\n", "\\nonumber\n", "$$" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "gives us" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "$$\n", "m\\frac{u^{n+1}-2u^n + u^{n-1}}{\\Delta t^2}\n", "+ b\\frac{u^{n+1}-u^n}{\\Delta t}\\frac{|u^n-u^{n-1}|}{\\Delta t}\n", "+ cu^n = F^n.\n", "\\nonumber\n", "$$" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We solve for $u^{n+1}$ to get the explicit updating formula as" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "$$\n", "u^{n+1} = \\left( m + b|u^n-u^{n-1}|\\right)^{-1}\\times \\nonumber\n", "$$" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "$$\n", "\\qquad \\left(2m u^n - mu^{n-1} + bu^n|u^n-u^{n-1}| + \\Delta t^2 (F^n - cu^n)\n", "\\right)\n", "\\thinspace .\n", "\\nonumber\n", "$$" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "and the equation for the first time step as" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "$$\n", "u^1 = u^0 + \\Delta t V + \\frac{\\Delta t^2}{2m}\\left(-bV|V| - cu^0 + F^0\\right)\n", "\\thinspace .\n", "\\nonumber\n", "$$" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Turning to verification with MMS and $u_e(t)=ct+d$, we get $d=I$ and $c=V$ independent\n", "of the damping term, so these parameter values stay as for the undamped case.\n", "\n", "Proceeding with linear damping, we get from the chapter [vib:ode2:verify](#vib:ode2:verify) that" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "$$\n", "F(t) = bV + c(Vt + I)\\thinspace .\n", "\\nonumber\n", "$$" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "(Note that there are two different c parameters here, one from $u_e=ct+d$ and one from the spring force $cu$.\n", "The first one disappears, however, as it is switched with $V$.)\n", "\n", "To show that $u_e$ is a perfect solution also to the discrete equations, we insert $u_e$ and $F$ into" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "$$\n", "[mD_tD_t u + bD_{2t}u + cu = F]^n\\thinspace .\n", "\\nonumber\n", "$$" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "This gives" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "$$\n", "[mD_tD_t (Vt+I) + bD_{2t}(Vt+I) + c(Vt+I) = bV + c(Vt + I)]^n,\n", "\\nonumber\n", "$$" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "which may be split up as" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "$$\n", "m[D_tD_t (Vt+I)]^n + b[D_{2t}(Vt+I)]^n + c[(Vt+I)]^n = b[V]^n + c[(Vt + I)]^n.\n", "\\nonumber\n", "$$" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Simplifying, we note that the first term is zero and that $c[(Vt+I)]^n$ appears with the\n", "same sign on each side of the equation. Thus, dropping these terms, and cancelling the common\n", "factor $b$, we are left with" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "$$\n", "[D_{2t}(Vt+I)]^n = [V]^n.\n", "\\nonumber\n", "$$" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "mathcal{I}_t therefore remains to show that $[D_{2t}(Vt+I)]^n$ is equal to $[V]^n = V$. We write out\n", "the left hand side as" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "$$\n", "[D_{2t}(Vt+I)]^n = \\frac{(Vt_{n+1}+I) - (Vt_{n-1}+I)}{2\\Delta t} \\nonumber\n", "$$" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "$$\n", "= \\frac{V(t_{n+1}-t_{n-1})}{2\\Delta t} \\nonumber\n", "$$" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "$$\n", "= \\frac{V((t_n + \\Delta t) - (t_n - \\Delta t))}{2\\Delta t} \\nonumber\n", "$$" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "$$\n", "= V, \\nonumber\n", "\\nonumber\n", "$$" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "which shows that the two sides of the equation are equal and that the discrete equations\n", "are fulfilled exactly for the given $u_e$ function.\n", "\n", "If the damping is rather quadratic, we find from the chapter [vib:ode2:verify](#vib:ode2:verify) that" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "$$\n", "F(t) = b|V|V + c(Vt + I)\\thinspace .\n", "\\nonumber\n", "$$" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "As with linear damping, we show that $u_e$ is a perfect solution also to the discrete equations\n", "by inserting $u_e$ and $F$ into" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "$$\n", "[mD_tD_t u + bD_{2t}u|D_{2t}u| + cu = F]^n\\thinspace .\n", "\\nonumber\n", "$$" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We then get" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "$$\n", "[mD_tD_t (Vt+I) + bD_{2t}(Vt+I)|D_{2t}(Vt+I)| + c(Vt+I) = b|V|V + c(Vt+I)]^n,\n", "\\nonumber\n", "$$" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "which simplifies to" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "$$\n", "[bD_{2t}(Vt+I)|D_{2t}(Vt+I)| = b|V|V]^n\n", "\\nonumber\n", "$$" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "and further to" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "$$\n", "[D_{2t}(Vt+I)]^n [|D_{2t}(Vt+I)|]^n = |V|V\n", "\\nonumber\n", "$$" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "which simply states that" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "$$\n", "V|V| = |V|V\\thinspace .\n", "\\nonumber\n", "$$" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Thus, $u_e$ fulfills the discrete equations exactly also when the damping term\n", "is quadratic.\n", "\n", "When the exact solution is changed to become quadratic or cubic, the situation is more complicated.\n", "\n", "For a quadratic solution $u_e$ combined with (zero damping or) linear damping, the output from the program below shows that the discrete equations are fulfilled exactly. However, this is not the case with nonlinear damping, where only the first step gives zero residual.\n", "\n", "For a cubic solution $u_e$, we get a nonzero residual for (zero damping and) linear and nonlinear damping." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# NBVAL_SKIP\n", "import sympy as sym\n", "import numpy as np\n", "\n", "# The code in vib_undamped_verify_mms.py is here generalized\n", "# to treat the model m*u'' + f(u') + c*u = F(t), where the\n", "# damping term f(u') = 0, b*u' or b*V*abs(V).\n", "\n", "def ode_source_term(u, damping):\n", " \"\"\"Return the terms in the ODE that the source term\n", " must balance, here m*u'' + f(u') + c*u.\n", " u is a symbolic Python function of t.\"\"\"\n", " if damping == 'zero':\n", " return m*sym.diff(u(t), t, t) + c*u(t)\n", " elif damping == 'linear':\n", " return m*sym.diff(u(t), t, t) + \\\n", " b*sym.diff(u(t), t) + c*u(t)\n", " else: # damping is nonlinear\n", " return m*sym.diff(u(t), t, t) + \\\n", " b*sym.diff(u(t), t)*abs(sym.diff(u(t), t)) + c*u(t)\n", "\n", "def residual_discrete_eq(u, damping):\n", " \"\"\"Return the residual of the discrete eq. with u inserted.\"\"\"\n", " if damping == 'zero':\n", " R = m*DtDt(u, dt) + c*u(t) - F\n", " elif damping == 'linear':\n", " R = m*DtDt(u, dt) + b*D2t(u, dt) + c*u(t) - F\n", " else: # damping is nonlinear\n", " R = m*DtDt(u, dt) + b*Dt_p_half(u, dt)*\\\n", " abs(Dt_m_half(u, dt)) + c*u(t) - F\n", " return sym.simplify(R)\n", "\n", "def residual_discrete_eq_step1(u, damping):\n", " \"\"\"Return the residual of the discrete eq. at the first\n", " step with u inserted.\"\"\"\n", " half = sym.Rational(1,2)\n", " if damping == 'zero':\n", " R = u(t+dt) - u(t) - dt*V - \\\n", " half*dt**2*(F.subs(t, 0)/m) + half*dt**2*(c/m)*I\n", " elif damping == 'linear':\n", " R = u(t+dt) - (I + dt*V + \\\n", " half*(dt**2/m)*(-b*V - c*I + F.subs(t, 0)))\n", " else: # damping is nonlinear\n", " R = u(t+dt) - (I + dt*V + \\\n", " half*(dt**2/m)*(-b*V*abs(V) - c*I + F.subs(t, 0)))\n", " R = R.subs(t, 0) # t=0 in the rhs of the first step eq.\n", " return sym.simplify(R)\n", "\n", "def DtDt(u, dt):\n", " \"\"\"Return 2nd-order finite difference for u_tt.\n", " u is a symbolic Python function of t.\n", " \"\"\"\n", " return (u(t+dt) - 2*u(t) + u(t-dt))/dt**2\n", "\n", "def D2t(u, dt):\n", " \"\"\"Return 2nd-order finite difference for u_t.\n", " u is a symbolic Python function of t.\n", " \"\"\"\n", " return (u(t+dt) - u(t-dt))/(2.0*dt)\n", "\n", "def Dt_p_half(u, dt):\n", " \"\"\"Return 2nd-order finite difference for u_t, sampled at n+1/2,\n", " i.e, n pluss one half... u is a symbolic Python function of t.\n", " \"\"\"\n", " return (u(t+dt) - u(t))/dt\n", "\n", "def Dt_m_half(u, dt):\n", " \"\"\"Return 2nd-order finite difference for u_t, sampled at n-1/2,\n", " i.e, n minus one half.... u is a symbolic Python function of t.\n", " \"\"\"\n", " return (u(t) - u(t-dt))/dt\n", "\n", "def main(u, damping):\n", " \"\"\"\n", " Given some chosen solution u (as a function of t, implemented\n", " as a Python function), use the method of manufactured solutions\n", " to compute the source term f, and check if u also solves\n", " the discrete equations.\n", " \"\"\"\n", " print '=== Testing exact solution: %s ===' % u(t)\n", " print \"Initial conditions u(0)=%s, u'(0)=%s:\" % \\\n", " (u(t).subs(t, 0), sym.diff(u(t), t).subs(t, 0))\n", "\n", " # Method of manufactured solution requires fitting F\n", " global F # source term in the ODE\n", " F = sym.simplify(ode_source_term(u, damping))\n", "\n", " # Residual in discrete equations (should be 0)\n", " print 'residual step1:', residual_discrete_eq_step1(u, damping)\n", " print 'residual:', residual_discrete_eq(u, damping)\n", "\n", "\n", "def linear(damping):\n", " def u_e(t):\n", " \"\"\"Return chosen linear exact solution.\"\"\"\n", " # General linear function u_e = c*t + d\n", " # Initial conditions u(0)=I, u'(0)=V require c=V, d=I\n", " return V*t + I\n", "\n", " main(u_e, damping)\n", "\n", "def quadratic(damping):\n", " # Extend with quadratic functions\n", " q = sym.Symbol('q') # arbitrary constant in quadratic term\n", "\n", " def u_e(t):\n", " return q*t**2 + V*t + I\n", "\n", " main(u_e, damping)\n", "\n", "def cubic(damping):\n", " r, q = sym.symbols('r q')\n", "\n", " main(lambda t: r*t**3 + q*t**2 + V*t + I, damping)\n", "\n", "\n", "def solver(I, V, F, b, c, m, dt, T, damping):\n", " \"\"\"\n", " Solve m*u'' + f(u') + c*u = F for t in (0,T], u(0)=I and u'(0)=V,\n", " by a central finite difference method with time step dt.\n", " F(t) is a callable Python function.\n", " \"\"\"\n", " dt = float(dt)\n", " Nt = int(round(T/dt))\n", " u = np.zeros(Nt+1)\n", " t = np.linspace(0, Nt*dt, Nt+1)\n", "\n", " if damping == 'zero':\n", " u[0] = I\n", " u[1] = u[0] - 0.5*dt**2*(c/m)*u[0] + \\\n", " 0.5*dt**2*F(t[0])/m + dt*V\n", " for n in range(1, Nt):\n", " u[n+1] = 2*u[n] - u[n-1] - \\\n", " dt**2*(c/m)*u[n] + dt**2*F(t[n])/m\n", " elif damping == 'linear':\n", " u[0] = I\n", " u[1] = u[0] + dt*V + \\\n", " 0.5*(dt**2/m)*(-b*V - c*u[0] + F(t[0]))\n", " for n in range(1, Nt):\n", " u[n+1] = (2*m*u[n] + (b*dt/2.-m)*u[n-1] + \\\n", " dt**2*(F(t[n])-c*u[n]))/(m+b*dt/2.)\n", " else: # damping is quadratic\n", " u[0] = I\n", " u[1] = u[0] + dt*V + \\\n", " 0.5*(dt**2/m)*(-b*V*abs(V) - c*u[0] + F(t[0]))\n", " for n in range(1, Nt):\n", " u[n+1] = 1./(m+b*abs(u[n]-u[n-1])) * \\\n", " (2*m*u[n] - m*u[n-1] + b*u[n]*\\\n", " abs(u[n]-u[n-1])+dt**2*(F(t[n])-c*u[n]))\n", " return u, t\n", "\n", "def test_quadratic_exact_solution(damping):\n", " # Transform global symbolic variables to functions and numbers\n", " # for numerical computations\n", "\n", " global p, V, I, b, c, m\n", " p, V, I, b, c, m = 2.3, 0.9, 1.2, 2.1, 1.6, 1.3 # i.e., as numbers\n", " global F, t\n", " u_e = lambda t: p*t**2 + V*t + I\n", " F = ode_source_term(u_e, damping) # fit source term\n", " F = sym.lambdify(t, F) # ...numerical Python function\n", "\n", " from math import pi, sqrt\n", " dt = 2*pi/sqrt(c/m)/10 # 10 steps per period 2*pi/w, w=sqrt(c/m)\n", " u, t = solver(I=I, V=V, F=F, b=b, c=c, m=m, dt=dt,\n", " T=(2*pi/sqrt(c/m))*2, damping=damping)\n", " u_e = u_e(t)\n", " error = np.abs(u - u_e).max()\n", " tol = 1E-12\n", " assert error < tol \n", " print 'Error in computing a quadratic solution:', error\n", "\n", "if __name__ == '__main__':\n", " damping = ['zero', 'linear', 'quadratic']\n", " for e in damping:\n", " V, t, I, dt, m, b, c = sym.symbols('V t I dt m b c') # global\n", " F = None # global variable for the source term in the ODE\n", " print '---------------------------------------Damping:', e\n", " linear(e) \t# linear solution used for MMS\n", " quadratic(e) \t# quadratic solution for MMS\n", " cubic(e) \t# ... and cubic\n", " test_quadratic_exact_solution(e)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "\n", "Filename: `vib_verify_mms`.\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "## Exercise 11: Use an exact discrete solution for verification\n", "
\n", "\n", "Write a test function in a separate file\n", "that employs the exact discrete solution\n", "([20](#vib:ode1:un:exact)) to verify the implementation of the\n", "`solver` function in the file `vib_undamped.py`.\n", "\n", "\n", "\n", "**Solution.**\n", "The code goes like this:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# NBVAL_SKIP\n", "from vib_undamped import solver\n", "from numpy import arcsin as asin, pi, cos, abs\n", "\n", "def test_solver_exact_discrete_solution():\n", " def tilde_w(w, dt):\n", " return (2./dt)*asin(w*dt/2.)\n", "\n", " def u_numerical_exact(t):\n", " return I*cos(tilde_w(w, dt)*t)\n", "\n", " w = 2.5\n", " I = 1.5\n", "\n", " # Estimate period and time step\n", " P = 2*pi/w\n", " num_periods = 4\n", " T = num_periods*P\n", " N = 5 # time steps per period\n", " dt = P/N\n", " u, t = solver(I, w, dt, T)\n", " u_e = u_numerical_exact(t)\n", " error= abs(u_e - u).max()\n", " # Make a plot in a file, but not on the screen\n", " from scitools.std import plot\n", " plot(t, u, 'bo', t, u_e, 'r-',\n", " legend=('numerical', 'exact'), show=False,\n", " savefig='tmp.png')\n", "\n", " assert error < 1E-14\n", "\n", "if __name__ == '__main__':\n", " test_solver_exact_discrete_solution()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "\n", "Filename: `test_vib_undamped_exact_discrete_sol`.\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "## Exercise 12: Use analytical solution for convergence rate tests\n", "
\n", "\n", "The purpose of this exercise is to perform convergence tests of the\n", "problem ([vib:ode2](#vib:ode2)) when $s(u)=cu$, $F(t)=A\\sin\\phi t$ and there\n", "is no damping. Find the complete analytical solution to the problem\n", "in this case (most textbooks on mechanics or ordinary differential\n", "equations list the various elements you need to write down the exact\n", "solution, or you can use symbolic tools like `sympy` or `wolframalpha.com`).\n", "Modify the `convergence_rate` function from the\n", "`vib_undamped.py` program to perform experiments with the extended\n", "model. Verify that the error is of order $\\Delta t^2$.\n", "\n", "\n", "\n", "**Solution.**\n", "The code:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# NBVAL_SKIP\n", "import numpy as np\n", "import matplotlib.pyplot as plt\n", "from vib_verify_mms import solver\n", "\n", "def u_exact(t, I, V, A, f, c, m):\n", " \"\"\"Found by solving mu'' + cu = F in Wolfram alpha.\"\"\"\n", " k_1 = I\n", " k_2 = (V - A*2*np.pi*f/(c - 4*np.pi**2*f**2*m))*\\\n", " np.sqrt(m/float(c))\n", " return A*np.sin(2*np.pi*f*t)/(c - 4*np.pi**2*f**2*m) + \\\n", " k_2*np.sin(np.sqrt(c/float(m))*t) + \\\n", " k_1*np.cos(np.sqrt(c/float(m))*t)\n", "\n", "def convergence_rates(N, solver_function, num_periods=8):\n", " \"\"\"\n", " Returns N-1 empirical estimates of the convergence rate\n", " based on N simulations, where the time step is halved\n", " for each simulation.\n", " solver_function(I, V, F, c, m, dt, T, damping) solves\n", " each problem, where T is based on simulation for\n", " num_periods periods.\n", " \"\"\"\n", "\n", " def F(t):\n", " \"\"\"External driving force\"\"\"\n", " return A*np.sin(2*np.pi*f*t)\n", "\n", " b, c, m = 0, 1.6, 1.3 # just some chosen values\n", " I = 0 # init. cond. u(0)\n", " V = 0 # init. cond. u'(0)\n", " A = 1.0 # amplitude of driving force\n", " f = 1.0 # chosen frequency of driving force\n", " damping = 'zero'\n", "\n", " P = 1/f\n", " dt = P/30 # 30 time step per period 2*pi/w\n", " T = P*num_periods\n", "\n", " dt_values = []\n", " E_values = []\n", " for i in range(N):\n", " u, t = solver_function(I, V, F, b, c, m, dt, T, damping)\n", " u_e = u_exact(t, I, V, A, f, c, m)\n", " E = np.sqrt(dt*np.sum((u_e-u)**2))\n", " dt_values.append(dt)\n", " E_values.append(E)\n", " dt = dt/2\n", "\n", " #plt.plot(t, u, 'b--', t, u_e, 'r-'); plt.grid(); plt.show()\n", "\n", " r = [np.log(E_values[i-1]/E_values[i])/\n", " np.log(dt_values[i-1]/dt_values[i])\n", " for i in range(1, N, 1)]\n", " print r\n", " return r\n", "\n", "def test_convergence_rates():\n", " r = convergence_rates(\n", " N=5,\n", " solver_function=solver,\n", " num_periods=8)\n", " # Accept rate to 1 decimal place\n", " tol = 0.1\n", " assert abs(r[-1] - 2.0) < tol\n", "\n", "if __name__ == '__main__':\n", " test_convergence_rates()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The output from the program shows that $r$ approaches 2.\n", "\n", "\n", "Filename: `vib_conv_rate`.\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "## Exercise 13: Investigate the amplitude errors of many solvers\n", "
\n", "\n", "Use the program `vib_undamped_odespy.py` from the section [Comparison of schemes](#vib:model2x2:compare) (utilize the function `amplitudes`) to investigate\n", "how well famous methods for 1st-order ODEs can preserve the amplitude of $u$ in undamped\n", "oscillations. Test, for example, the 3rd- and 4th-order Runge-Kutta\n", "methods (`RK3`, `RK4`), the Crank-Nicolson method (`CrankNicolson`),\n", "the 2nd- and 3rd-order Adams-Bashforth methods (`AdamsBashforth2`,\n", "`AdamsBashforth3`), and a 2nd-order Backwards scheme\n", "(`Backward2Step`). The relevant governing equations are listed in\n", "the beginning of the section [Alternative schemes based on 1st-order equations](#vib:model2x2).\n", "\n", "Running the code, we get the plots seen in [Figure](#vib:exer:fig:ampl_RK34),\n", "[vib:exer:fig:ampl_CNB2](#vib:exer:fig:ampl_CNB2), and [vib:exer:fig:ampl_AB](#vib:exer:fig:ampl_AB). They\n", "show that `RK4` is superior to the others, but that also `CrankNicolson` performs well. In fact, with `RK4` the amplitude changes by less than $0.1$ per cent over the interval.\n", "\n", "\n", "\n", "
\n", "\n", "

The amplitude as it changes over 100 periods for RK3 and RK4.

\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "
\n", "\n", "

The amplitude as it changes over 100 periods for Crank-Nicolson and Backward 2 step.

\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "
\n", "\n", "

The amplitude as it changes over 100 periods for Adams-Bashforth 2 and 3.

\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "**Solution.**\n", "We modify the proposed code to the following:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# NBVAL_SKIP\n", "import scitools.std as plt\n", "#import matplotlib.pyplot as plt\n", "from vib_empirical_analysis import minmax, amplitudes\n", "import sys\n", "import odespy\n", "import numpy as np\n", "\n", "def f(u, t, w=1):\n", " # v, u numbering for EulerCromer to work well\n", " v, u = u # u is array of length 2 holding our [v, u]\n", " return [-w**2*u, v]\n", "\n", "def run_solvers_and_check_amplitudes(solvers, timesteps_per_period=20,\n", " num_periods=1, I=1, w=2*np.pi):\n", " P = 2*np.pi/w # duration of one period\n", " dt = P/timesteps_per_period\n", " Nt = num_periods*timesteps_per_period\n", " T = Nt*dt\n", " t_mesh = np.linspace(0, T, Nt+1)\n", "\n", " file_name = 'Amplitudes' # initialize filename for plot\n", " for solver in solvers:\n", " solver.set(f_kwargs={'w': w})\n", " solver.set_initial_condition([0, I])\n", " u, t = solver.solve(t_mesh)\n", "\n", " solver_name = \\\n", " 'CrankNicolson' if solver.__class__.__name__ == \\\n", " 'MidpointImplicit' else solver.__class__.__name__\n", " file_name = file_name + '_' + solver_name\n", "\n", " minima, maxima = minmax(t, u[:,0])\n", " a = amplitudes(minima, maxima)\n", " plt.plot(range(len(a)), a, '-', label=solver_name)\n", " plt.hold('on')\n", "\n", " plt.xlabel('Number of periods')\n", " plt.ylabel('Amplitude (absolute value)')\n", " plt.legend(loc='upper left')\n", " plt.savefig(file_name + '.png')\n", " plt.savefig(file_name + '.pdf')\n", " plt.show()\n", "\n", "\n", "# Define different sets of experiments\n", "solvers_CNB2 = [odespy.CrankNicolson(f, nonlinear_solver='Newton'),\n", " odespy.Backward2Step(f)]\n", "solvers_RK34 = [odespy.RK3(f), \n", " odespy.RK4(f)]\n", "solvers_AB = [odespy.AdamsBashforth2(f), \n", " odespy.AdamsBashforth3(f)]\n", "\n", "if __name__ == '__main__':\n", " # Default values\n", " timesteps_per_period = 30\n", " solver_collection = 'CNB2'\n", " num_periods = 100\n", " # Override from command line\n", " try:\n", " # Example: python vib_undamped_odespy.py 30 RK34 50\n", " timesteps_per_period = int(sys.argv[1])\n", " solver_collection = sys.argv[2]\n", " num_periods = int(sys.argv[3])\n", " except IndexError:\n", " pass # default values are ok\n", " solvers = eval('solvers_' + solver_collection) # list of solvers\n", " run_solvers_and_check_amplitudes(solvers,\n", " timesteps_per_period,\n", " num_periods)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "\n", "Filename: `vib_amplitude_errors`.\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "## Problem 14: Minimize memory usage of a simple vibration solver\n", "
\n", "\n", "We consider the model problem $u''+\\omega^2 u = 0$, $u(0)=I$, $u'(0)=V$,\n", "solved by a second-order finite difference scheme. A standard implementation\n", "typically employs an array `u` for storing all the $u^n$ values. However,\n", "at some time level `n+1` where we want to compute `u[n+1]`, all we need\n", "of previous `u` values are from level `n` and `n-1`. We can therefore avoid\n", "storing the entire array `u`, and instead work with `u[n+1]`, `u[n],`\n", "and `u[n-1]`, named as `u`, `u_n`, `u_nmp1`, for instance. Another\n", "possible naming convention is `u`, `u_n[0]`, `u_n[-1]`.\n", "Store the solution in a file\n", "for later visualization. Make a test function that verifies the implementation\n", "by comparing with the another code for the same problem.\n", "\n", "\n", "\n", "**Solution.**\n", "The modified solver function needs more manual steps initially, and it needs\n", "shuffling of the `u_n` and `u_nm1` variables at each time level. Otherwise\n", "it is very similar to the previous `solver` function with an array `u` for\n", "the entire mesh function." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# NBVAL_SKIP\n", "import numpy as np\n", "import matplotlib.pyplot as plt\n", "\n", "def solver_memsave(I, w, dt, T, filename='tmp.dat'):\n", " \"\"\"\n", " As vib_undamped.solver, but store only the last three\n", " u values in the implementation. The solution is written to\n", " file `tmp_memsave.dat`.\n", " Solve u'' + w**2*u = 0 for t in (0,T], u(0)=I and u'(0)=0,\n", " by a central finite difference method with time step dt.\n", " \"\"\"\n", " dt = float(dt)\n", " Nt = int(round(T/dt))\n", " t = np.linspace(0, Nt*dt, Nt+1)\n", " outfile = open(filename, 'w')\n", "\n", " u_n = I\n", " outfile.write('%20.12f %20.12f\\n' % (0, u_n))\n", " u = u_n - 0.5*dt**2*w**2*u_n\n", " outfile.write('%20.12f %20.12f\\n' % (dt, u))\n", " u_nm1 = u_n\n", " u_n = u\n", " for n in range(1, Nt):\n", " u = 2*u_n - u_nm1 - dt**2*w**2*u_n\n", " outfile.write('%20.12f %20.12f\\n' % (t[n], u))\n", " u_nm1 = u_n\n", " u_n = u\n", " return u, t" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Verification can be done by comparing with the `solver` function in\n", "the `vib_undamped` module. Note that to compare both time series, we need\n", "to load the data written to file in `solver_memsave` back in memory again.\n", "For this purpose, we can use the `numpy.loadtxt` function, which reads\n", "tabular data and returns them as a table `data`. Our interest is in the\n", "second column of the data (the `u` values)." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# NBVAL_SKIP\n", "def test_solver_memsave():\n", " from vib_undamped import solver\n", " _, _ = solver_memsave(I=1, dt=0.1, w=1, T=30)\n", " u_expected, _ = solver (I=1, dt=0.1, w=1, T=30)\n", " data = np.loadtxt('tmp.dat')\n", " u_computed = data[:,1]\n", " diff = np.abs(u_expected - u_computed).max()\n", " assert diff < 5E-13, diff" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "\n", "Filename: `vib_memsave0`.\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "## Problem 15: Minimize memory usage of a general vibration solver\n", "
\n", "\n", "The program [`vib.py`](https://github.com/devitocodes/devito_book/blob/master/fdm-devito-notebooks/01_vib/src-vib/vib.py) stores the complete\n", "solution $u^0,u^1,\\ldots,u^{N_t}$ in memory, which is convenient for\n", "later plotting. Make a memory minimizing version of this program\n", "where only the last three $u^{n+1}$, $u^n$, and $u^{n-1}$ values are\n", "stored in memory under the names `u`, `u_n`, and `u_nm1` (this is the\n", "naming convention used in this book).\n", "Write each computed $(t_{n+1}, u^{n+1})$ pair to\n", "file. Visualize the data in the file (a cool solution is to read one\n", "line at a time and plot the $u$ value using the line-by-line plotter\n", "in the `visualize_front_ascii` function - this technique makes it\n", "trivial to visualize very long time simulations).\n", "\n", "\n", "\n", "**Solution.**\n", "Here is the complete program:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# NBVAL_SKIP\n", "import numpy as np\n", "import scitools.std as plt\n", "\n", "def solve_and_store(filename, I, V, m, b, s,\n", " F, dt, T, damping='linear'):\n", " \"\"\"\n", " Solve m*u'' + f(u') + s(u) = F(t) for t in (0,T], u(0)=I and\n", " u'(0)=V, by a central finite difference method with time step\n", " dt. If damping is 'linear', f(u')=b*u, while if damping is\n", " 'quadratic', f(u')=b*u'*abs(u'). F(t) and s(u) are Python\n", " functions. The solution is written to file (filename).\n", " Naming convention: we use the name u for the new solution\n", " to be computed, u_n for the solution one time step prior to\n", " that and u_nm1 for the solution two time steps prior to that.\n", " Returns min and max u values needed for subsequent plotting.\n", " \"\"\"\n", " dt = float(dt); b = float(b); m = float(m) # avoid integer div.\n", " Nt = int(round(T/dt))\n", " outfile = open(filename, 'w')\n", " outfile.write('Time Position\\n')\n", "\n", " u_nm1 = I\n", " u_min = u_max = u_nm1\n", " outfile.write('%6.3f %7.5f\\n' % (0*dt, u_nm1))\n", " if damping == 'linear':\n", " u_n = u_nm1 + dt*V + dt**2/(2*m)*(-b*V - s(u_nm1) + F(0*dt))\n", " elif damping == 'quadratic':\n", " u_n = u_nm1 + dt*V + \\\n", " dt**2/(2*m)*(-b*V*abs(V) - s(u_nm1) + F(0*dt))\n", " if u_n < u_nm1:\n", " u_min = u_n\n", " else: # either equal or u_n > u_nm1\n", " u_max = u_n\n", " outfile.write('%6.3f %7.5f\\n' % (1*dt, u_n))\n", "\n", " for n in range(1, Nt):\n", " # compute solution at next time step\n", " if damping == 'linear':\n", " u = (2*m*u_n + (b*dt/2 - m)*u_nm1 +\n", " dt**2*(F(n*dt) - s(u_n)))/(m + b*dt/2)\n", " elif damping == 'quadratic':\n", " u = (2*m*u_n - m*u_nm1 + b*u_n*abs(u_n - u_nm1)\n", " + dt**2*(F(n*dt) - s(u_n)))/\\\n", " (m + b*abs(u_n - u_nm1))\n", " if u < u_min:\n", " u_min = u\n", " elif u > u_max:\n", " u_max = u\n", "\n", " # write solution to file\n", " outfile.write('%6.3f %7.5f\\n' % ((n+1)*dt, u))\n", " # switch references before next step\n", " u_nm1, u_n, u = u_n, u, u_nm1\n", "\n", " outfile.close()\n", " return u_min, u_max\n", "\n", "def main():\n", " import argparse\n", " parser = argparse.ArgumentParser()\n", " parser.add_argument('--I', type=float, default=1.0)\n", " parser.add_argument('--V', type=float, default=0.0)\n", " parser.add_argument('--m', type=float, default=1.0)\n", " parser.add_argument('--b', type=float, default=0.0)\n", " parser.add_argument('--s', type=str, default='u')\n", " parser.add_argument('--F', type=str, default='0')\n", " parser.add_argument('--dt', type=float, default=0.05)\n", " parser.add_argument('--T', type=float, default=10)\n", " parser.add_argument('--window_width', type=float, default=30.,\n", " help='Number of periods in a window')\n", " parser.add_argument('--damping', type=str, default='linear')\n", " parser.add_argument('--savefig', action='store_true')\n", " # Hack to allow --SCITOOLS options\n", " # (scitools.std reads this argument at import)\n", " parser.add_argument('--SCITOOLS_easyviz_backend',\n", " default='matplotlib')\n", " a = parser.parse_args()\n", " from scitools.std import StringFunction\n", " s = StringFunction(a.s, independent_variable='u')\n", " F = StringFunction(a.F, independent_variable='t')\n", " I, V, m, b, dt, T, window_width, savefig, damping = \\\n", " a.I, a.V, a.m, a.b, a.dt, a.T, a.window_width, a.savefig, \\\n", " a.damping\n", "\n", " filename = 'vibration_sim.dat'\n", " u_min, u_max = solve_and_store(filename, I, V, m, b, s,\n", " F, dt, T, damping)\n", "\n", " read_and_plot(filename, u_min, u_max)\n", "\n", "def read_and_plot(filename, u_min, u_max):\n", " \"\"\"\n", " Read file and plot u vs t line by line in a\n", " terminal window (only using ascii characters).\n", " \"\"\"\n", " from scitools.avplotter import Plotter\n", " import time\n", " umin = 1.2*u_min; umax = 1.2*u_max\n", " p = Plotter(ymin=umin, ymax=umax, width=60, symbols='+o')\n", " fps = 10\n", " infile = open(filename, 'r')\n", "\n", " # read and treat one line at a time\n", " infile.readline() # skip header line\n", " for line in infile:\n", " time_and_pos = line.split() # gives list with 2 elements\n", " t = float(time_and_pos[0])\n", " u = float(time_and_pos[1])\n", " #print 'time: %g position: %g' % (time, pos)\n", " print p.plot(t, u), '%.2f' % (t)\n", " time.sleep(1/float(fps))\n", "\n", "if __name__ == '__main__':\n", " main()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "\n", "Filename: `vib_memsave`.\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "## Exercise 16: Implement the Euler-Cromer scheme for the generalized model\n", "
\n", "\n", "We consider the generalized model problem" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "$$\n", "mu'' + f(u') + s(u) = F(t),\\quad u(0)=I,\\ u'(0)=V\\thinspace .\n", "$$" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "**a)**\n", "Implement the Euler-Cromer method from the section [vib:ode2:EulerCromer](#vib:ode2:EulerCromer).\n", "\n", "\n", "\n", "**Solution.**\n", "A suitable function is" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# NBVAL_SKIP\n", "import numpy as np\n", "from math import pi\n", "\n", "def solver(I, V, m, b, s, F, dt, T, damping='linear'):\n", " \"\"\"\n", " Solve m*u'' + f(u') + s(u) = F(t) for t in (0,T], u(0)=I,\n", " u'(0)=V by an Euler-Cromer method.\n", " \"\"\"\n", " f = lambda v: b*v if damping == 'linear' else b*abs(v)*v\n", " dt = float(dt)\n", " Nt = int(round(T/dt))\n", " u = np.zeros(Nt+1)\n", " v = np.zeros(Nt+1)\n", " t = np.linspace(0, Nt*dt, Nt+1)\n", "\n", " v[0] = V\n", " u[0] = I\n", " for n in range(0, Nt):\n", " v[n+1] = v[n] + dt*(1./m)*(F(t[n]) - s(u[n]) - f(v[n]))\n", " u[n+1] = u[n] + dt*v[n+1]\n", " #print 'F=%g, s=%g, f=%g, v_prev=%g' % (F(t[n]), s(u[n]), f(v[n]), v[n])\n", " #print 'v[%d]=%g u[%d]=%g' % (n+1,v[n+1],n+1,u[n+1])\n", " return u, v, t" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "\n", "\n", "**b)**\n", "We expect the Euler-Cromer method to have first-order convergence rate.\n", "Make a unit test based on this expectation.\n", "\n", "\n", "\n", "**Solution.**\n", "We may use SymPy to derive a problem based on a manufactured solution\n", "$u=3\\cos t$. Then we may run some $\\Delta t$ values, compute the error\n", "divided by $\\Delta t$, and check that this ratio remains approximately\n", "constant. (An alternative is to compute true convergence rates and check\n", "that they are close to unity.)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# NBVAL_SKIP\n", "def test_solver():\n", " \"\"\"Check 1st order convergence rate.\"\"\"\n", " m = 4; b = 0.1\n", " s = lambda u: 2*u\n", " f = lambda v: b*v\n", "\n", " import sympy as sym\n", " def ode(u):\n", " \"\"\"Return source F(t) in ODE for given manufactured u.\"\"\"\n", " print 'ode:', m*sym.diff(u, t, 2), f(sym.diff(u,t)), s(u)\n", " return m*sym.diff(u, t, 2) + f(sym.diff(u,t)) + s(u)\n", "\n", " t = sym.symbols('t')\n", " u = 3*sym.cos(t)\n", " F = ode(u)\n", " F = sym.simplify(F)\n", " print 'F:', F, 'u:', u\n", " F = sym.lambdify([t], F, modules='numpy')\n", " u_exact = sym.lambdify([t], u, modules='numpy')\n", " I = u_exact(0)\n", " V = sym.diff(u, t).subs(t, 0)\n", " print 'V:', V, 'I:', I\n", "\n", " # Numerical parameters\n", " w = np.sqrt(0.5)\n", " P = 2*pi/w\n", " dt_values = [P/20, P/40, P/80, P/160, P/320]\n", " T = 8*P\n", " error_vs_dt = []\n", " for n, dt in enumerate(dt_values):\n", " u, v, t = solver(I, V, m, b, s, F, dt, T, damping='linear')\n", " error = np.abs(u - u_exact(t)).max()\n", " if n > 0:\n", " error_vs_dt.append(error/dt)\n", " for i in range(len(error_vs_dt)):\n", " assert abs(error_vs_dt[i]-\n", " error_vs_dt[0]) < 0.1" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "\n", "\n", "**c)**\n", "Consider a system with $m=4$, $f(v)=b|v|v$, $b=0.2$, $s=2u$, $F=0$.\n", "Compute the solution using the centered difference scheme\n", "from the section [vib:ode2:fdm:flin](#vib:ode2:fdm:flin) and the Euler-Cromer scheme\n", "for the longest possible time step $\\Delta t$. We can use the\n", "result from the case without damping, i.e., the largest $\\Delta t= 2/\\omega$,\n", "$\\omega\\approx\n", "\\sqrt{0.5}$ in this case, but since $b$ will modify the frequency, we\n", "take the longest possible time step as a safety factor 0.9 times $2/\\omega$.\n", "Refine $\\Delta t$ three times by a factor of two and compare the\n", "two curves.\n", "\n", "\n", "\n", "**Solution.**\n", "We rely on the module `vib` for the implementation of the method\n", "from the section [vib:ode2:fdm:flin](#vib:ode2:fdm:flin). A suitable function for making\n", "the comparisons is then" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# NBVAL_SKIP\n", "def demo():\n", " \"\"\"\n", " Demonstrate difference between Euler-Cromer and the\n", " scheme for the corresponding 2nd-order ODE.\n", " \"\"\"\n", " I = 1.2; V = 0.2; m = 4; b = 0.2\n", " s = lambda u: 2*u\n", " F = lambda t: 0\n", " w = np.sqrt(2./4) # approx freq\n", " dt = 0.9*2/w # longest possible time step\n", " w = 0.5\n", " P = 2*pi/w\n", " T = 4*P\n", " from vib import solver as solver2\n", " import scitools.std as plt\n", " for k in range(4):\n", " u2, t2 = solver2(I, V, m, b, s, F, dt, T, 'quadratic')\n", " u, v, t = solver(I, V, m, b, s, F, dt, T, 'quadratic')\n", " plt.figure()\n", " plt.plot(t, u, 'r-', t2, u2, 'b-')\n", " plt.legend(['Euler-Cromer', 'centered scheme'])\n", " plt.title('dt=%.3g' % dt)\n", " raw_input()\n", " plt.savefig('tmp_%d' % k + '.png')\n", " plt.savefig('tmp_%d' % k + '.pdf')\n", " dt /= 2" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "\n", "\n", "\n", "Filename: `vib_EulerCromer`.\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "## Problem 17: Interpret $[D_tD_t u]^n$ as a forward-backward difference\n", "
\n", "\n", "Show that the difference $[D_t D_tu]^n$ is equal to $[D_t^+D_t^-u]^n$\n", "and $D_t^-D_t^+u]^n$. That is, instead of applying a centered difference\n", "twice one can alternatively apply a mixture of forward and backward\n", "differences.\n", "\n", "\n", "\n", "**Solution.**" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "$$\n", "[D_t^+D_t^-u]^n = [D_t^+\\left(\\frac{u^n - u^{n-1}}{\\Delta t}\\right)]^n \\nonumber\n", "$$" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "$$\n", "= [\\left(\\frac{u^{n+1} - u^n}{\\Delta t}\\right) - \\left(\\frac{u^n - u^{n-1}}{\\Delta t}\\right)]^n \\nonumber\n", "$$" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "$$\n", "= \\frac{u^{n+1}-2u^n+u^{n-1}}{\\Delta t^2} \\nonumber\n", "$$" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "$$\n", "= [D_t D_tu]^n. \\nonumber\n", "$$" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Similarly, we get that" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "$$\n", "[D_t^-D_t^+u]^n = [D_t^-\\left(\\frac{u^{n+1} - u^n}{\\Delta t}\\right)]^n \\nonumber\n", "$$" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "$$\n", "= [\\left(\\frac{u^{n+1} - u^n}{\\Delta t}\\right) - \\left(\\frac{u^n - u^{n-1}}{\\Delta t}\\right)]^n \\nonumber\n", "$$" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "$$\n", "= \\frac{u^{n+1}-2u^n+u^{n-1}}{\\Delta t^2} \\nonumber\n", "$$" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "$$\n", "= [D_t D_tu]^n. \\nonumber\n", "$$" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "\n", "Filename: `vib_DtDt_fw_bw`.\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "## Exercise 18: Analysis of the Euler-Cromer scheme\n", "
\n", "\n", "The Euler-Cromer scheme for the model problem\n", "$u^{\\prime\\prime} + \\omega^2 u =0$, $u(0)=I$, $u^{\\prime}(0)=0$, is given in\n", "([55](#vib:model2x2:EulerCromer:ueq1b))-([54](#vib:model2x2:EulerCromer:veq1b)).\n", "Find the exact discrete solutions of this scheme and show that the solution\n", "for $u^n$ coincides with that found in the section [Analysis of the numerical scheme](#vib:ode1:analysis).\n", "\n", "\n", "\n", "**Hint.**\n", "Use an \"ansatz\" $u^n=I\\exp{(i\\tilde\\omega\\Delta t\\,n)}$ and\n", "$v^n=qu^n$, where $\\tilde\\omega$ and $q$ are unknown parameters. The\n", "following formula is handy:" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "$$\n", "\\e^{i\\tilde\\omega\\Delta t} + e^{i\\tilde\\omega(-\\Delta t)} - 2\n", "= 2\\left(\\cosh(i\\tilde\\omega\\Delta t) -1 \\right)\n", "=-4\\sin^2(\\frac{\\tilde\\omega\\Delta t}{2})\\thinspace .\n", "$$" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "\n", "\n", "\n", "\n", "**Solution.**\n", "We follow the ideas in the section [Analysis of the numerical scheme](#vib:ode1:analysis). Inserting\n", "$u^n=I\\exp{(i\\tilde\\omega\\Delta t\\,n)}$ and\n", "$v^n=qu^n$ in\n", "([55](#vib:model2x2:EulerCromer:ueq1b))-([54](#vib:model2x2:EulerCromer:veq1b))\n", "and dividing by $I\\exp{(i\\tilde\\omega\\Delta t\\,n)}$ gives" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "\n", "
\n", "\n", "$$\n", "\\begin{equation}\n", "q\\exp{(i\\tilde\\omega\\Delta t)} = q - \\omega^2 \\Delta t,\n", "\\label{vib:exer:EulerCromer:analysis:equ} \\tag{73} \n", "\\end{equation}\n", "$$" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "\n", "
\n", "\n", "$$\n", "\\begin{equation} \n", "\\exp{(i\\tilde\\omega\\Delta t)} = 1 + \\Delta t\\, q\\exp{(i\\tilde\\omega\\Delta t)}\n", "\\label{vib:exer:EulerCromer:analysis:eqv} \\tag{74}\\thinspace .\n", "\\end{equation}\n", "$$" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Solving ([74](#vib:exer:EulerCromer:analysis:eqv)) with respect to $q$ gives" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "$$\n", "q = \\frac{1}{\\Delta t}\\left( 1 - \\exp{(i\\tilde\\omega\\Delta t)} \\right)\\thinspace .\n", "$$" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Inserting this expression for $q$ in ([73](#vib:exer:EulerCromer:analysis:equ))\n", "results in" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "$$\n", "\\exp{(i\\tilde\\omega\\Delta t)} + \\exp{(-i\\tilde\\omega\\Delta t)} -2\n", "= - \\omega^2\\Delta t^2\\thinspace .\n", "$$" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Using the relation\n", "$\\exp{(i\\tilde\\omega(\\Delta t))} + \\exp{(i\\tilde\\omega(-\\Delta t))} - 2\n", "= -4\\sin^2(\\frac{\\tilde\\omega\\Delta t}{2})$ gives" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "$$\n", "-4\\sin^2(\\frac{\\tilde\\omega\\Delta t}{2}) = - \\omega^2\\Delta t^2,\n", "$$" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "or after dividing by 4," ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "$$\n", "\\sin^2(\\frac{\\tilde\\omega\\Delta t}{2}) = \\left(\\frac{1}{2}\\omega\\Delta t\\right)^2,\n", "$$" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "which is the same equation for $\\tilde\\omega$ as found in\n", "the section [Analysis of the numerical scheme](#vib:ode1:analysis), such that $\\tilde\\omega$ is the\n", "same. The accuracy, stability, and formula for the exact discrete solution\n", "are then all the same as derived in the section [Analysis of the numerical scheme](#vib:ode1:analysis).\n", "\n", "\n", "\n", "\n", "\n", "" ] } ], "metadata": { "kernelspec": { "display_name": "Python 3", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.8.2" } }, "nbformat": 4, "nbformat_minor": 4 }