\documentclass[12pt]{article} %\usepackage{fullpage} \usepackage{amsmath,amssymb} \begin{document} \title{Two Dimensional Euler Simulation} \author{ S.J. Montgomery-Smith\\ Department of Mathematics\\ University of Missouri\\ Columbia, MO 65211, U.S.A.\\ stephen@math.missouri.edu\\ http://www.math.missouri.edu/\~{}stephen} \date{September 10, 2000} \maketitle This document describes a program I wrote to simulate the two dimensional Euler Equation --- a program that is part of the {\tt xlock} screensaver as the {\tt euler2d} mode. A similar explanation may also be found in the book by Chorin \cite{C}. \section{The Euler Equation} The Euler Equation describes the motion of an incompressible fluid that has no viscosity. If the fluid is contained in a domain $\Omega$ with boundary $\partial \Omega$, then the equation is in the vector field $u$ (the velocity) and the scalar field $p$ (the pressure): \begin{eqnarray*} \frac{\partial}{\partial t} u &=& -u \cdot \nabla u + \nabla p \\ \nabla \cdot u &=& 0 \\ u \cdot n &=& 0 \quad \text{on $\partial \Omega$} \end{eqnarray*} where $n$ is the unit normal to $\partial \Omega$. \section{Vorticity} It turns out that it can be easier write these equations in terms of the vorticity. In two dimensions the vorticity is the scalar $w = \partial u_2/\partial x - \partial u_1/\partial y$. The equation for vorticity becomes \[ \frac{\partial}{\partial t} w = -u \cdot \nabla w .\] A solution to this equation can be written as follows. The velocity $u$ causes a flow, that is, a function $\varphi(t,x)$ that tells where the particle initially at $x$ ends up at time $t$, that is \[ \frac\partial{\partial t} \varphi(t,x) = u(t,\varphi(t,x)) .\] Then the equation for $w$ tells us that the vorticity is ``pushed'' around by the flow, that is, $w(t,\varphi(t,x)) = w(0,x)$. \section{The Biot-Savart Kernel} Now, once we have the vorticity, we can recover the velocity $u$ by solving the equation \begin{eqnarray*} \partial u_2/\partial x - \partial u_1/\partial y &=& w \\ \nabla \cdot u &=& 0 \\ u \cdot n &=& 0 \quad \text{on $\partial \Omega$}. \end{eqnarray*} This equation is solved by using a Biot-Savart kernel $K(x,y)$: $$ u(x) = \int_\Omega K(x,y) w(y) \, dy .$$ The function $K$ depends upon the choice of domain. First let us consider the case when $\Omega$ is the whole plane (in which case the boundary condition $u \cdot n = 0$ is replaced by saying that $u$ decays at infinity). Then \begin{equation*} K(x,y) = K_1(x,y) = c \frac{(x-y)^\perp}{|x-y|^2} . \end{equation*} Here $x^\perp = (-x_2,x_1)$, and $c$ is a constant, probably something like $1/2\pi$. In any case we will set it to be one, which in effect is rescaling the time variable, so we don't need to worry about it. We can use this as a basis to find $K$ on the unit disk $\Omega = \Delta = \{x:|x|<1\}$. It turns out to be \begin{equation*} K_2(x,y) = K_1(x,y) - K_1(x,y^*) , \end{equation*} where $y^* = y/|y|^2$ is called the reflection of $y$ about the boundary of the unit disk. Another example is if we have a bijective analytic function $p:\Delta \to {\mathbb C}$, and we let $\Omega = p(\Delta)$. (Here we think of $\Delta$ as a subset of $\mathbb C$, that is, we are identifying the plane with the set of complex numbers.) In that case we get \[ K_p(p(x),p(y)) = K_2(x,y)/|p'(x)|^2 .\] Our simulation considers the last case. Examples of such analytic functions include series $p(x) = x + \sum_{n=2}^\infty c_n x^n$, where $\sum_{n=2}^\infty n |c_n| \le 1$. (Thanks to David Ullrich for pointing this out to me.) \section{The Simulation} Now let's get to decribing the simulation. We assume a rather unusual initial distribution for the vorticity --- that the vorticity is a finite sum of dirac delta masses. \[ w(0,x) = \sum_{k=1}^N w_k \delta(x-x_k(0)) .\] Here $x_k(0)$ is the initial place where the points of vorticity are concentrated, with values $w_k$. Then at time $t$, the vorticity becomes \[ w(t,x) = \sum_{k=1}^N w_k \delta(x-x_k(t)) .\] The points of fluid $x_k(t)$ are pushed by the flow, that is, $x_k(t) = \varphi(t,x_k(0))$, or \[ \frac{\partial}{\partial t} x_k(t) = u(t,x_k(t)) .\] Putting this all together, we finally obtain the equations \[ \frac{\partial}{\partial t} x_k = \alpha_k \] where \[ \alpha_k = \sum_{l=1}^N w_l K(x_k,x_l) .\] This is the equation that our simulation solves. In fact, in our case, where the domain is $p(\Delta)$, the points are described by points $\tilde x_k$, where $x_k = p(\tilde x_k)$. Then the equations become \begin{eqnarray} \label{tildex-p1} \frac{\partial}{\partial t} \tilde x_k &=& \tilde\alpha_k \\ \label{tildex-p2} \tilde\alpha_k &=& \frac1{|p'(\tilde x_k)|^2} \sum_{l=1}^N w_l K_2(\tilde x_k,\tilde x_l) . \end{eqnarray} We solve this $2N$ system of equations using standard numerical methods, in our case, using the second order midpoint method for the first step, and thereafter using the second order Adams-Bashforth method. (See for example the book by Burden and Faires \cite{BF}). \section{The Program - Data Structures} The computer program solves equation (\ref{tildex-p1}), and displays the results on the screen, with a boundary. All the information for solving the equation and displaying the output is countained in the structure {\tt euler2dstruct}. Let us describe some of the fields in {\tt euler2dstruct}. The points $\tilde x_k$ are contained in {\tt double *x}: with the coordinates of $\tilde x_k$ being the two numbers {\tt x[2*k+0]}, {\tt x[2*k+1]}. The values $w_k$ are contained in {\tt double *w}. The total number of points is {\tt int N}. (But only the first {\tt int Nvortex} points have $w_k \ne 0$.) The coefficients of the analytic function (in our case a polynomial) $p$ are contained in {\tt double p\_coef[2*(deg\_p-1)]} --- here {\tt deg\_p} is the degree of $p$, and the real and imaginary parts of the coefficient $c_n$ is contained in {\tt p\_coef[2*(n-2)+0]} and {\tt p\_coef[2*(n-2)+1]}. \section{Data Initialization} The program starts in the function {\tt init\_euler2d}. After allocating the memory for the data, and initialising some of the temporary variables required for the numerical solving program, it randomly assigns the coefficients of $p$, making sure that $\sum_{n=2}^{\tt deg\_p} n |c_n| = 1$. Then the program figures out how to draw the boundary, and what rescaling of the data is required to draw it on the screen. (This uses the function {\tt calc\_p} which calculates $p(x)$.) Next, it randomly assigns the initial values of $\tilde x_k$. We want to do this in such a way so that the points are uniformly spread over the domain. Let us first consider the case when the domain is the unit circle $\Delta$. In that case the proportion of points that we would expect inside the circle of radius $r$ would be proportional to $r^2$. So we do it as follows: \[ r = \sqrt{R_{0,1}},\quad \theta = R_{-\pi,\pi}, \quad \tilde x_k = r (\cos \theta, \sin \theta) .\] Here, and in the rest of this discussion, $R_{a,b}$ is a function that returns a random variable uniformly distributed over the interval $[a,b]$. This works fine for $\Delta$, but for $p(\Delta)$, the points $p(\tilde x_k)$ are not uniformly distributed over $p(\Delta)$, but are distributed with a density proportional to $1/|p'(\tilde x_k)|^2$. So to restore the uniform density we need to reject this value of $\tilde x_k$ with probability proportional to $|p'(\tilde x_k)|^2$. Noticing that the condition $\sum_{n=2}^{\tt deg\_p} n |c_n| = 1$ implies that $|p'(\tilde x_k)| \le 2$, we do this by rejecting if $|p'(\tilde x_k)|^2 < R_{0,4}$. (This makes use of the function {\tt calc\_mod\_dp2} which calculates $|p'(x)|^2$.) \section{Solving the Equation} The main loop of the program is in the function {\tt draw\_euler2d}. Most of the drawing operations are contained in this function, and the numerical aspects are sent to the function {\tt ode\_solve}. But there is an aspect of this that I would like to discuss in the next section, and so we will look at a simple method for numerically solving differential equations. The Euler Method (nothing to do with the Euler Equation), is as follows. Pick a small number $h$ --- the time step (in the program call {\tt delta\_t}). Then we approximate the solution of the equation: \begin{equation} \label{method-simple} \tilde x_k(t+h) = \tilde x_k(t) + h \tilde\alpha_k(t) . \end{equation} The more sophisticated methods we use are variations of the Euler Method, and so the discussion in the following section still applies. In the program, the quantities $\tilde\alpha_k$, given by equations (\ref{tildex-p2}) are calculated by the function {\tt derivs} (which in turns calls {\tt calc\_all\_mod\_dp2} to calculate $|p'(\tilde x_k)|^2$ at all the points). \section{Subtle Perturbation} Added later: the scheme described here seems to not be that effective, so now it is not used. One problem using a numerical scheme such as the Euler Method occurs when the points $\tilde x_k$ get close to the boundary of $\Delta$. In that case, it is possible that the new points will be pushed outside of the boundary. Even if they are not pushed out of the boundary, they may be much closer or farther from the boundary than they should be. Our system of equations is very sensitive to how close points are to the boundary --- points with non-zero vorticity (``vortex points'') that are close to the boundary travel at great speed alongside the boundary, with speed that is inversely proportional to the distance from the boundary. A way to try to mitigate this problem is something that I call ``subtle perturbation.'' We map the points in the unit disk to points in the plane using the map \begin{equation*} F(x) = f(|x|) \frac x{|x|} , \end{equation*} where $f:[0,1]\to[0,\infty]$ is an increasing continuous bijection. It turns out that a good choice is \begin{equation*} f(t) = -\log(1-t) . \end{equation*} (The reason for this is that points close to each other that are a distance about $r$ from the boundary will be pushed around so that their distance from each other is about multiplied by the derivative of $\log r$, that is, $1/r$.) Note that the inverse of this function is given by \begin{equation*} F^{-1}(x) = f^{-1}(|x|) \frac x{|x|} , \end{equation*} where \begin{equation*} f^{-1}(t) = 1-e^{-t} . \end{equation*} So what we could do is the following: instead of working with the points $\tilde x_k$, we could work instead with the points $y_k = F(\tilde x_k)$. In effect this is what we do. Instead of performing the computation (\ref{method-simple}), we do the calculation \begin{equation*} y_k = F(\tilde x_k(t)) + h {\cal A}(\tilde x_k) \tilde\alpha_k(t) \end{equation*} where ${\cal A}(x)$ is the matrix of partial derivatives of $F$: \begin{equation*} {\cal A}(x) = \frac{f(|x|)}{|x|} \left[ \begin{matrix} 1 & 0\\ 0 & 1 \end{matrix} \right] + \frac1{|x|} \left(\frac{f'(|x|)}{|x|} - \frac{f(|x|)}{|x|^2}\right) \left[ \begin{matrix} x_{1}^2 & x_{1} x_{2}\\ x_{1} x_{2} & x_{2}^2 \end{matrix} \right], \end{equation*} and then compute \begin{equation*} \tilde x_k(t+h) = F^{-1}(y_k). \end{equation*} These calculations are done in the function {\tt perturb}, if the quantity {\tt SUBTLE\_PERTURB} is set. \section{Drawing the Points} As we stated earlier, most of the drawing functions are contained in the function {\tt draw\_euler2d}. If the variable {\tt hide\_vortex} is set (and the function {\tt init\_euler2d} will set this with probability $3/4$), then we only display the points $\tilde x_k$ for ${\tt Nvortex} < k \le N$. If {\tt hide\_vortex} is not set, then the ``vortex points'' $\tilde x_k$ ($1 \le k \le {\tt Nvortex}$) are displayed in white. In fact the points $p(\tilde x_k)$ are what are put onto the screen, and for this we make use of the function {\tt calc\_all\_p}. \section{Addition to Program: Changing the Power Law} A later addition to the program adds an option {\tt eulerpower}, which allows one to change the power law that describes how the vortex points influence other points. In effect, if this option is set with the value $m$, then the Biot-Savart Kernel is replace by $$ K_1(x,y) = \frac{(x-y)^\perp}{|x-y|^{m+1}}, $$ and $$ K_2(x,y) = K_1(x,y) - |y|^{1-m} K_1(x,y) .$$ So for example, setting $m=2$ corresponds to the quasi-geostrophic equation. (I haven't yet figured out what $K_p$ should be, so if $m \ne 1$ we use the unit circle as the boundary.) \begin{thebibliography}{9} \bibitem{BF} Richard L. Burden, J. Douglas Faires, Numerical Analysis, sixth edition, Brooks/Cole, 1996. \bibitem{C} Alexandre J. Chorin, Vorticity and Turbulence, Applied Mathematical Sciences, Vol 103, Springer Verlag, 1994. \end{thebibliography} \end{document}