d8098edAn interactive tutorial on the wave equation in acoustics: strings, drums and rooms, measured by earJeremy Magland 1import type { Chapter } from '../../ui/types.ts'
2import type { Params } from '../../engine/types.ts'
3import driver from '../membrane/driver.m?raw'
4import script from './drum.m?raw'
5import text from './text.md?raw'
6import {
7 OrdersPanels,
8 SCHEME,
9 STRIKE_CONTROL,
10 StrikePanels,
11 strikeParams,
12 strikeSummary,
13 type OrdersOut,
14 type StrikeOut,
15} from '../membrane/panels.tsx'
16import { fmtCents } from '../common/panels.tsx'
18/** each scheme at a Courant number just inside its own stability limit */
19const LAM: Record<string, number> = { fd2: 0.7, fd4: 0.6, fd44: 0.7 }
21export const ch05: Chapter = {
22 id: 'order',
23 nav: 'Fewer points per wavelength',
24 part: 'Drums',
25 title: 'Fewer points per wavelength',
26 blurb:
27 'Fourth-order stencils in space, in time, and in both, measured by pitch error against cost, and why raising the order in space alone buys little.',
28 text,
29 labs: [
30 {
31 id: 'orders',
32 title: 'Three schemes, three test modes',
33 caption:
34 'Each scheme runs at a Courant number just inside its own stability limit. This lab takes several seconds, so it waits for the run button.',
35 driver,
36 script,
37 controls: [],
38 defaults: {},
39 derive: () => ({
40 mode: 'orders',
41 Ns: [16, 24, 32, 48, 64, 96],
42 schemes: ['fd2', 'fd4', 'fd44'],
43 lams: [LAM.fd2, LAM.fd4, LAM.fd44],
44 modes: [4, 3, 9, 2, 14, 1],
45 }),
46 manual: true,
47 editorOpen: true,
48 render: (d: OrdersOut) => <OrdersPanels d={d} />,
49 summary: (d: OrdersOut) =>
50 d.schemes.map((s, k) => ({
51 label: `${s}, N = ${d.Ns[d.Ns.length - 1]}`,
52 value: fmtCents(d.worst[k][d.Ns.length - 1]),
53 })),
54 },
55 {
56 id: 'listen',
57 title: 'The drum again, by each scheme',
58 caption:
59 'Chapter 4’s drum on the same grid with each scheme. On a coarse grid the fourth-order scheme is much closer to the exact sound.',
60 driver,
61 script,
62 controls: [
63 {
64 kind: 'select',
65 key: 'scheme',
66 label: 'scheme',
67 options: Object.entries(SCHEME).map(([k, v]) => ({ value: k, label: v.label })),
68 },
69 { kind: 'slider', key: 'N', label: 'grid intervals N', min: 0, max: 0, values: [24, 32, 48] },
70 STRIKE_CONTROL,
71 ],
72 defaults: { scheme: 'fd44', N: 32, at: 'off' },
73 derive: (p: Params) => strikeParams({ ...p, lam: LAM[String(p.scheme)] }, String(p.scheme)),
74 render: (d: StrikeOut, p: Params) => <StrikePanels d={d} scheme={String(p.scheme)} />,
75 summary: strikeSummary,
76 },
77 ],
78}