/ concept-collection / acoustic-scattering-2d
Sign in
concept-collection / acoustic-scattering-2d
acoustic-scattering-2d / src / scene / registry.ts
195 lines · 6.8 KBBlameHistoryRaw
1/**
2 * The available scenes: their MATLAB source, and the parameters the host
3 * offers each one.
4 *
5 * Same split as the models (src/mgpu/registry.ts): what the medium *is* lives
6 * in the .m, and the sliders around it live here. A scene also gets `x`, `y`
7 * (metres), `L`, `h` (metres), `c0` (metres per second — the speed of sound
8 * in air), `npts`, `nx` and `ny` for free — see src/scene/scene.ts.
9 *
10 * Every scene here takes its speed contrasts (`cin`, `cwall`, `dn`, `amp`) as
11 * plain ratios to `c0` rather than as absolute speeds, so a slider means the
12 * same thing whatever the background is. Its lengths (`R`, `side`, `gap`,
13 * `w`, ...) are metres, and its rates (`absorb`) are inverse seconds.
14 */
15import type { ParamSpec, Params } from '../mgpu/registry.ts';
16import diskSource from '../../scenes/disk.m?raw';
17import roomSource from '../../scenes/room.m?raw';
18import slitSource from '../../scenes/slit.m?raw';
19import lensSource from '../../scenes/lens.m?raw';
20import speckleSource from '../../scenes/speckle.m?raw';
22export interface MScene {
23 key: string;
24 label: string;
25 blurb: string;
26 params: ParamSpec[];
27 /**
28 * Model parameters this scene wants set when it is chosen — a source
29 * position, usually. A scene cannot reach into the model's parameters, and
30 * should not: they belong to different files. But a room is no use with the
31 * source outside it, so a scene may say what it would like, and the app
32 * applies it as if the sliders had been moved by hand.
33 */
34 suggest?: Params;
35 /** Where this scene would like the microphone. Inside the room, for a room.
36 * Applied like `suggest`, and movable afterwards. */
37 mic?: { x: number; y: number };
38 source: string;
41const disk: MScene = {
42 key: 'disk',
43 label: 'Disk',
44 blurb: 'One circular scatterer — the case with an exact series solution.',
45 params: [
46 {
47 key: 'cin',
48 label: 'speed inside (×c0)',
49 value: 0.5,
50 min: 0.2,
51 max: 3,
52 step: 0.05,
53 hint: 'Ratio to the background speed. Well above 1 is nearly rigid, well below nearly pressure-release, and exactly 1 is no scatterer at all.',
54 },
55 { key: 'R', label: 'radius (m)', value: 0.75, min: 0.1, max: 2.5, step: 0.05 },
56 {
57 key: 'absorb',
58 label: 'absorption (1/s)',
59 value: 0,
60 min: 0,
61 max: 1500,
62 step: 50,
63 hint: 'Damping inside the disk. Turn it up and the scatterer swallows what enters it instead of ringing.',
64 },
65 ],
66 source: diskSource,
67};
69const room: MScene = {
70 key: 'room',
71 label: 'Room',
72 blurb: 'An enclosure with a doorway: echoes, modes, and something to listen to.',
73 params: [
74 {
75 key: 'cwall',
76 label: 'wall speed (×c0)',
77 value: 0.15,
78 min: 0.05,
79 max: 6,
80 step: 0.05,
81 hint: 'Ratio to the background speed. Below 1 the wall is slower than the room and reflects without costing timestep; above 1 it is rigid, and the timestep drops to match. Either way what reflects is |cwall-1|/(cwall+1).',
82 },
83 {
84 key: 'side',
85 label: 'room half-width (m)',
86 value: 1.5,
87 min: 0.5,
88 max: 2.5,
89 step: 0.05,
90 hint: 'The room is 2×side across — a real room, not a fraction of the domain.',
91 },
92 {
93 key: 'gap',
94 label: 'doorway (m)',
95 value: 0.9,
96 min: 0,
97 max: 2,
98 step: 0.05,
99 hint: 'Width of the opening in the right-hand wall — 0.9 m is a typical interior door. At 0 the room is sealed and the sound never leaves.',
100 },
101 { key: 'thick', label: 'wall thickness (m)', value: 0.1, min: 0.03, max: 0.3, step: 0.01 },
102 {
103 key: 'absorb',
104 label: 'wall absorption (1/s)',
105 value: 70,
106 min: 0,
107 max: 1500,
108 step: 10,
109 hint: 'Damping inside the wall. It swallows whatever gets in rather than letting it rattle around in there, and it is what sets how long the room rings. At 0 a sealed room rings almost forever.',
110 },
111 ],
112 // A click from a point inside the room, off-centre so it does not excite
113 // only the symmetric modes, heard from the far corner. 700 Hz on a 1.5 m
114 // half-width room is a wavelength of about half a metre — small enough to
115 // show interference between the direct sound and the walls, and (at the
116 // app's default 512-point grid over a 10 m domain) resolved to about 25
117 // cells per wavelength.
118 suggest: { point: 1, x0: -0.7, y0: 0.5, w: 0.03, f: 700, tw: 0.003, t0: 0.015, cw: 0 },
119 mic: { x: 0.9, y: -0.7 },
120 source: roomSource,
121};
123const slit: MScene = {
124 key: 'slit',
125 label: 'Two slits',
126 blurb: 'A hard screen with two apertures: diffraction and interference.',
127 params: [
128 {
129 key: 'cwall',
130 label: 'wall speed (×c0)',
131 value: 4,
132 min: 1.5,
133 max: 8,
134 step: 0.1,
135 hint: 'Ratio to the background speed.',
136 },
137 { key: 'gap', label: 'slit width (m)', value: 0.4, min: 0.05, max: 2, step: 0.02 },
138 { key: 'sep', label: 'slit separation (m)', value: 2, min: 0.5, max: 4, step: 0.05 },
139 { key: 'thick', label: 'screen thickness (m)', value: 0.3, min: 0.05, max: 1, step: 0.02 },
140 ],
141 suggest: { point: 0, f: 400, tw: 0.008, t0: 0.04, cw: 0, x0: -3 },
142 source: slitSource,
143};
145const lens: MScene = {
146 key: 'lens',
147 label: 'Lens',
148 blurb: 'A smooth slow patch that refracts a plane wave to a focus.',
149 params: [
150 {
151 key: 'dn',
152 label: 'speed dip (fraction of c0)',
153 value: 0.35,
154 min: 0,
155 max: 0.8,
156 step: 0.01,
157 hint: 'Fraction of the background speed the centre of the lens is slower by. Deeper means a shorter focal length.',
158 },
159 { key: 'w', label: 'lens width (m)', value: 1.5, min: 0.5, max: 3, step: 0.05 },
160 { key: 'x0', label: 'lens x (m)', value: -1, min: -3, max: 3, step: 0.1 },
161 ],
162 suggest: { point: 0, f: 400, tw: 0.008, t0: 0.04, cw: 0, x0: -3 },
163 source: lensSource,
164};
166const speckle: MScene = {
167 key: 'speckle',
168 label: 'Random medium',
169 blurb: 'Weak random structure everywhere: multiple scattering, and a coda.',
170 params: [
171 { key: 'amp', label: 'contrast (fraction of c0)', value: 0.15, min: 0, max: 0.4, step: 0.01 },
172 {
173 key: 'kc',
174 label: 'structure scale (rad/m)',
175 value: 6,
176 min: 1,
177 max: 20,
178 step: 0.5,
179 hint: 'Centre wavenumber (2π / wavelength) of the random field. Scattering is strongest when this is comparable to the wave’s own.',
180 },
181 { key: 'seed', label: 'seed', value: 1, min: 1, max: 9999, step: 1, reseed: true },
182 ],
183 suggest: { point: 0, f: 400, tw: 0.008, t0: 0.04, cw: 0, x0: -3 },
184 source: speckleSource,
185};
187// The room first: it is the one that shows the most, and the one the
188// microphone is for.
189export const mScenes: MScene[] = [room, disk, slit, lens, speckle];
191export const mSceneByKey = (key: string): MScene | undefined =>
192 mScenes.find((s) => s.key === key);
194export const defaultSceneParams = (s: MScene): Params =>
195 Object.fromEntries(s.params.map((p) => [p.key, p.value]));
moveopenescclose