// Static figures for Chapters 1 to 4.
import { Arrowheads, Figure, Label, Node, arrow } from './svg.tsx'
import { ink, method } from '../plot/palette.ts'
/** The mesh, the three-point stencil, and the band it puts in the matrix. */
export function ThreePointStencil() {
const L = 70
const R = 470
const Y = 96
const N = 8
const xs = Array.from({ length: N + 1 }, (_, i) => L + (i * (R - L)) / N)
const hit = [3, 4, 5]
return (
{/* the mesh */}
{xs.map((x, i) => (
))}
{/* h */}
{/* the stencil, sitting on one interior node */}
{hit.map((i, k) => (
))}
{/* the matrix band */}
{Array.from({ length: 7 }, (_, r) =>
Array.from({ length: 7 }, (_, c) => {
const d = Math.abs(r - c)
if (d > 1) return null
return (
)
}),
)}
)
}
/** Where manufactured data comes from, and what is then compared with what. */
export function ManufacturedLoop() {
const boxes = [
{ x: 6, w: 126, t: 'pick u(x)', s: 'any function you like' },
{ x: 148, w: 206, t: 'f := −u″, α := u(0), β := u(1)', s: 'differentiate by hand' },
{ x: 370, w: 146, t: 'solve for uₕ', s: 'the method under test' },
{ x: 532, w: 178, t: 'error = uₕ − u', s: 'known exactly, everywhere' },
]
const Y = 42
return (
{boxes.map((b, i) => (
{i < boxes.length - 1 ? (
) : null}
))}
)
}
/** The V: discretisation down, roundoff up, and the best attainable error. */
export function RoundoffFloor() {
const L = 66
const R = 546
const T = 24
const B = 168
// x is log10 N from 1 to 7; y is log10 error, 1 at the top and 1e-10 at the foot
const sx = (v: number) => L + ((v - 1) / 6) * (R - L)
const sy = (v: number) => T + (-v / 10) * (B - T)
const disc = (n: number) => -2.3 - 2 * (n - 1)
const round = (n: number) => -16 + 1.925 * n
const total = (n: number) => Math.log10(10 ** disc(n) + 10 ** round(n))
const path = (f: (n: number) => number) => {
const pts: string[] = []
for (let i = 0; i <= 240; i++) {
const n = 1 + (6 * i) / 240
const y = f(n)
if (y > 0 || y < -10) continue
pts.push(`${sx(n).toFixed(1)} ${sy(y).toFixed(1)}`)
}
return 'M ' + pts.join(' L ')
}
const nStar = 4 // where the two contributions are equal
return (
{[0, -2, -4, -6, -8, -10].map((e) => (
))}
{[1, 2, 3, 4, 5, 6, 7].map((n) => (
))}
)
}
/** Numerov changes the right-hand side and leaves the matrix alone. */
export function CompactScheme() {
const CX = [268, 336, 404]
const cell = (x: number, y: number, v: string | null, colour: string) =>
v === null ? (
) : (
<>
>
)
const row = (y: number, name: string, vals: (string | null)[], colour: string, note: string) => (
{vals.map((v, i) => (
{cell(CX[i], y, v, colour)}
))}
)
return (
{['i−1', 'i', 'i+1'].map((t, i) => (
))}
{row(52, 'both schemes', ['−1', '2', '−1'], method.fd2, '÷ h², tridiagonal')}
{row(142, 'three-point', [null, 'fᵢ', null], ink.secondary, 'order 2')}
{row(184, 'Numerov', ['f/12', '10f/12', 'f/12'], method.fd4, 'order 4, for one pass over an array')}
)
}
/** Equispaced against Chebyshev points, and where they come from. */
export function ChebyshevPoints() {
const N = 16
const L = 172
const R = 562
const cx = (L + R) / 2
const rad = (R - L) / 2
const YARC = 216
const cheb = Array.from({ length: N + 1 }, (_, j) => 0.5 * (1 - Math.cos((Math.PI * j) / N)))
const equi = Array.from({ length: N + 1 }, (_, j) => j / N)
const X = (t: number) => L + t * (R - L)
return (
{cheb.map((t, j) => {
const th = Math.PI - (Math.PI * j) / N
const ax = cx + rad * Math.cos(th)
const ay = YARC - rad * Math.sin(th)
return (
)
})}
{cheb.map((t, j) => (
))}
{equi.map((t, j) => (
))}
)
}