Marker-and-Cell (MAC) Grid

We want to be able to simulate Continuum Mechanics, and more specifically Fluid Mechanics.

A crude and naive way to do is to split the world into a 3D grid of cells, labeled whether it is an obstacle, fluid, or empty space. The fluid velocity is defined on the cell wall.

"\\usepackage{tikz}\n\n\\begin{document}\n\\begin{tikzpicture}[scale=0.8, every node/.style={scale=1.0}]\n\n% Grid lines\n\\draw (0,0) grid (8,8);\n\n% Native TikZ mix colors (safest for Obsidian's dark mode SVG inversion)\n\\colorlet{lightgraycell}{black!35}\n\\colorlet{darkgraycell}{black!70}\n\n% --- Shading and Text for Cells ---\n\n% Row 1 (top, y=7 to y=8)\n\\foreach \\x in {0,...,7} {\n \\fill[white] (\\x,7) rectangle (\\x+1,8);\n \\node at (\\x+0.5,7.5) [font=\\huge] {E};\n}\n\n% Row 2 (y=6 to y=7)\n\\fill[white] (0,6) rectangle (1,7); \\node at (0.5,6.5) [font=\\huge] {E};\n\\fill[lightgraycell] (1,6) rectangle (2,7); \\node at (1.5,6.5) [font=\\huge\\bfseries] {F};\n\\foreach \\x in {2,...,7} {\n \\fill[white] (\\x,6) rectangle (\\x+1,7);\n \\node at (\\x+0.5,6.5) [font=\\huge] {E};\n}\n\n% Row 3 (y=5 to y=6)\n\\fill[darkgraycell] (0,5) rectangle (1,6); \\node at (0.5,5.5) [font=\\huge, text=white] {S};\n\\fill[white] (1,5) rectangle (2,6); \\node at (1.5,5.5) [font=\\huge] {E};\n\\fill[white] (2,5) rectangle (3,6); \\node at (2.5,5.5) [font=\\huge] {E};\n\\fill[lightgraycell] (3,5) rectangle (4,6); \\node at (3.5,5.5) [font=\\huge\\bfseries] {F};\n\\fill[lightgraycell] (4,5) rectangle (5,6); \\node at (4.5,5.5) [font=\\huge\\bfseries] {F};\n\\fill[white] (5,5) rectangle (6,6); \\node at (5.5,5.5) [font=\\huge] {E};\n\\fill[white] (6,5) rectangle (7,6); \\node at (6.5,5.5) [font=\\huge] {E};\n\\fill[darkgraycell] (7,5) rectangle (8,6); \\node at (7.5,5.5) [font=\\huge, text=white] {S};\n\n% Row 4 (y=4 to y=5)\n\\fill[darkgraycell] (0,4) rectangle (1,5); \\node at (0.5,4.5) [font=\\huge, text=white] {S};\n\\fill[white] (1,4) rectangle (2,5); \\node at (1.5,4.5) [font=\\huge] {E};\n\\fill[white] (2,4) rectangle (3,5); \\node at (2.5,4.5) [font=\\huge] {E};\n\\fill[white] (3,4) rectangle (4,5); \\node at (3.5,4.5) [font=\\huge] {E};\n\\fill[lightgraycell] (4,4) rectangle (5,5); \\node at (4.5,4.5) [font=\\huge\\bfseries] {F};\n\\fill[lightgraycell] (5,4) rectangle (6,5); \\node at (5.5,4.5) [font=\\huge\\bfseries] {F};\n\\fill[lightgraycell] (6,4) rectangle (7,5); \\node at (6.5,4.5) [font=\\huge\\bfseries] {F};\n\\fill[darkgraycell] (7,4) rectangle (8,5); \\node at (7.5,4.5) [font=\\huge, text=white] {S};\n\n% Row 5 (y=3 to y=4)\n\\fill[darkgraycell] (0,3) rectangle (1,4); \\node at (0.5,3.5) [font=\\huge, text=white] {S};\n\\fill[white] (1,3) rectangle (2,4); \\node at (1.5,3.5) [font=\\huge] {E};\n\\foreach \\x in {2,...,6} {\n \\fill[lightgraycell] (\\x,3) rectangle (\\x+1,4);\n \\node at (\\x+0.5,3.5) [font=\\huge\\bfseries] {F};\n}\n\\fill[darkgraycell] (7,3) rectangle (8,4); \\node at (7.5,3.5) [font=\\huge, text=white] {S};\n\n% Row 6 (y=2 to y=3)\n\\fill[darkgraycell] (0,2) rectangle (1,3); \\node at (0.5,2.5) [font=\\huge, text=white] {S};\n\\foreach \\x in {1,...,5} {\n \\fill[lightgraycell] (\\x,2) rectangle (\\x+1,3);\n \\node at (\\x+0.5,2.5) [font=\\huge\\bfseries] {F};\n}\n\\fill[darkgraycell] (6,2) rectangle (7,3); \\node at (6.5,2.5) [font=\\huge, text=white] {S};\n\\fill[darkgraycell] (7,2) rectangle (8,3); \\node at (7.5,2.5) [font=\\huge, text=white] {S};\n\n% Row 7 (y=1 to y=2)\n\\fill[darkgraycell] (0,1) rectangle (1,2); \\node at (0.5,1.5) [font=\\huge, text=white] {S};\n\\foreach \\x in {1,...,3} {\n \\fill[lightgraycell] (\\x,1) rectangle (\\x+1,2);\n \\node at (\\x+0.5,1.5) [font=\\huge\\bfseries] {F};\n}\n\\foreach \\x in {4,...,7} {\n \\fill[darkgraycell] (\\x,1) rectangle (\\x+1,2);\n \\node at (\\x+0.5,1.5) [font=\\huge, text=white] {S};\n}\n\n% Row 8 (bottom, y=0 to y=1)\n\\foreach \\x in {0,...,7} {\n \\fill[darkgraycell] (\\x,0) rectangle (\\x+1,1);\n \\node at (\\x+0.5,0.5) [font=\\huge, text=white] {S};\n}\n\n% Draw border around the entire grid last for clean look\n\\draw[ultra thick] (0,0) rectangle (8,8);\n\n\\end{tikzpicture}\n\\end{document}"EEEEEEEEEFEEEEEESEEFFEESSEEEFFFSSEFFFFFSSFFFFFSSSFFFSSSSSSSSSSSS
source code

The fluid velocity has 3 components

like 3 scalar fields, with defined on the faces normal to the direction (and similarly with the other two). Velocity on a wall next to a solid cell must be .

"\\usepackage{amsmath}\n\\usepackage{amssymb}\n\\usepackage{amsfonts}\n\\usepackage{xcolor}\n\n\\begin{document}\n\\begin{tikzpicture}[>=stealth, line join=round, line cap=round, \n x={(1.3cm,0cm)}, y={(0cm,1.3cm)}, z={(0.6cm,0.45cm)}]\n\n% --- Define Custom Colors Matching the Image ---\n\\definecolor{myblue}{RGB}{35, 75, 165}\n\\definecolor{fillblue}{RGB}{145, 175, 215}\n\n\\definecolor{myorange}{RGB}{200, 60, 15}\n\\definecolor{fillorange}{RGB}{240, 155, 115}\n\n\\definecolor{myteal}{RGB}{40, 145, 130}\n\\definecolor{fillteal}{RGB}{160, 215, 200}\n\n% =========================================================\n% BACKGROUND GRIDS (Solid Lines)\n% =========================================================\n\n% Top Face Grid\n\\foreach \\x in {0,...,4} {\n \\draw[thick] (\\x, 4, 0) -- (\\x, 4, 3);\n}\n\\foreach \\z in {0,...,3} {\n \\draw[thick] (0, 4, \\z) -- (4, 4, \\z);\n}\n\n% Right Face Grid\n\\foreach \\y in {0,...,4} {\n \\draw[thick] (4, \\y, 0) -- (4, \\y, 3);\n}\n\\foreach \\z in {0,...,3} {\n \\draw[thick] (4, 0, \\z) -- (4, 4, \\z);\n}\n\n% =========================================================\n% INTERNAL GRIDS (Dashed Lines)\n% =========================================================\n\n% Internal Dashed Grid at z=1 (Back of the first cell layer)\n\\foreach \\x in {1,...,3} {\n \\draw[thick, dashed] (\\x, 0, 1) -- (\\x, 4, 1);\n}\n\\foreach \\y in {1,...,3} {\n \\draw[thick, dashed] (0, \\y, 1) -- (4, \\y, 1);\n}\n\n% Depth Dashed Lines (z=0 to z=1)\n% Interior depth lines\n\\foreach \\x in {1,...,3} {\n \\foreach \\y in {1,...,3} {\n \\draw[thick, dashed] (\\x, \\y, 0) -- (\\x, \\y, 1);\n }\n}\n% Left edge depth lines\n\\foreach \\y in {1,...,3} {\n \\draw[thick, dashed] (0, \\y, 0) -- (0, \\y, 1);\n}\n% Bottom edge depth lines\n\\foreach \\x in {1,...,3} {\n \\draw[thick, dashed] (\\x, 0, 0) -- (\\x, 0, 1);\n}\n\n% =========================================================\n% STAGGERED GRID FACES (Colored Fills)\n% =========================================================\n\n% u3 (Blue) - Front face (y=2..3, x=0..1, z=0)\n\\fill[fillblue, fill opacity=0.8] (0,2,0) -- (1,2,0) -- (1,3,0) -- (0,3,0) -- cycle;\n\n% u1 (Orange) - Right side face (y=2..3, x=2, z=0..1)\n\\fill[fillorange, fill opacity=0.8] (2,2,0) -- (2,3,0) -- (2,3,1) -- (2,2,1) -- cycle;\n\n% u2 (Teal) - Top side face (y=2, x=2..3, z=0..1)\n\\fill[fillteal, fill opacity=0.8] (2,2,0) -- (3,2,0) -- (3,2,1) -- (2,2,1) -- cycle;\n\n% =========================================================\n% FRONT GRID (Solid Lines)\n% =========================================================\n\n\\foreach \\x in {0,...,4} {\n \\draw[thick] (\\x, 0, 0) -- (\\x, 4, 0);\n}\n\\foreach \\y in {0,...,4} {\n \\draw[thick] (0, \\y, 0) -- (4, \\y, 0);\n}\n\n% =========================================================\n% COLORED BORDERS (Drawn over front grid for visibility)\n% =========================================================\n\n\\draw[myblue, dashed, very thick] (0,2,0) -- (1,2,0) -- (1,3,0) -- (0,3,0) -- cycle;\n\\draw[myorange, dashed, very thick] (2,2,0) -- (2,3,0) -- (2,3,1) -- (2,2,1) -- cycle;\n\\draw[myteal, dashed, very thick] (2,2,0) -- (3,2,0) -- (3,2,1) -- (2,2,1) -- cycle;\n\n% =========================================================\n% CELL CENTERS (Black Dots)\n% =========================================================\n\n\\foreach \\x in {0.5, 1.5, 2.5, 3.5} {\n \\foreach \\y in {0.5, 1.5, 2.5, 3.5} {\n \\fill (\\x, \\y, 0.5) circle (4pt);\n }\n}\n\n% =========================================================\n% LABELS\n% =========================================================\n\n\\node[font=\\Huge, text=myblue] at (-0.4, 2.5, 0) {$u^3$};\n\\node[font=\\Huge, text=myorange] at (1.8, 4.3, 0) {$u^1$};\n\\node[font=\\Huge, text=myteal] at (3.5, 2.3, 0.5) {$u^2$};\n\n\\end{tikzpicture}\n\\end{document}"u3u1u2
source code
  • Let denote the set of faces incident to both fluid cells.
  • Let denote the set of fluid cells.
  • Velocity field is a data .
  • Scalar quantities like pressure and density are stored at cell center.

The divergence of the velocity field is defined on fluid cells

where (a sparse matrix) is defined by aggregating the total flux from each cell wall.

Pressure Projection

Given non divergence free , (some velocity field generated without respect to pressure), we need to try and project it to . The idea here is that consists of all velocity fields with exactly zero divergence. The goal of the algorithm is to find the closest valid vector in this subspace to our invalid input vector .

Idea

This is mathematically the least-squares projection problem. Again, treat as a linear operator (matrix).

We want to solve for pressure where

and (to satisfy incompressibility). Mathematically this means we need to find a where

Note that is a discrete (negative) gradient operator. So, solving this system is the same as finding where

and . We can also see that the matrix is a discrete Laplacian. Fortunately, this means we can solve this by any linear algebra routine such as conjugate gradient.

Applying FFT (Spectral Method)

If the domain has a simple periodic boundary condition, then we can use the Fast Fourier Transform (FFT) to turn this system into a diagonalized equation with direct solution.

For a fluid to have a simple periodic boundary condition, it means a fluid flows out one side of some volume (like a cube) and flows in seamlessly on the left1.

Let be the tuple that describes the “size” of the periodic domain. The goal is to solve the Poisson problem 2 such that

2 is actually the Laplacian operator, the continuous equivalent of the discrete matrix!

We will discretize both and on a regular grid with resolution . Then apply FFT:

where represent the individual grid cells and represent the individual frequencies. Recall that represent variables in the spatial domain and represent variables in the frequency domain.

This is fast because we can represent the pressure field as a linear combination of Fourier basis functions. More simply, image is the sum of one wave, where is the position in space and is the frequency of the wave. The derivative of is , making this operation on a computer much easier.

Since the simulation exists on a discrete grid of fixed pixels, we need to prevent aliasing2 via the following definition of :

There are two distinct subspaces that our final velocity field can exist in

  1. The subspace of all possible velocity fields where the fluid is perfectly incompressible everywhere (i.e. ).
  2. The subspace of all possible velocity fields where the fluid inside and immediately on the boundary of the solid obstacle is completely stationary (i.e. ).

The solver finds the intersection of both. The alternating projection (pressure project set velocity to zero by enforcing obstacles) will converge to their intersection.

Obstacles

If we want to add obstacles, we can just do Pressure Projection and set velocity in the obstacle to zero.

Time Splitting

Consider the incompressible fluid Euler equation:

Projection Method (Chorin)

For each time step, we will

  1. solve the advection term (for half timestep):
  2. pressure “reflect”

Because we advected the fluid while ignoring pressure, the fluid followed the wrong trajectory for that entire . Projecting it straight back down does not return it to where it would have been if pressure had been applied continuously.

Reflection Method (Strang)

We can solve this by: for each time step, we will

  1. solve the advection term (for half timestep):
  2. pressure “reflect” This reflection across the div-free subspace allows you to push the fluid further along the correct trajectory (via the advection step) before projecting it back down to the div-free subspace.
  3. solve the advection equation (for half timestep):
  4. pressure project
"\\usepackage{amsmath}\n\\usepackage{amssymb}\n\\usepackage{xcolor}\n\n\\begin{document}\n\\begin{tikzpicture}[>=stealth, scale=1.3]\n\n% --- Define Custom Colors ---\n\\definecolor{myteal}{RGB}{85, 175, 155}\n\\definecolor{mypurple}{RGB}{125, 80, 165}\n\n% =========================================================\n% TOP SECTION (Standard Projection)\n% =========================================================\n\n% Horizontal Line (div-free subspace)\n\\draw[line width=1.8pt] (0, 4) -- (6.8, 4) node[right, font=\\large, text=black] {div-free subspace};\n\n% Teal Curved Arrow (Solve)\n\\draw[->, myteal, line width=1.5pt] (0.5, 4) to[out=45, in=190] (3.8, 5.8);\n\n% Teal Text Label\n\\node[myteal, align=left, font=\\large, anchor=west] at (-1.2, 5.0) {Solve\\\\[0.5ex] $\\frac{\\partial \\mathbf{u}}{\\partial t} + \\nabla_{\\mathbf{u}}\\mathbf{u} = 0$};\n\n% Purple Straight Arrow (Projection)\n\\draw[->, mypurple, line width=1.5pt] (3.8, 5.7) -- (3.8, 4.05);\n\n% Purple Text Label\n\\node[mypurple, align=left, font=\\large, anchor=west] at (4.0, 4.9) {Pressure\\\\[0.3ex] projection};\n\n\n% =========================================================\n% BOTTOM SECTION (Higher-order / Reflection)\n% =========================================================\n\n% Horizontal Line (div-free subspace)\n\\draw[line width=1.8pt] (0, 0) -- (6.8, 0) node[right, font=\\large, text=black] {div-free subspace};\n\n% 1. Teal Curved Arrow (Solve)\n\\draw[->, myteal, line width=1.5pt] (0.5, 0) to[out=45, in=190] (2.2, 1.5);\n\n% Teal Text Label\n\\node[myteal, align=left, font=\\large, anchor=west] at (-1.2, 1.0) {Solve\\\\[0.5ex] $\\frac{\\partial \\mathbf{u}}{\\partial t} + \\nabla_{\\mathbf{u}}\\mathbf{u} = 0$};\n\n% 2. Purple Straight Arrow (Reflection - crosses line)\n\\draw[->, mypurple, line width=1.5pt] (2.2, 1.4) -- (2.2, -1.5);\n\n% Purple Text Label (Reflection)\n\\node[mypurple, align=left, font=\\large, anchor=west] at (2.4, 0.8) {Pressure\\\\[0.3ex] reflection};\n\n% 3. Teal Curved Arrow (Returning from below)\n\\draw[->, myteal, line width=1.5pt] (2.2, -1.4) to[out=40, in=190] (4.4, 0.6);\n\n% 4. Purple Straight Arrow (Final Projection)\n\\draw[->, mypurple, line width=1.5pt] (4.4, 0.5) -- (4.4, 0.05);\n\n% Purple Text Label (Projection)\n\\node[mypurple, align=left, font=\\large, anchor=west] at (4.6, 0.6) {Pressure\\\\[0.3ex] projection};\n\n\\node[align=left, font=\\large\\bfseries, text=black, anchor=north west] at (4.8, -0.5) {Less splitting error\\\\[0.3ex] with little additional cost};\n\n\\end{tikzpicture}\n\\end{document}"div-freesubspaceSolve@u@t+ruu=0Pressureprojectiondiv-freesubspaceSolve@u@t+ruu=0Pressurere°ectionPressureprojectionLesssplittingerrorwithlittleadditionalcost
source code

Advection

Let be a time-independent vector field over the world coordinate . A time dependent scalar function is advected by if

where is the Lie Derivative. Equivalently, if is the flow map generated by ( and ) and let (inverse flow map), then an advected function satisfies

In other words, tracing the flow with allows us to see where the fluid originally came from. We can convert this to the language of pullbacks.

In fact,

connecting continuous geometry (the pullback) to discrete numerical methods (the ).

We have two numerical methods for advection.

  1. Eulerian method: approximate the or the pullback operator
  2. Lagrangian method: set quantites on particles (or other geometry) and let the particle move by .

1D Advection of a Function

Suppose we have an advection equation with flow velocity .

Let denote the approximate solution at the th gridpoint and th timestep.

Aside: Von Neumann Stability Analysis

We can analyze the stability of a numerical method by looking at how it amplifies or dampens perfect continuous sine waves . The scaling factor is . If , the wave amplifies and the method is unstable. If , the wave dampens and the method is stable. If , the wave is preserved and the method is neutrally stable.

Central Difference Method

A straightforward approach is to look at the neighboring cells to find the slope. We then step forward in time by using the formula

We can apply Von Neumann Stability Analysis. Let . Then we get that

Because its magnitude is greater than 1, this method is unconditionally unstable, meaning no matter how small we make , the method will always explode.

Using ODE Solvers

We can rewrite the advection equation as an ODE by treating as a parameter.

This equation can still be stable using ODE solvers like Runge-Kutta Method (RK4) or Forward Euler Method or Backward Euler Method.

But this leaves us with some Taylor expansion errors since we will try to approximate a continuos function on discrete grid points. We must analyze the modified euquation to see what errors we are making. For example, the central difference method generates

The numerical dispersion term means waves of different frequencies will travel at different speeds, and cause rippling near sharp corners (like in a square wave).

Lax-Friedrichs Method

One quick fix to the central difference method is to stop using the current cell value, and instead take the average of its left and right neighbors. This gives us the following update rule:

Using Von Neumann Stability Analysis, we can find that

It is stable iff

and so it is conditionally stable. We get the modified equation:

The average injects an artificial diffusion term, which causes the solution to blur over time.

Upwind Method

Recall the equation. The Upwind Method examines how , the transport coefficient, changes sign.

This is stable iff

It is conditionally stable. The modified equation is

The numerical diffusion is better than Lax-Friedrichs.

Courant-Friedrichs-Lewy (CFL) Condition

The CFL condition ensures Lax-Friedrichs Method and Upwind Method are conditionally stable. They only work if

To understand this physically,

  1. is the actual physical distance the fluid travels in one time step.
  2. is the physical width of one grid cell.

The CFL condition is a strict speed limit: the fluid cannot travel more than one grid cell per time step.

Semi-Lagrangian Scheme

Consider again the advection equation . Sometimes we want a larger time step. We can do this by tracing the flow backwards in time.

How it works:

  1. Stand at the current grid cell center .
  2. Trace a trajectory backwards in time by seconds.
  3. Arrive at the physical location .
  4. The starting location will almost certainly not land perfectly on a grid cell center, so interpolate the values of the surrounding grid cells to estimate the fluid data at that exact continuous coordinate.
  5. Copy that value into the current grid cell.

This method is unconditionally stable. The modified equation is the same as the Upwind Method.

nD Advection of a Function

The advection equation is now

We want to approximate

The Semi-Lagrangian scheme is somewhat the same. For scalar functions, we use

In 1D, interpolation is simple between two points. In 2D, we need interpolate the surronding 4 grid cells (pixels). In 3D, we need to interpolate the surrounding 8 grid cells (voxels).

Let us call one semi-Lagrangian step as . By combining multiple semi-Lagrangian steps, we can bootstrap a higher order method.

Back-and-Forth Error Correction and Compensation (BFECC)

How can we remove the blur caused by the term if the grid fundamentally requires interpolation? The idea is to trick the grid into calculating its own error and then compensate (subtract) for it.

Here, we are calculating the forward step via , and then finding the backward step by applying the opposite velocity to giving us . The remaining term is the error, which we take the semi-Lagrangian step of and subtract from . Indeed,

The tradeoff is that we remove numerical dissipation (blurring) but we get some numerical dispersion (spatial oscillation). We can detect and cutoff overshoots using a limiter.

Limiter

If the value of the field after BFECC is lying outside some convex hull of the neighborhood values of stable semi-Lagrangian, blend with or use the stbale semi-Lagrangian result.

"\\usepackage{tikz}\n\n\\begin{document}\n\\begin{tikzpicture}[>=stealth, scale=1.1]\n\n% =================================================\n% LEFT PLOT: Numerical Dissipation\n% =================================================\n\\begin{scope}[shift={(0,0)}]\n % Solid square wave\n \\draw[thick] (0,1) -- (2.0,1) -- (2.0,4) -- (3.3,4) -- (3.3,1) -- (5.5,1);\n\n % Dashed smoothed wave (Dissipation)\n % Uses bezier curves centered perfectly on the jumps (x=2.0 and x=3.3)\n \\draw[thick, dashed] (0,1) -- (1.6,1) \n .. controls (2.0,1) and (2.0,4) .. (2.4,4) \n -- (2.9,4) \n .. controls (3.3,4) and (3.3,1) .. (3.7,1) \n -- (5.5,1);\n\n % Label\n \\node[font=\\large] at (2.75, 0) {numerical dissipation};\n\\end{scope}\n\n% =================================================\n% RIGHT PLOT: Numerical Dispersion\n% =================================================\n\\begin{scope}[shift={(6.5,0)}]\n % Solid square wave (drawn thin to match reference)\n \\draw[thin] (0,1) -- (2.0,1) -- (2.0,4) -- (3.3,4) -- (3.3,1) -- (5.5,1);\n\n % Blue dispersed wave with dots (Ringing/Gibbs phenomenon)\n % Coordinates are hardcoded to safely bypass compiler loop issues while mimicking the oscillations\n \\draw[blue, thick, mark=*, mark size=1.5pt] plot coordinates {\n (0,1) (0.15,1) (0.3,1) (0.45,1) (0.6,1) (0.75,1) (0.9,1) (1.05,1) (1.2,1) (1.35,1)\n (1.45, 0.98) (1.55, 1.05) (1.65, 0.9) (1.75, 1.2) (1.85, 0.6) (1.95, 1.5)\n (2.05, 3.5) (2.15, 3.9) (2.25, 4.05) (2.35, 3.9) (2.45, 4.1) (2.55, 3.85) \n (2.65, 4.0) (2.75, 4.1) (2.85, 3.9) (2.95, 4.4) (3.05, 3.75) (3.15, 4.1) (3.25, 3.9)\n (3.35, 1.5) (3.45, 0.4) (3.55, 1.3) (3.65, 0.8) (3.75, 1.1) (3.85, 0.95) (3.95, 1.02)\n (4.05, 1) (4.2, 1) (4.35, 1) (4.5, 1) (4.65, 1) (4.8, 1) (4.95, 1) (5.1, 1) (5.25, 1) (5.4, 1) (5.5, 1)\n };\n\n % Label\n \\node[font=\\large] at (2.75, 0) {numerical dispersion};\n\\end{scope}\n\n\\end{tikzpicture}\n\\end{document}"numericaldissipationnumericaldispersion
source code

Footnotes

  1. Like in PacMan!

  2. This is a term from computer graphics. It refers to the phenomenon where high-frequency signals are misinterpreted as low-frequency signals when sampled at a discrete rate. 2 3