The implicit scheme
Lesson 6.5.1 replaced in by the second difference at the old time . Take it at the new time instead:
Multiply by , write as before, and gather the new values on the left:
Rule: The implicit scheme
One equation for each inside point . Each new value depends on its new neighbours, which are unknown too. So the scheme is implicit: a step solves all the equations together, as a linear system (lesson 3.3.1).
Seen from the new time, the time difference is now a backward difference (lesson 2.2.2). Its error is about (lesson 2.8.1), the same size as the forward difference's, so the implicit scheme is still first order in time.
One step by hand
Take cells with both ends held at 0. Start with a spike, 1 at the middle point and 0 elsewhere, and take .
Call the three new inside values , and . Then the rule gives
Elimination (lesson 3.3.1) clears the matrix below its diagonal in two steps:
| step | operation | new row | new right side |
|---|---|---|---|
| 1 | 1 | ||
| 2 |
Back substitution gives , then , then .
| left | middle | right | |
|---|---|---|---|
| start | 0 | 1 | 0 |
| explicit step, | 1 | 1 | |
| implicit step, | 0.1429 | 0.4286 | 0.1429 |
At the explicit scheme is unstable, and its middle value has gone negative, which heat never does. The implicit step spreads the spike smoothly. Its values add up to , less than 1, because heat leaks out through the cold ends.
Boundary values
The first and last equations contain an end value. In the first, is known from the boundary condition, so it moves to the right side: .
The last equation gains the same way. With both ends at 0, as here, nothing is added.
Try it
A is the matrix of the three equations and b their right side. The cell does the two elimination steps of the table and prints the new rows, then solves the system with np.linalg.solve.
The last line is lesson 6.5.1's explicit step at , for comparison.
Tridiagonal systems
With more points the matrix keeps the same pattern: down the diagonal, just above and just below it, and 0 everywhere else. For four inside points:
A matrix whose only nonzero entries lie on the diagonal and the two lines beside it is tridiagonal. Each step of the implicit scheme solves , where holds the new inside values and the old ones plus the boundary terms.
Elimination is cheap here. Each pivot has just one nonzero entry below it, and clearing it adds nothing new to the rows below. So the work grows like the number of points, not like its cube, as it does for a full matrix.
It is also safe. Each diagonal entry, , is larger than the two others in its row put together, . So elimination never meets a small pivot and needs no row swaps: the pivots above were 3, and , all above 2.
The condition number of lesson 3.3.2 is small too: 2.78 for the matrix above, and never more than .
Rule: Solving a tridiagonal system
From scipy.linalg; the (1, 1) says one line above the diagonal and one below. ab stores them as three rows: the line above, shifted right by one; the diagonal; the line below, shifted left by one. The unused corners can hold anything.
For the example, ab is , where marks the unused corners.
A full matrix wastes room. With 10,000 points it holds numbers, 800 MB, almost all of them 0. The three lines hold 30,000 numbers.
Try it
The first lines store the example's three lines in ab, with 0 in the corners, and solve it with solve_banded.
Then the condition number of the example, and of the matrix for 99 inside points with and , beside .
Stable for every r
Lesson 6.5.1 measured stability with a zig-zag, whose second difference is times itself. Put it into the implicit rule: , so each step multiplies the zig-zag by .
That factor lies between 0 and 1 for every . The zig-zag shrinks without flipping sign, and so does every smoother wave. The implicit scheme is unconditionally stable: no limit on .
| 0.25 | 0.5 | 1 | 5 | 10 | |
|---|---|---|---|---|---|
| explicit, | 0 | ||||
| implicit, | 0.5 | 0.3333 | 0.2 | 0.0476 | 0.0244 |
Lesson 6.5.1's tent, on 20 cells, shows it. At , ten times the explicit limit, the largest value falls to 0.3909, 0.3339, 0.2284 and 0.1272 after 1, 2, 5 and 10 steps. No value ever goes negative.
Now accuracy limits the step.
Take lesson 6.5.1's rod with its ends at 0 and 1. In 20 implicit steps of () it reaches the line , to within at . The explicit scheme took 250 steps but got within : bigger steps, bigger errors.
Try it
implicit builds ab once, then each step adds the boundary terms to the old values and solves. The zig-zag factors come first.
Then the tent at , with its largest and smallest values, and the rod with its ends at 0 and 1.
Crank–Nicolson
The explicit scheme takes at the old time, the implicit one at the new time. The Crank–Nicolson scheme takes the average of the two. Write for the second difference at point :
Gather the new values on the left and the old ones on the right:
Rule: Crank–Nicolson
The left side is a tridiagonal system, as for the implicit scheme, with on the diagonal and beside it. The right side is an explicit step of half the size, worked out from the old values.
The values each scheme links in one step, with their weights. When more than one new value appears, the new values are tied together and must be solved for at once: a tridiagonal system.
Why the average helps: is a central difference about the half step . The average of the two second differences sits at that half step too. Lesson 2.8.1's central difference has an error in , not , and so does this one: Crank–Nicolson's time error shrinks like .
The zig-zag factor is , which lies between and 1 for every . So Crank–Nicolson is unconditionally stable too.
Back to the spike with . The right side is , and the system , , gives and .
The middle has fallen below its neighbours: a dip the true solution never has. The last section of this lesson comes back to it.
Try it
crank_nicolson adds half an explicit step to the old values, then solves the tridiagonal system with solve_banded. The two += lines add the boundary terms; here both ends are 0.
The cell takes one step from the spike with , then prints the zig-zag factor for five values of .
Three schemes compared
Take lesson 6.5.1's rod with the profile , whose exact solution is , and run to . Refine space and time together, keeping , so grows from 1 to 8: far too large for the explicit scheme. It runs instead at , with the many more steps that needs.
| cells | steps | implicit error | Crank–Nicolson error | explicit steps | explicit error |
|---|---|---|---|---|---|
| 10 | 10 | 25 | |||
| 20 | 20 | 100 | |||
| 40 | 40 | 400 | |||
| 80 | 80 | 1,600 |
Each halving of both steps halves the implicit error: its term is the larger one. It divides Crank–Nicolson's error by 4, since both its terms are squares. On 80 cells, Crank–Nicolson takes 80 steps to beat what the explicit scheme gets in 1,600.
Rule: Three schemes
How the error of each scheme shrinks, and when it is stable. The two implicit schemes cost one tridiagonal solve per step, which is cheap, so Crank–Nicolson is the usual choice.
Try it
theta_step does one step of either scheme: theta = 1 is implicit, theta = 0.5 is Crank–Nicolson (the weight on the new time). explicit is lesson 6.5.1's.
For each grid the cell prints the steps and the largest error of each scheme.
Oscillations near a kink
Crank–Nicolson has one weakness. Start from the kink on , with the ends held, and take large steps: and , so . After 5 steps, at , compare with the exact solution of lesson 6.4.2, .
Here and are the standard normal distribution function and density. The values look fine: for they are within of the exact solution for the implicit scheme, for Crank–Nicolson. The second differences tell another story:
| 0 | 0.1 | 0.2 | |||
|---|---|---|---|---|---|
| exact | 0.391 | 0.397 | 0.399 | 0.397 | 0.391 |
| implicit | 0.421 | 0.430 | 0.433 | 0.430 | 0.421 |
| Crank–Nicolson | 0.545 | 2.065 | 2.065 | 0.545 |
These are spurious oscillations: a zig-zag that the true solution does not have. The kink holds a little of every wave, the zig-zag included. Crank–Nicolson multiplies the zig-zag by per step, so it flips sign each step and hardly shrinks.
The values from the three solutions look alike, but their second differences do not. Crank–Nicolson's large steps leave a zig-zag at the kink that the implicit scheme removes at once.
The oscillation hides in the values and shows in the curvature. Case study 6.5.4 reads an option's gamma, a second derivative, off such a grid, so it matters there.
Two remedies are standard. Keep moderate near the kink: with () the factor is and the zig-zag dies quickly.
Or start with the implicit scheme, which removes the zig-zag at once. Replace the first step by two implicit steps of half the length, then carry on with Crank–Nicolson. This is Rannacher's start, after the mathematician who studied it.
With Rannacher's start the values are within , ten times closer than either scheme alone. The second differences are 0.383, 0.398, 0.424, 0.398 and 0.383, with a largest error of 0.025 near the kink.
Try it
theta_step is from the last cell, and exact is lesson 6.4.2's solution for the kink, whose at is . Each run prints its largest value error for , its second differences at the five middle points and their largest error.
The third run is Rannacher's start: two implicit half steps, then four Crank–Nicolson steps.
Common mistakes
- Forgetting the boundary terms. A known end value moves to the right side of the first or last equation. Leaving it out holds that end at 0.
- Building the full matrix.
np.linalg.solvegives the right answer, but its memory grows like the square of the points and its work like the cube. Usesolve_banded. - Misplacing the lines in
ab. The line above the diagonal is shifted right,ab[0, 1:]; the line below is shifted left,ab[2, :-1]. - Taking stable for accurate. The implicit scheme survives any , but its error grows with .
- Copying the explicit weights into Crank–Nicolson. Its right side has in the middle, not : each side carries half the second difference.
- Trusting Crank–Nicolson's curvature near a kink. With large the values look smooth while the second differences zig-zag. Take smaller steps there, or start with implicit steps.
Implicit schemes and Crank–Nicolson
0 of 12 correct
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
How to type answers
- Powers with
^:x^7,2^(1/3),a^-2. - Multiply with
*or by writing side by side:3x^2,2(x+1). ln(x)is the natural log,log(x, 2)is log base 2,eis Euler's number,sqrt(x)is a square root.sin(x),cos(x)andtan(x)take radians; writepifor π andsin(x)^2for sin²x.- Your first answer to each question is saved: it counts towards your score and brings the question back for review.
- Exact answers like
ln(20)/ln(3)are best. Decimals need about 4 significant figures.
Summary card
- The implicit scheme takes the second difference at the new time: . Each step solves a tridiagonal system, with known end values moved to the right side.
scipy.linalg.solve_banded((1, 1), ab, b)solves it with work proportional to the number of points;abholds the line above (shifted right), the diagonal and the line below (shifted left).- The implicit scheme multiplies a zig-zag by : stable for every , but first order in time.
- Crank–Nicolson averages the explicit and implicit second differences. It is stable for every , and its error shrinks like .
- Near a kink with large , Crank–Nicolson leaves a zig-zag (factor , close to ) that shows in the second differences. Take smaller steps there, or start with two implicit half steps (Rannacher's start).