import { createElement, Fragment } from 'react' import type { Chapter } from '../../ui/types.ts' import driver from '../onedim/driver.m?raw' import script from './spectral.m?raw' import text from './text.md?raw' import { Convergence, Solution1D, WorkPrecision, fmtErr, type SingleOut, type SweepOut } from '../onedim/panels.tsx' import { KIND_CONTROL } from '../shared.ts' import type { MethodKey } from '../../plot/palette.ts' const NS = [8, 16, 32, 64, 128, 256, 1024, 4096, 16384] const SLOTS: MethodKey[] = ['fd2', 'fd4', 'spectral'] export const ch04: Chapter = { id: 'spectral', nav: 'Chebyshev collocation', part: 'One dimension', title: 'Chebyshev collocation', blurb: 'A dense matrix, geometric convergence, and the conditions under which that is worth it.', text, labs: [ { id: 'single', title: 'One spectral solve', caption: 'Note where the nodes are. The error is plotted at the nodes, so at N = 32 there are 33 of them and the answer is already near machine precision.', driver, script, controls: [ KIND_CONTROL, { kind: 'select', key: 'mid', label: 'method', options: [ { value: 3, label: 'Chebyshev collocation' }, { value: 2, label: 'compact 4th order' }, { value: 1, label: '3-point differences' }, ], }, { kind: 'slider', key: 'N', label: 'N', values: [4, 8, 12, 16, 24, 32, 48, 64, 96, 128], min: 4, max: 128 }, ], defaults: { kind: 'smooth', mode: 'single', mid: 3, N: 24, ngrid: 700 }, editorOpen: true, render: (d: SingleOut, p) => createElement(Solution1D, { data: d, slot: SLOTS[Number(p.mid) - 1] ?? 'spectral' }), summary: (d: SingleOut) => [ { label: 'unknowns', value: String(d.n - 1) }, { label: 'max error', value: fmtErr(d.maxerr) }, ], }, { id: 'sweep', title: 'All three, by resolution and by cost', caption: 'Chebyshev is capped at N = 256, where the dense solve starts to dominate the timing.', driver, script, controls: [KIND_CONTROL], defaults: { kind: 'smooth', mode: 'sweep', ns: NS, nmaxs: [16384, 4096, 256], timed: 1 }, render: (d: SweepOut) => createElement( Fragment, null, createElement(Convergence, { key: 'c', data: d, style: { slots: SLOTS, refs: [ { p: -2, label: 'slope −2' }, { p: -4, label: 'slope −4' }, ], }, height: 340, }), createElement(WorkPrecision, { key: 'w', data: d, style: { slots: SLOTS }, height: 340 }), ), summary: (d: SweepOut) => [ { label: 'best spectral', value: fmtErr(Math.min(...(d.E[2].filter((v) => v != null) as number[]))) }, { label: 'best 3-point', value: fmtErr(Math.min(...(d.E[0].filter((v) => v != null) as number[]))) }, ], }, ], }