// Figures for the iterative-solver chapter: how the residual falls per // iteration, how the true error falls, and how both compare with a direct // solve of the same system. import Plot from '../../plot/Plot.tsx' import Legend from '../../plot/Legend.tsx' import { EndLabel, Line, Markers } from '../../plot/Curves.tsx' import { ink, marker, method, type MethodKey } from '../../plot/palette.ts' import type { Num } from '../../engine/types.ts' import { fmtErr } from '../onedim/panels.tsx' export interface IterOut { names: string[] N: number its: number[] R: Num[][] E: Num[][] T: Num[][] discErr: number directMs: number unknowns: number } // one palette slot per method, in the order the script lists them const SLOTS: MethodKey[] = ['fd2', 'fd4', 'fast', 'spectral', 'iterative'] function lowHigh(rows: Num[][], floor = 1e-14): [number, number] { let lo = Infinity let hi = -Infinity for (const r of rows) { for (const v of r) { if (v == null || !isFinite(v) || v <= 0) continue if (v < lo) lo = v if (v > hi) hi = v } } if (!isFinite(lo)) return [floor, 1] return [Math.max(lo / 3, floor), hi * 3] } function lastIndex(row: Num[]): number { for (let i = row.length - 1; i >= 0; i--) if (row[i] != null && isFinite(row[i] as number)) return i return 0 } export function IterPanels({ data }: { data: IterOut }) { const its = data.its const R = data.R const E = data.E const xd: [number, number] = [0, its[its.length - 1]] const rd = lowHigh(R) const ed = lowHigh([...E, [data.discErr]]) const td = lowHigh(data.T.map((r) => r.map((v) => (v != null && v > 0 ? v : null)))) td[0] = Math.max(td[0], 0.05) const legend = data.names.map((n, k) => ({ label: n, color: method[SLOTS[k]] })) return ( <>