/ proof-of-concept / plate-dynamics-lab-2
Sign in
proof-of-concept / plate-dynamics-lab-2
Plate dynamics lab 2: a hammer on a hung plate by Rayleigh-Ritz and by Mindlin-Reissner finite elements
Two independent modal simulators of the reference problem in sim-docs, run as MATLAB source in numbl in the browser: Kirchhoff-Love energy on a tensor-Legendre basis with a prescribed Hertz pulse, and full-integration Q4 Mindlin-Reissner elements with a subspace-iteration eigensolver and the hammer integrated against the moving plate. Modes are kept between strikes. Tests check both against the Python simulators' diagnostics.
Jeremy Magland <jeremy.magland@gmail.com> committed commit 2004981dacf2 Browse files
127 changed files+16433−0
.gitignoreadded+4−0View file
@@ -0,0 +1,4 @@
1+node_modules/
2+dist/
3+*.tsbuildinfo
4+.DS_Store
.mochi/workflows/deploy.ymladded+69−0View file
@@ -0,0 +1,69 @@
1+name: Deploy the lab
2+
3+# configure-pages runs before the build rather than after it, because the build
4+# needs the base path it reports: a mochi site is served at the root of its own
5+# origin when the repository has a site label, and under /<collection>/<repo>/site/
6+# when it does not, and never at /<repo>/.
7+
8+on:
9+ push:
10+ branches: [main]
11+ workflow_dispatch:
12+
13+permissions:
14+ contents: read
15+ pages: write
16+ id-token: write
17+
18+concurrency:
19+ group: pages
20+ cancel-in-progress: false
21+
22+jobs:
23+ build:
24+ runs-on: ubuntu-latest
25+ steps:
26+ - name: Checkout
27+ uses: actions/checkout@v4
28+
29+ - name: Setup Node
30+ uses: actions/setup-node@v4
31+ with:
32+ node-version: 22
33+
34+ - name: Setup Pages
35+ id: pages
36+ uses: actions/configure-pages@v5
37+
38+ - name: Install
39+ run: npm ci
40+
41+ - name: Typecheck
42+ run: npm run typecheck
43+
44+ # Runs the .m library under numbl in Node against the reference diagnostics
45+ # of the two Python simulators: mode frequencies, contact numbers and the
46+ # first 50 ms of sound. A regression here should not deploy.
47+ - name: Test
48+ run: npm test
49+
50+ - name: Build the lab
51+ env:
52+ DEPLOY_BASE: ${{ steps.pages.outputs.base_path }}
53+ run: npm run build -w apps/lab
54+
55+ - name: Upload artifact
56+ uses: actions/upload-pages-artifact@v3
57+ with:
58+ path: apps/lab/dist
59+
60+ deploy:
61+ needs: build
62+ runs-on: ubuntu-latest
63+ environment:
64+ name: github-pages
65+ url: ${{ steps.deployment.outputs.page_url }}
66+ steps:
67+ - name: Deploy to Pages
68+ id: deployment
69+ uses: actions/deploy-pages@v4
.prettierrcadded+1−0View file
@@ -0,0 +1 @@
1+{ "printWidth": 100, "singleQuote": false, "trailingComma": "all" }
README.mdadded+75−0View file
@@ -0,0 +1,75 @@
1+# Plate Dynamics Lab 2
2+
3+An interactive lab for the sound of a small metal plate hung from a thread and struck by a hammer: a 30 mm square of 1 mm 6061-T6 aluminium, a thread through a hole near its top edge, a 10 g steel hammer at 0.45 m/s. The lab solves the problem two independent ways and lets you compare them: a Rayleigh-Ritz solution of the Kirchhoff-Love thin-plate energy on a tensor-Legendre basis with the strike applied as a prescribed Hertz pulse, and a Mindlin-Reissner finite-element mesh whose modes are struck by a hammer integrated against the moving plate. Both are modal: the modes of the plate on its thread are solved once and kept, and every strike is a sum of damped oscillators, so trying strike points, hammers and microphones is near instant. The app shows the plate on its thread, its modes and their shapes, the contact force and the hammer's motion, the displacement and surface-stress fields of the strike transient, a probe time series and spectrum, and the radiated sound, which you can play or save; the comparison tab lines up the two methods' modes, contact numbers, spectra and sounds.
4+
5+The numerics run in [numbl](https://github.com/flatironinstitute/numbl), a MATLAB-syntax environment executing in the browser, in double precision. The MATLAB source shown on screen is the source that ran: edit any file and rerun. No physics is computed in JavaScript; the host draws arrays and plays samples.
6+
7+This lab succeeds [plate-dynamics-lab](../plate-dynamics-lab), whose explicit finite-difference time stepping under gravity turned out to be a less realistic model of a struck plate than the two modal simulators in [sim-docs](../sim-docs), which this lab follows constant for constant. The old lab's WebGPU time loop has no counterpart here: nothing in a modal method is expensive enough to need it, except a very large finite-element eigensolve, and that is done in double precision in numbl instead (see below).
8+
9+## Layout
10+
11+```
12+packages/core problem spec, .m generation, numbl runner, result typing, field primitives (no UI)
13+packages/core/src/m the MATLAB library: plate_rr_modes.m, plate_fem_modes.m, plate_synthesize.m, ...
14+packages/components React components: code view, parameter panel, 2-D field view, 3-D scene, plots, equations, run status, audio, movie
15+apps/lab the app: one arrangement of the components
16+```
17+
18+## The object
19+
20+A rectangular plate of width W, height H and thickness h with a hole of diameter 0.8 mm on its centreline 1.5 mm below the top edge, hung from a thread of length L. The thread holds the hole with the linearized pendulum stiffness m g / L against transverse motion, a soft point spring and not a clamp, and a dashpot at the hole stands for the loss where the thread rubs it. Coordinates are metres from the plate's centre, x to the right and y towards the top edge, as in the simulators this lab follows. A steel hammer with a rounded tip strikes the plate at a chosen point; a microphone sits on the plate's normal.
21+
22+## The two methods
23+
24+**Rayleigh-Ritz, Kirchhoff-Love** (`plate_rr_modes.m`, `plate_rr_strike.m`). The bending energy of a thin plate, with no shear deformation or rotary inertia, is minimized over products of Legendre polynomials up to order 12 per side, 169 coefficients, integrated by 30-point Gauss-Legendre quadrature per side. The hole subtracts its mass at its position and the thread adds its spring there; the generalized eigenproblem is solved densely after a Cholesky reduction. The rigid rotations and the pendulum mode are dropped. The strike is the force history of a Hertz contact on a rigid target, a sin^(3/2) pulse of the duration and impulse the Hertz formulas give, applied to every mode through Duhamel's integral by quadrature. The plate does not push back on the hammer.
25+
26+**Finite elements, Mindlin-Reissner** (`plate_fem_modes.m`, `plate_fem_strike.m`). Bilinear four-node elements with three unknowns per node (the deflection and two rotations) carry the bending and transverse-shear energies, both integrated with 2 x 2 Gauss points, and the mass with rotary inertia. This is the element of the reference simulator, kept as it is: full integration of the shear term locks on a thin plate, so the partials converge from above as the mesh is refined, by several percent between 32 x 32 and 80 x 80. The default mesh is 48 x 48, about 3 percent high on the main partial and a few seconds to solve; 80 x 80, where the reference was run, is one setting away and takes tens of seconds. The three rigid-body modes (two free rotations about the hole and the pendulum) are kept, so that the collision can put momentum into the freely hanging plate, and left out of the sound. The hammer and the plate are integrated together as one system, the hammer's Hertz spring with a Hunt-Crossley loss against the sum of all the modes under it, by fourth-order Runge-Kutta until they separate; the very light plate moves away under the hammer, so the contact is longer, the force far lower and the impulse smaller than the rigid-target pulse.
27+
28+The two share the plate, the hammer, the damping constants and the radiation model, and nothing else. Each mode is damped by three losses: the material's structural Q, an air-loading term that grows with frequency, and the thread's dashpot acting on the mode's displacement at the hole. The pressure at the microphone is a compact-source estimate, each mode's volume velocity reduced below the plate's coincidence frequency by the front/back cancellation of a small unbaffled plate; it is meaningful in timbre and decay, approximate in level, so the export is peak-normalized. All of this is the reference simulators' modelling, constant for constant, and the tests check it against their diagnostics (see below).
29+
30+## Two stages, and what is kept
31+
32+The modes depend on the plate, its elastic constants, the hole, the thread's length, the sample rate (modes are kept below 0.47 fs) and the method's numerics. The strike depends on everything else: the hammer, the strike point, the probe, the microphone, the material's Q, the dashpot and the render settings. The generated script `main.m` calls the two stages in turn, and the lab runs them as two scripts: the modes struct comes back to the host and is kept, and a later run whose modes key is unchanged goes straight to the strike, which takes a couple of seconds for four seconds of sound at 96 kHz. Editing `main.m` runs it as written, in one go.
33+
34+## The eigensolver
35+
36+numbl's own `eigs` is far too slow on the finite-element problem (twenty thousand unknowns at 80 x 80), but its sparse Cholesky after an approximate-minimum-degree ordering is fast, so `plate_fem_eigs.m` is a subspace iteration written on top of it: one factorization of K - sigma M with a negative shift, which keeps the matrix positive definite, then repeated block solves and Rayleigh-Ritz projections until the lowest eigenvalues settle. It is a block method, which matters for a square plate, whose spectrum is full of degenerate pairs. The block is sized from the modal density of a thin plate and enlarged if the wanted set does not reach past the cutoff; the count of iterations and the largest residual are reported under Run details. Everything stays in double precision.
37+
38+## Verification
39+
40+`packages/core/test/fixtures` holds the diagnostics the two Python simulators in `sim-docs` write, at the reference centre hit and an off-centre hit, for the Rayleigh-Ritz method at orders 12 and 8 and for the finite-element method at meshes of 16, 24, 32 and 48, together with the first 50 ms of each reference WAV. The tests run the library in numbl under Node against them: the mode frequencies agree to a part in ten million, the contact time, peak force, impulse and exit velocities to a part in a hundred thousand (the contact integrators differ), and the peak-normalized sound to the quantization of the WAV for Rayleigh-Ritz and to a few parts in ten thousand for the finite elements, where the two eigensolvers mix the degenerate pairs differently. The finite-element method is also checked to converge from above with mesh refinement, and the two-stage run to equal the single script bit for bit.
41+
42+## Saving a run
43+
44+- **The sound**, as a 24-bit mono WAV at the simulation's own sample rate, peak-normalized to the render's dBFS setting, as the reference simulators write theirs.
45+- **The movie**, as WebM (VP9 and Opus) or MP4 where the browser records it: the 3-D scene as it stands, with a title bar carrying the simulated time and a caption bar carrying the field and its colour scale. With the sound it is recorded in real time, so the frames, which cover the first two milliseconds of the strike, flash by at the start; the slow-motion option plays the frames one after another at thirty a second without the sound, which could not be slowed a thousandfold. The recorder is the browser's own `MediaRecorder`, so the tab has to stay in front, and a machine that cannot keep up drops frames rather than slowing down.
46+
47+## The live app
48+
49+The lab is deployed at <https://plate-dynamics-lab-2.vault1-sites.magland.org/>. `.mochi/workflows/deploy.yml` typechecks, runs the tests and builds `apps/lab` on every push to `main`, then publishes it; a regression against the reference diagnostics stops the deployment. The site sits at the root of an origin of its own, which the repository's site label gives it, so the build's base path comes from `configure-pages` rather than being written into the Vite config, and the page can use `localStorage` for your settings and source edits.
50+
51+## Running
52+
53+```bash
54+npm install
55+npm run dev # the lab, http://localhost:5173
56+npm test # vitest: the .m files in numbl under Node against the reference diagnostics
57+npm run typecheck
58+npm run build
59+```
60+
61+The `.m` files also run unchanged from the numbl CLI, Octave or MATLAB:
62+
63+```bash
64+npx numbl run --path packages/core/src/m my_script.m
65+```
66+
67+## Honest limits
68+
69+- Both models are linear: no denting of the plate, no chatter of the hammer, no thread buzz, no room, no microphone colouring. A hard strike on aluminium would dent it, and the model does not know.
70+- Kirchhoff-Love theory has no shear deformation or rotary inertia, so the Rayleigh-Ritz partials in the tens of kHz run a little high; the Mindlin-Reissner element carries the shear but locks on a thin plate, so its partials converge from above with the mesh. The two are a percent or two apart on this plate, and neither is the truth.
71+- Neither contact model has been checked against a measured hammer; the prescribed pulse ignores the plate's motion, and the coupled model's loss is a rule of thumb tuned to the nominal restitution.
72+- The damping constants are the reference simulators' order-of-magnitude figures, chosen, not measured. The thread is a point spring; the hole is a point mass correction (Rayleigh-Ritz) or not cut at all (finite elements).
73+- The radiated sound is a compact-source estimate with a smooth cancellation below coincidence, not a solution of the diffraction problem: right in timbre and decay, approximate in level.
74+- The finite-element method's rigid-body modes have no gravitational restoring force, so after the strike the plate drifts in rotation at a constant rate rather than swinging; they are shown in the total displacement and kept out of the sound.
75+- Stresses are second derivatives of the fields (for the finite elements, first-order gradients of the rotations) and converge more slowly than the frequencies.
apps/lab/index.htmladded+12−0View file
@@ -0,0 +1,12 @@
1+<!doctype html>
2+<html lang="en">
3+ <head>
4+ <meta charset="UTF-8" />
5+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6+ <title>Plate Dynamics Lab 2</title>
7+ </head>
8+ <body>
9+ <div id="root"></div>
10+ <script type="module" src="/src/main.tsx"></script>
11+ </body>
12+</html>
apps/lab/package.jsonadded+18−0View file
@@ -0,0 +1,18 @@
1+{
2+ "name": "@plate-dyn/lab",
3+ "version": "0.1.0",
4+ "private": true,
5+ "type": "module",
6+ "scripts": {
7+ "dev": "vite",
8+ "build": "vite build",
9+ "preview": "vite preview",
10+ "typecheck": "tsc -p tsconfig.json --noEmit"
11+ },
12+ "dependencies": {
13+ "@plate-dyn/components": "*",
14+ "@plate-dyn/core": "*",
15+ "react": "^19.2.0",
16+ "react-dom": "^19.2.0"
17+ }
18+}
apps/lab/src/App.tsxadded+692−0View file
@@ -0,0 +1,692 @@
1+import { useCallback, useEffect, useMemo, useRef, useState } from "react";
2+import {
3+ CostModel,
4+ LIBRARY_FILES,
5+ METHODS,
6+ SCENARIO_PRESETS,
7+ buildModesBundle,
8+ buildPlanBundle,
9+ buildRunBundle,
10+ buildStrikeBundle,
11+ checkModesValue,
12+ cloneSpec,
13+ defaultSpec,
14+ methodById,
15+ modesKey,
16+ parseMethodPlan,
17+ parsePlateResult,
18+ validateSpec,
19+ withEdits,
20+ type BackendInfo,
21+ type MethodId,
22+ type MethodPlan,
23+ type PlainValue,
24+ type PlateResult,
25+ type ProblemSpec,
26+} from "@plate-dyn/core";
27+import {
28+ CodeView,
29+ EquationsView,
30+ ParameterPanel,
31+ RunStatus,
32+ useRunner,
33+ useSolve,
34+ type ParameterSection,
35+ type StagedStatus,
36+} from "@plate-dyn/components";
37+import { ResultsView } from "./views/ResultsView";
38+import { ComparisonView } from "./views/ComparisonView";
39+import { clearPersistentState, usePersistentState } from "./usePersistentState";
40+
41+type RightTab = "equations" | "source" | "run";
42+type MiddleTab = "result" | "compare";
43+
44+const RIGHT_TABS: { id: RightTab; label: string }[] = [
45+ { id: "equations", label: "Equations" },
46+ { id: "source", label: "Source" },
47+ { id: "run", label: "Run details" },
48+];
49+
50+/** What is on the bench: the settings a first run needs. */
51+const PROBLEM_SECTIONS: ParameterSection[] = [
52+ "plate",
53+ "material",
54+ "suspension",
55+ "hammer",
56+ "strike",
57+];
58+/** Reasonable defaults, rarely the point. */
59+const ADVANCED_SECTIONS: ParameterSection[] = ["probe", "mic", "render"];
60+const ADVANCED_PREFIXES = ["probe", "mic", "render"];
61+
62+const SUMMARIES: Record<string, string> = Object.fromEntries(
63+ LIBRARY_FILES.map((f) => [f.name, f.summary]),
64+);
65+SUMMARIES["main.m"] = "Generated from the controls: the params struct, the modes and the strike";
66+
67+/** A kept result and what it came from. */
68+export interface KeptResult {
69+ result: PlateResult;
70+ spec: ProblemSpec;
71+ stamp: string;
72+}
73+
74+const isSpec = (v: unknown): v is ProblemSpec => {
75+ const s = v as ProblemSpec | null;
76+ return (
77+ !!s &&
78+ typeof s === "object" &&
79+ !!s.plate &&
80+ !!s.material &&
81+ !!s.hole &&
82+ !!s.thread &&
83+ !!s.hammer &&
84+ !!s.strike &&
85+ !!s.probe &&
86+ !!s.mic &&
87+ !!s.render &&
88+ !!s.rr &&
89+ !!s.fem &&
90+ typeof s.render.frame_span === "number"
91+ );
92+};
93+const isRightTab = (v: unknown): v is RightTab => RIGHT_TABS.some((t) => t.id === v);
94+const isMiddleTab = (v: unknown): v is MiddleTab => v === "result" || v === "compare";
95+const isMethodId = (v: unknown): v is MethodId => v === "rr" || v === "fem";
96+const isBool = (v: unknown): v is boolean => typeof v === "boolean";
97+const isEdits = (v: unknown): v is Record<string, string> =>
98+ !!v && typeof v === "object" && Object.values(v as object).every((x) => typeof x === "string");
99+
100+/** Edits to the library files (not main.m), which change the modes and so their cache key. */
101+function libraryEdits(edits: Record<string, string>): Record<string, string> {
102+ return Object.fromEntries(Object.entries(edits).filter(([k]) => k !== "main.m"));
103+}
104+
105+export function App() {
106+ // Everything the user chooses is remembered in this browser; "Reset to defaults" forgets it all.
107+ const [spec, setSpec] = usePersistentState<ProblemSpec>("spec-v1", defaultSpec, isSpec);
108+ const [methodId, setMethodId] = usePersistentState<MethodId>("method", () => "rr", isMethodId);
109+ const [edits, setEdits] = usePersistentState<Record<string, string>>(
110+ "edits",
111+ () => ({}),
112+ isEdits,
113+ );
114+ const [rightTab, setRightTab] = usePersistentState<RightTab>(
115+ "rightTab",
116+ () => "equations",
117+ isRightTab,
118+ );
119+ const [middleTab, setMiddleTab] = usePersistentState<MiddleTab>(
120+ "middleTab",
121+ () => "result",
122+ isMiddleTab,
123+ );
124+ const [advancedOpen, setAdvancedOpen] = usePersistentState<boolean>(
125+ "advancedOpen",
126+ () => false,
127+ isBool,
128+ );
129+ const method = methodById(methodId);
130+ const runner = useRunner();
131+ const [backend, setBackend] = useState<BackendInfo | null>(null);
132+ useEffect(() => {
133+ void runner.backend.then(setBackend);
134+ }, [runner]);
135+
136+ const issues = useMemo(() => validateSpec(spec), [spec]);
137+ const hasErrors = issues.some((i) => i.severity === "error");
138+ const mainEdited = "main.m" in edits;
139+
140+ const scenarioId = useMemo(
141+ () => SCENARIO_PRESETS.find((s) => JSON.stringify(s.spec) === JSON.stringify(spec))?.id ?? "",
142+ [spec],
143+ );
144+
145+ // The source on screen is what runs: main.m as one script, or its two stages.
146+ const displayBundle = useMemo(() => buildRunBundle(spec, method), [spec, method]);
147+
148+ const plan = useSolve(parseMethodPlan);
149+ const cost = useRef(new CostModel());
150+ useEffect(() => {
151+ if (hasErrors) return;
152+ const t = window.setTimeout(() => {
153+ void plan.run({ bundle: withEdits(buildPlanBundle(spec, method), edits) });
154+ }, 300);
155+ return () => window.clearTimeout(t);
156+ // eslint-disable-next-line react-hooks/exhaustive-deps
157+ }, [spec, edits, method, hasErrors]);
158+ const methodPlan: MethodPlan | null =
159+ plan.last && plan.last.method === method.id ? plan.last : null;
160+ const planError = plan.state.status === "error" ? plan.state.run.error : null;
161+
162+ // ---- the staged run: modes (kept), then the strike
163+ const [status, setStatus] = useState<StagedStatus>({ status: "idle" });
164+ const [results, setResults] = useState<Partial<Record<MethodId, KeptResult>>>({});
165+ const modesCache = useRef(new Map<string, PlainValue>());
166+ const runSeq = useRef(0);
167+ const running = status.status === "running";
168+
169+ /** Identifies the inputs a result came from, so a result from other settings can say so. */
170+ const stamp = useMemo(
171+ () => JSON.stringify({ spec, edits, method: method.id }),
172+ [spec, edits, method.id],
173+ );
174+ const currentModesKey = modesKey(spec, method.id) + "|" + JSON.stringify(libraryEdits(edits));
175+ const modesKept = !mainEdited && modesCache.current.has(currentModesKey);
176+
177+ const runMethod = useCallback(
178+ async (m: typeof method, s: ProblemSpec, ed: Record<string, string>, st: string) => {
179+ const seq = ++runSeq.current;
180+ const output: string[] = [];
181+ const startedAt = performance.now();
182+ const edited = "main.m" in ed;
183+ const say = (text: string) => {
184+ output.push(text);
185+ if (seq === runSeq.current)
186+ setStatus((prev) =>
187+ prev.status === "running" ? { ...prev, output: [...output] } : prev,
188+ );
189+ };
190+ const fail = (error: string, backend: BackendInfo, cancelled?: boolean) =>
191+ setStatus({
192+ status: "error",
193+ error,
194+ output,
195+ wallMs: performance.now() - startedAt,
196+ backend,
197+ cancelled,
198+ });
199+ let value: PlainValue;
200+ let modesMs: number | null = null;
201+ let strikeMs = 0;
202+ let backendInfo: BackendInfo;
203+ if (edited) {
204+ setStatus({ status: "running", stage: "script", startedAt, output: [] });
205+ const r = await runner.run({ bundle: withEdits(buildRunBundle(s, m), ed) }, say);
206+ if (seq !== runSeq.current) return;
207+ if (!r.ok) return fail(r.error, r.backend, r.cancelled);
208+ value = r.value;
209+ modesMs = r.wallMs;
210+ backendInfo = r.backend;
211+ } else {
212+ const key = modesKey(s, m.id) + "|" + JSON.stringify(libraryEdits(ed));
213+ let modes = modesCache.current.get(key);
214+ if (!modes) {
215+ setStatus({ status: "running", stage: "modes", startedAt, output: [] });
216+ const r = await runner.run({ bundle: withEdits(buildModesBundle(s, m), ed) }, say);
217+ if (seq !== runSeq.current) return;
218+ if (!r.ok) return fail(r.error, r.backend, r.cancelled);
219+ try {
220+ checkModesValue(r.value);
221+ } catch (e) {
222+ return fail(
223+ `the modes stage returned an unexpected shape: ${(e as Error).message}`,
224+ r.backend,
225+ );
226+ }
227+ modes = r.value;
228+ // keep a handful of the most recent, so switching between a few plates stays instant
229+ if (modesCache.current.size >= 6)
230+ modesCache.current.delete(modesCache.current.keys().next().value as string);
231+ modesCache.current.set(key, modes);
232+ modesMs = r.wallMs;
233+ if (methodPlan) cost.current.observe(m, "modes", methodPlan.work, r.wallMs);
234+ }
235+ setStatus({ status: "running", stage: "strike", startedAt, output: [...output] });
236+ const r2 = await runner.run(
237+ { bundle: withEdits(buildStrikeBundle(s, m), ed), inputs: { modes } },
238+ say,
239+ );
240+ if (seq !== runSeq.current) return;
241+ if (!r2.ok) return fail(r2.error, r2.backend, r2.cancelled);
242+ value = r2.value;
243+ strikeMs = r2.wallMs;
244+ backendInfo = r2.backend;
245+ }
246+ let result: PlateResult;
247+ try {
248+ result = parsePlateResult(value);
249+ } catch (e) {
250+ return fail(
251+ `the script ran, but its result has an unexpected shape: ${(e as Error).message}`,
252+ backendInfo,
253+ );
254+ }
255+ cost.current.observe(
256+ m,
257+ "strike",
258+ result.info.nsamples * result.info.nmodes,
259+ strikeMs || modesMs || 0,
260+ );
261+ setResults((prev) => ({ ...prev, [m.id]: { result, spec: s, stamp: st } }));
262+ setStatus({
263+ status: "done",
264+ output,
265+ wallMs: performance.now() - startedAt,
266+ modesMs,
267+ strikeMs,
268+ backend: backendInfo,
269+ });
270+ },
271+ [runner, methodPlan],
272+ );
273+
274+ const run = useCallback(async () => {
275+ if (running || hasErrors) return;
276+ await runMethod(method, cloneSpec(spec), { ...edits }, stamp);
277+ }, [running, hasErrors, runMethod, method, spec, edits, stamp]);
278+
279+ const runBoth = useCallback(async () => {
280+ if (running || hasErrors) return;
281+ const s = cloneSpec(spec);
282+ const ed = { ...edits };
283+ for (const m of METHODS) {
284+ const st = JSON.stringify({ spec: s, edits: ed, method: m.id });
285+ await runMethod(m, s, ed, st);
286+ }
287+ setMiddleTab("compare");
288+ }, [running, hasErrors, runMethod, spec, edits, setMiddleTab]);
289+
290+ const cancel = () => {
291+ runSeq.current++;
292+ runner.cancel();
293+ setStatus({ status: "idle" });
294+ };
295+
296+ const resetAll = () => {
297+ if (!window.confirm("Reset every setting and source edit to the defaults?")) return;
298+ clearPersistentState();
299+ setSpec(defaultSpec());
300+ setMethodId("rr");
301+ setEdits({});
302+ setRightTab("equations");
303+ setMiddleTab("result");
304+ setAdvancedOpen(false);
305+ setResults({});
306+ setStatus({ status: "idle" });
307+ modesCache.current.clear();
308+ };
309+
310+ useEffect(() => {
311+ const onKey = (e: KeyboardEvent) => {
312+ if ((e.ctrlKey || e.metaKey) && e.key === "Enter") {
313+ e.preventDefault();
314+ void run();
315+ }
316+ };
317+ window.addEventListener("keydown", onKey);
318+ return () => window.removeEventListener("keydown", onKey);
319+ }, [run]);
320+
321+ const current = results[method.id] ?? null;
322+ const stale = current !== null && !running && current.stamp !== stamp;
323+ const estimate = methodPlan
324+ ? {
325+ modesMs: modesKept ? null : cost.current.modesMs(method, methodPlan.work),
326+ strikeMs: cost.current.strikeMs(
327+ method,
328+ methodPlan.nsamples,
329+ Math.min(methodPlan.modes_cap, methodPlan.modes_expected + 6) + 3,
330+ ),
331+ }
332+ : undefined;
333+ const estimateMs = estimate ? (estimate.modesMs ?? 0) + estimate.strikeMs : undefined;
334+ const slow = estimateMs !== undefined && estimateMs > 5000;
335+ const advancedIssue = issues.some(
336+ (i) =>
337+ i.severity === "error" &&
338+ ADVANCED_PREFIXES.some((p) => i.path === p || i.path.startsWith(p + ".")),
339+ );
340+ const numericsSection: ParameterSection = method.id === "rr" ? "rr" : "fem";
341+
342+ return (
343+ <div className="app">
344+ <header className="app-header">
345+ <div>
346+ <h1>Plate Dynamics Lab 2</h1>
347+ <div className="sub">
348+ A small metal plate on a thread, struck by a hammer: its modes, the contact, the sound,
349+ by two independent methods, solved by MATLAB code running in your browser.
350+ </div>
351+ </div>
352+ <div className="method-picker">
353+ <span className="picker-label">Scenario</span>
354+ <select
355+ className="pl-select"
356+ style={{ width: "auto" }}
357+ value={scenarioId}
358+ onChange={(e) => {
359+ const s = SCENARIO_PRESETS.find((x) => x.id === e.target.value);
360+ if (s) setSpec(cloneSpec(s.spec));
361+ }}
362+ >
363+ <option value="">custom settings</option>
364+ {SCENARIO_PRESETS.map((s) => (
365+ <option key={s.id} value={s.id}>
366+ {s.name}
367+ </option>
368+ ))}
369+ </select>
370+ <span className="picker-label">Method</span>
371+ <select
372+ className="pl-select"
373+ style={{ width: "auto" }}
374+ value={method.id}
375+ onChange={(e) => setMethodId(e.target.value as MethodId)}
376+ title={method.summary}
377+ >
378+ {METHODS.map((m) => (
379+ <option key={m.id} value={m.id}>
380+ {m.name}
381+ </option>
382+ ))}
383+ </select>
384+ </div>
385+ </header>
386+ <div className="app-body">
387+ <aside className="col">
388+ <div className="action-bar">
389+ {running ? (
390+ <button className="pl-btn pl-btn-danger run-btn" onClick={cancel}>
391+ Cancel
392+ </button>
393+ ) : (
394+ <button
395+ className={`pl-btn pl-btn-primary run-btn${stale ? " run-btn-stale" : ""}`}
396+ onClick={() => void run()}
397+ disabled={hasErrors}
398+ title="Ctrl+Enter or Cmd+Enter"
399+ >
400+ Run {method.shortName}
401+ </button>
402+ )}
403+ <div className="action-meta">
404+ {running && (
405+ <span className="pl-badge pl-badge-info">
406+ {status.status === "running" && status.stage === "modes"
407+ ? "solving the modes"
408+ : status.status === "running" && status.stage === "strike"
409+ ? "striking"
410+ : "running"}
411+ </span>
412+ )}
413+ {!running && status.status === "done" && (
414+ <span className="pl-badge pl-badge-ok">finished</span>
415+ )}
416+ {!running && status.status === "error" && (
417+ <span className="pl-badge pl-badge-error">error</span>
418+ )}
419+ {slow && (
420+ <span className="pl-badge pl-badge-warn">
421+ about {(estimateMs! / 1000).toFixed(0)} s
422+ </span>
423+ )}
424+ {!slow && estimateMs !== undefined && (
425+ <span className="pl-muted">
426+ about{" "}
427+ {estimateMs < 1000
428+ ? `${estimateMs.toFixed(0)} ms`
429+ : `${(estimateMs / 1000).toFixed(1)} s`}
430+ {modesKept ? ", modes kept" : ""}
431+ </span>
432+ )}
433+ <span style={{ flex: 1 }} />
434+ <button
435+ className="pl-btn"
436+ onClick={() => void runBoth()}
437+ disabled={running || hasErrors}
438+ title="Run both methods on these settings, then open the comparison"
439+ >
440+ Run both
441+ </button>
442+ <button
443+ className="pl-btn"
444+ onClick={resetAll}
445+ title="Forget every setting and source edit saved in this browser"
446+ >
447+ Reset
448+ </button>
449+ </div>
450+ {hasErrors && !running && (
451+ <div className="pl-issue">
452+ Some settings are out of range; the groups that need attention are marked below.
453+ </div>
454+ )}
455+ </div>
456+ <div className="method-note">
457+ <strong>{method.name}</strong>
458+ <div className="pl-muted">{method.summary}</div>
459+ </div>
460+ {scenarioId && (
461+ <div className="method-note">
462+ <strong>{SCENARIO_PRESETS.find((s) => s.id === scenarioId)?.name}</strong>
463+ <div className="pl-muted">
464+ {SCENARIO_PRESETS.find((s) => s.id === scenarioId)?.note}
465+ </div>
466+ </div>
467+ )}
468+ {mainEdited && (
469+ <div className="banner">
470+ <span>
471+ The generated script was edited, so the controls no longer feed the run, which runs
472+ the script as one piece. Keep editing the source, or restore it to reconnect the
473+ controls.
474+ </span>
475+ <button
476+ className="pl-btn"
477+ onClick={() =>
478+ setEdits((e) =>
479+ Object.fromEntries(Object.entries(e).filter(([k]) => k !== "main.m")),
480+ )
481+ }
482+ >
483+ Restore
484+ </button>
485+ </div>
486+ )}
487+ <div className={mainEdited ? "pl-disabled" : undefined}>
488+ <div className="panel-head">On the bench</div>
489+ <ParameterPanel
490+ spec={spec}
491+ onChange={setSpec}
492+ issues={issues}
493+ sections={PROBLEM_SECTIONS}
494+ open={["hammer", "strike"]}
495+ />
496+ {methodPlan && !hasErrors && <PlanSummary plan={methodPlan} />}
497+ {planError && (
498+ <div className="pl-note">The setup refuses this problem: {planError}</div>
499+ )}
500+ <div className="panel-head">Numerics</div>
501+ <ParameterPanel
502+ spec={spec}
503+ onChange={setSpec}
504+ issues={issues}
505+ sections={[numericsSection]}
506+ open={[numericsSection]}
507+ />
508+ {methodPlan && (
509+ <div className="pl-small pl-muted plan-line">
510+ {methodPlan.size_note}; about {methodPlan.modes_expected} modes below{" "}
511+ {(methodPlan.f_max / 1000).toFixed(1)} kHz; {methodPlan.nsamples.toLocaleString()}{" "}
512+ samples. {modesKept ? "The modes of this plate are kept from an earlier run." : ""}
513+ </div>
514+ )}
515+ <details
516+ className="advanced"
517+ open={advancedOpen || advancedIssue}
518+ onToggle={(e) => setAdvancedOpen((e.currentTarget as HTMLDetailsElement).open)}
519+ >
520+ <summary>
521+ More settings: probe, microphone, render
522+ {advancedIssue && (
523+ <span className="pl-badge pl-badge-error" style={{ marginLeft: 6 }}>
524+ check
525+ </span>
526+ )}
527+ </summary>
528+ <ParameterPanel
529+ spec={spec}
530+ onChange={setSpec}
531+ issues={issues}
532+ sections={ADVANCED_SECTIONS}
533+ open={ADVANCED_SECTIONS}
534+ />
535+ </details>
536+ </div>
537+ <div className="pl-small pl-muted">
538+ Settings and source edits are saved in this browser. Ctrl+Enter runs.
539+ </div>
540+ </aside>
541+
542+ <main className="col">
543+ <div className="tabs" role="tablist">
544+ <button
545+ className={middleTab === "result" ? "active" : ""}
546+ onClick={() => setMiddleTab("result")}
547+ role="tab"
548+ aria-selected={middleTab === "result"}
549+ >
550+ {method.shortName}
551+ </button>
552+ <button
553+ className={middleTab === "compare" ? "active" : ""}
554+ onClick={() => setMiddleTab("compare")}
555+ role="tab"
556+ aria-selected={middleTab === "compare"}
557+ >
558+ Comparison
559+ </button>
560+ </div>
561+ {middleTab === "result" && (
562+ <>
563+ {stale && (
564+ <div className="banner">
565+ <span>
566+ The settings changed after this run, so what you see below is from the earlier
567+ ones.
568+ </span>
569+ <button className="pl-btn" onClick={() => void run()}>
570+ Run again
571+ </button>
572+ </div>
573+ )}
574+ {status.status === "error" && <div className="pl-error">{status.error}</div>}
575+ {current && (
576+ <ResultsView result={current.result} spec={current.spec} method={method} />
577+ )}
578+ {!current && <EmptyState status={status.status} method={method.shortName} />}
579+ </>
580+ )}
581+ {middleTab === "compare" && <ComparisonView results={results} current={spec} />}
582+ </main>
583+
584+ <section className="col-right">
585+ <div className="tabs" role="tablist">
586+ {RIGHT_TABS.map((t) => (
587+ <button
588+ key={t.id}
589+ className={rightTab === t.id ? "active" : ""}
590+ onClick={() => setRightTab(t.id)}
591+ role="tab"
592+ aria-selected={rightTab === t.id}
593+ >
594+ {t.label}
595+ </button>
596+ ))}
597+ </div>
598+ {rightTab === "equations" && (
599+ <div className="tab-body">
600+ <div className="pl-small pl-muted tab-intro">
601+ What the lab solves and how the {method.shortName} method discretizes it. The code
602+ that does it is under Source.
603+ </div>
604+ {method.equationGroups.map((g) => (
605+ <div key={g.title} className="eq-group">
606+ <h3>{g.title}</h3>
607+ <EquationsView equations={g.equations} />
608+ </div>
609+ ))}
610+ </div>
611+ )}
612+ {rightTab === "source" && (
613+ <CodeView
614+ files={displayBundle.files}
615+ edits={edits}
616+ onEditsChange={setEdits}
617+ order={["main.m", ...method.readingOrder]}
618+ summaries={SUMMARIES}
619+ height="100%"
620+ />
621+ )}
622+ {rightTab === "run" && (
623+ <div className="tab-body" style={{ display: "flex", flexDirection: "column", gap: 12 }}>
624+ <RunStatus
625+ state={status}
626+ result={current?.result}
627+ method={method}
628+ backend={backend}
629+ estimate={estimate}
630+ onCancel={cancel}
631+ />
632+ </div>
633+ )}
634+ </section>
635+ </div>
636+ </div>
637+ );
638+}
639+
640+/** The estimates the plan found, before any run. */
641+function PlanSummary({ plan }: { plan: MethodPlan }) {
642+ const lines: string[] = [];
643+ lines.push(`mass ${(plan.plate_mass * 1000).toPrecision(3)} g`);
644+ lines.push(`pendulum ${plan.pendulum_hz.toFixed(2)} Hz`);
645+ lines.push(
646+ `Hertz contact ${(plan.hertz_duration * 1e6).toPrecision(3)} µs, ${plan.hertz_peak_force.toPrecision(3)} N peak, ${(plan.hertz_impulse * 1e3).toPrecision(3)} mN s on a rigid target`,
647+ );
648+ lines.push(`coincidence ${(plan.critical_frequency / 1000).toPrecision(3)} kHz`);
649+ return (
650+ <div className="pl-small pl-muted plan-line">
651+ Statics: {lines.join("; ")}.
652+ {plan.notes.map((n) => (
653+ <div key={n} className="pl-issue pl-issue-warning">
654+ {n}
655+ </div>
656+ ))}
657+ </div>
658+ );
659+}
660+
661+/** What the middle column says before anything has been solved. */
662+function EmptyState({ status, method }: { status: string; method: string }) {
663+ if (status === "running") return <div className="empty">Solving…</div>;
664+ if (status === "error")
665+ return (
666+ <div className="empty">
667+ The run failed. The message is above, and the full log is under Run details.
668+ </div>
669+ );
670+ return (
671+ <div className="empty">
672+ <h2>Nothing solved yet by {method}</h2>
673+ <ol>
674+ <li>
675+ Pick a scenario and a method at the top, or set the plate, the hammer and the strike on
676+ the left.
677+ </li>
678+ <li>
679+ Press <strong>Run</strong>, or Ctrl+Enter. <strong>Run both</strong> solves the other
680+ method too and opens the comparison.
681+ </li>
682+ <li>
683+ The plate on its thread, its modes, the contact, the fields, the probe traces, the
684+ spectrum and the sound appear here.
685+ </li>
686+ </ol>
687+ <p className="pl-muted">
688+ The equations are on the right; Source shows the MATLAB that runs, and you can edit it.
689+ </p>
690+ </div>
691+ );
692+}
apps/lab/src/main.tsxadded+14−0View file
@@ -0,0 +1,14 @@
1+import { StrictMode } from "react";
2+import { createRoot } from "react-dom/client";
3+import { RunnerProvider } from "@plate-dyn/components";
4+import "@plate-dyn/components/styles.css";
5+import "./styles.css";
6+import { App } from "./App";
7+
8+createRoot(document.getElementById("root")!).render(
9+ <StrictMode>
10+ <RunnerProvider>
11+ <App />
12+ </RunnerProvider>
13+ </StrictMode>,
14+);
apps/lab/src/styles.cssadded+329−0View file
@@ -0,0 +1,329 @@
1+* {
2+ box-sizing: border-box;
3+}
4+html,
5+body,
6+#root {
7+ height: 100%;
8+ margin: 0;
9+}
10+body {
11+ font-family: var(--pl-font);
12+ color: var(--pl-text);
13+ background: #eef0f3;
14+ font-size: 13px;
15+}
16+.app {
17+ display: grid;
18+ grid-template-rows: auto 1fr;
19+ height: 100%;
20+}
21+.app-header {
22+ display: flex;
23+ align-items: center;
24+ gap: 14px;
25+ padding: 8px 14px;
26+ background: #fff;
27+ border-bottom: 1px solid var(--pl-border);
28+ flex-wrap: wrap;
29+}
30+.app-header h1 {
31+ font-size: 16px;
32+ margin: 0;
33+ font-weight: 650;
34+}
35+.app-header .sub {
36+ color: var(--pl-muted);
37+ font-size: 12px;
38+}
39+.method-picker {
40+ display: flex;
41+ align-items: center;
42+ gap: 8px;
43+ margin-left: auto;
44+}
45+.method-picker .picker-label {
46+ font-size: 11px;
47+ text-transform: uppercase;
48+ letter-spacing: 0.06em;
49+ color: var(--pl-muted);
50+}
51+.mode-tabs {
52+ display: flex;
53+ gap: 2px;
54+ background: var(--pl-panel);
55+ border: 1px solid var(--pl-border);
56+ border-radius: 6px;
57+ padding: 2px;
58+}
59+.mode-tabs button {
60+ font: inherit;
61+ padding: 4px 12px;
62+ border: 0;
63+ background: transparent;
64+ border-radius: 4px;
65+ cursor: pointer;
66+ color: var(--pl-muted);
67+}
68+.mode-tabs button.active {
69+ background: #fff;
70+ color: var(--pl-text);
71+ box-shadow: 0 1px 2px rgba(0, 0, 0, 0.08);
72+}
73+.app-body {
74+ display: grid;
75+ grid-template-columns: 320px minmax(420px, 1fr) minmax(460px, 560px);
76+ gap: 10px;
77+ padding: 10px;
78+ min-height: 0;
79+}
80+.col {
81+ min-height: 0;
82+ overflow: auto;
83+ display: flex;
84+ flex-direction: column;
85+ gap: 10px;
86+}
87+.action-bar {
88+ position: sticky;
89+ top: 0;
90+ z-index: 2;
91+ background: #eef0f3;
92+ padding-bottom: 6px;
93+ display: flex;
94+ flex-direction: column;
95+ gap: 6px;
96+}
97+.run-btn {
98+ width: 100%;
99+ padding: 9px 12px;
100+ font-size: 14px;
101+ font-weight: 600;
102+}
103+.action-meta {
104+ display: flex;
105+ gap: 8px;
106+ align-items: center;
107+ font-size: 12px;
108+ flex-wrap: wrap;
109+}
110+.col-right {
111+ display: grid;
112+ grid-template-rows: auto 1fr;
113+ gap: 6px;
114+ min-height: 0;
115+}
116+.col-right .pl-code {
117+ min-height: 320px;
118+}
119+.tabs {
120+ display: flex;
121+ gap: 2px;
122+ border-bottom: 1px solid var(--pl-border);
123+}
124+.tabs button {
125+ font: inherit;
126+ font-size: 12px;
127+ padding: 5px 10px;
128+ border: 1px solid transparent;
129+ border-bottom: 0;
130+ background: transparent;
131+ cursor: pointer;
132+ color: var(--pl-muted);
133+ border-radius: 5px 5px 0 0;
134+}
135+.tabs button.active {
136+ background: #fff;
137+ border-color: var(--pl-border);
138+ color: var(--pl-text);
139+}
140+.tab-body {
141+ background: #fff;
142+ border: 1px solid var(--pl-border);
143+ border-top: 0;
144+ border-radius: 0 0 6px 6px;
145+ padding: 10px;
146+ overflow: auto;
147+ min-height: 0;
148+}
149+.results {
150+ display: flex;
151+ flex-direction: column;
152+ gap: 12px;
153+}
154+.results-toolbar {
155+ display: flex;
156+ gap: 8px;
157+ align-items: center;
158+ flex-wrap: wrap;
159+ font-size: 12px;
160+}
161+.two-up {
162+ display: grid;
163+ grid-template-columns: 1fr 1fr;
164+ gap: 10px;
165+}
166+.modes-table {
167+ font-size: 11px;
168+ border-collapse: collapse;
169+}
170+.modes-table td,
171+.modes-table th {
172+ padding: 1px 8px;
173+ text-align: right;
174+ border-bottom: 1px solid var(--pl-border);
175+}
176+.modes-table th:first-child,
177+.modes-table td:first-child {
178+ text-align: left;
179+}
180+.banner {
181+ padding: 8px 10px;
182+ border-radius: 6px;
183+ background: var(--pl-warn-bg);
184+ border: 1px solid #f1d7a8;
185+ font-size: 12px;
186+ display: flex;
187+ gap: 8px;
188+ align-items: center;
189+}
190+.empty {
191+ color: var(--pl-muted);
192+ padding: 24px 26px;
193+ background: #fff;
194+ border: 1px dashed var(--pl-border);
195+ border-radius: 6px;
196+ max-width: 560px;
197+}
198+.empty h2 {
199+ font-size: 15px;
200+ margin: 0 0 8px;
201+ color: var(--pl-text);
202+ font-weight: 650;
203+}
204+.empty ol {
205+ margin: 0 0 10px;
206+ padding-left: 20px;
207+ line-height: 1.7;
208+}
209+.empty p {
210+ margin: 0;
211+ font-size: 12px;
212+}
213+
214+/* Left column structure: the problem, then the numerics, then everything else */
215+.panel-head {
216+ font-size: 11px;
217+ text-transform: uppercase;
218+ letter-spacing: 0.06em;
219+ color: var(--pl-muted);
220+ font-weight: 650;
221+ margin: 12px 0 6px;
222+}
223+.panel-head:first-child {
224+ margin-top: 0;
225+}
226+.method-note {
227+ font-size: 12px;
228+ line-height: 1.45;
229+ padding: 8px 10px;
230+ background: #fff;
231+ border: 1px solid var(--pl-border);
232+ border-left: 3px solid var(--pl-accent);
233+ border-radius: 5px;
234+}
235+.plan-line {
236+ margin-top: 6px;
237+}
238+.pl-params + .pl-params,
239+.plan-line + .pl-params {
240+ margin-top: 10px;
241+}
242+details.advanced {
243+ margin-top: 12px;
244+ border-top: 1px solid var(--pl-border);
245+ padding-top: 8px;
246+}
247+details.advanced > summary {
248+ font-size: 12px;
249+ color: var(--pl-muted);
250+ cursor: pointer;
251+ margin-bottom: 8px;
252+}
253+details.advanced > summary:hover {
254+ color: var(--pl-accent);
255+}
256+/* The result on screen is from other settings: the button says so without moving. */
257+.run-btn-stale {
258+ box-shadow: 0 0 0 3px rgba(31, 95, 168, 0.25);
259+}
260+kbd {
261+ font-family: var(--pl-mono);
262+ font-size: 11px;
263+ border: 1px solid var(--pl-border);
264+ border-bottom-width: 2px;
265+ border-radius: 3px;
266+ padding: 0 4px;
267+ background: #fff;
268+}
269+@media (max-width: 1250px) {
270+ .app-body {
271+ grid-template-columns: 300px 1fr;
272+ }
273+ .col-right {
274+ grid-column: 1 / -1;
275+ }
276+}
277+@media (max-width: 800px) {
278+ .app-body {
279+ grid-template-columns: 1fr;
280+ }
281+ .col-right {
282+ grid-column: auto;
283+ }
284+}
285+.sound-bar {
286+ padding: 6px 8px;
287+ background: #fff;
288+ border: 1px solid var(--pl-border);
289+ border-radius: 6px;
290+}
291+.pair-picker .pl-select {
292+ width: auto;
293+ flex: 1;
294+}
295+.tab-intro {
296+ margin-bottom: 10px;
297+ padding-bottom: 8px;
298+ border-bottom: 1px solid var(--pl-border);
299+}
300+
301+/* Equation groups */
302+.eq-group {
303+ margin-bottom: 14px;
304+}
305+.eq-group h3 {
306+ font-size: 12px;
307+ text-transform: uppercase;
308+ letter-spacing: 0.05em;
309+ color: var(--pl-muted);
310+ margin: 10px 0 6px;
311+}
312+
313+/* Plate Dynamics Lab 2 additions */
314+.modes-table tr.selected td {
315+ background: #e6eef9;
316+}
317+.modes-table tbody tr:hover td {
318+ background: #f3f6fa;
319+}
320+.modes-table .bar {
321+ display: inline-block;
322+ height: 8px;
323+ background: var(--pl-accent);
324+ border-radius: 2px;
325+ vertical-align: middle;
326+}
327+main.col > .tabs {
328+ margin-bottom: 8px;
329+}
apps/lab/src/usePersistentState.tsadded+47−0View file
@@ -0,0 +1,47 @@
1+import { useCallback, useEffect, useState } from "react";
2+
3+const PREFIX = "plate-dyn:";
4+
5+/** React state mirrored into localStorage under `key`. `validate` rejects stale shapes. */
6+export function usePersistentState<T>(
7+ key: string,
8+ initial: () => T,
9+ validate?: (v: unknown) => v is T,
10+): [T, (v: T | ((prev: T) => T)) => void, () => void] {
11+ const full = PREFIX + key;
12+ const [value, setValue] = useState<T>(() => {
13+ try {
14+ const raw = localStorage.getItem(full);
15+ if (raw !== null) {
16+ const parsed: unknown = JSON.parse(raw);
17+ if (!validate || validate(parsed)) return parsed as T;
18+ }
19+ } catch {
20+ /* fall back to the default */
21+ }
22+ return initial();
23+ });
24+ useEffect(() => {
25+ try {
26+ localStorage.setItem(full, JSON.stringify(value));
27+ } catch {
28+ /* storage unavailable: state simply does not persist */
29+ }
30+ }, [full, value]);
31+ const reset = useCallback(() => setValue(initial()), [initial]);
32+ return [value, setValue, reset];
33+}
34+
35+/** Forget everything the lab has stored in this browser. */
36+export function clearPersistentState(): void {
37+ try {
38+ const keys: string[] = [];
39+ for (let i = 0; i < localStorage.length; i++) {
40+ const k = localStorage.key(i);
41+ if (k && k.startsWith(PREFIX)) keys.push(k);
42+ }
43+ keys.forEach((k) => localStorage.removeItem(k));
44+ } catch {
45+ /* ignore */
46+ }
47+}
apps/lab/src/views/ComparisonView.tsxadded+295−0View file
@@ -0,0 +1,295 @@
1+import { useMemo } from "react";
2+import { methodById, type MethodId, type PlateResult, type ProblemSpec } from "@plate-dyn/core";
3+import { AudioPlayer, LinePlot, useSharedAxis } from "@plate-dyn/components";
4+import type { KeptResult } from "../App";
5+
6+export interface ComparisonViewProps {
7+ results: Partial<Record<MethodId, KeptResult>>;
8+ /** The settings on the bench now, to say whether the kept results match them */
9+ current: ProblemSpec;
10+}
11+
12+const RR_COLOR = "#1f5fa8";
13+const FEM_COLOR = "#d1495b";
14+
15+/**
16+ * The last result of each method, side by side: the modes, the contact, the
17+ * spectra, the sound. The two are independent solutions of the same object,
18+ * so where they agree the agreement means something, and where they differ
19+ * the difference has a cause the tables name.
20+ */
21+export function ComparisonView({ results, current }: ComparisonViewProps) {
22+ const rr = results.rr;
23+ const fem = results.fem;
24+ const axis = useSharedAxis();
25+ const spectrumAxis = useSharedAxis();
26+ const forceAxis = useSharedAxis();
27+ const sameInputs = rr && fem && sameProblem(rr.spec, fem.spec);
28+ const rrStale = rr && !sameProblem(rr.spec, current);
29+ const femStale = fem && !sameProblem(fem.spec, current);
30+ const rows = useMemo(() => (rr && fem ? modeRows(rr.result, fem.result) : []), [rr, fem]);
31+
32+ if (!rr || !fem)
33+ return (
34+ <div className="empty">
35+ <h2>Two results are needed</h2>
36+ <p className="pl-muted">
37+ {rr ? "Rayleigh-Ritz has run" : "Rayleigh-Ritz has not run"};{" "}
38+ {fem ? "the finite-element method has run" : "the finite-element method has not run"}.
39+ Press <strong>Run both</strong>, or run each method in turn, and the comparison lines up
40+ the last result of each.
41+ </p>
42+ </div>
43+ );
44+
45+ const a = rr.result;
46+ const b = fem.result;
47+ const ca = a.contact;
48+ const cb = b.contact;
49+ const pct = (x: number, y: number) => (x === 0 ? "–" : `${(((y - x) / x) * 100).toFixed(2)} %`);
50+ const audioT = (r: PlateResult) =>
51+ Float64Array.from({ length: r.audio.p.length }, (_, i) => i / r.audio.fs);
52+
53+ return (
54+ <div className="results">
55+ {!sameInputs && (
56+ <div className="banner">
57+ <span>
58+ The two results come from different settings (the plate, the hammer, the strike or the
59+ render differ), so this is not a like-for-like comparison. Run both on one set of
60+ settings.
61+ </span>
62+ </div>
63+ )}
64+ {sameInputs && (rrStale || femStale) && (
65+ <div className="banner">
66+ <span>The settings changed after these runs; the comparison is of the earlier ones.</span>
67+ </div>
68+ )}
69+ <div className="pl-small pl-muted">
70+ {methodById("rr").name}: {a.info.size_note}. {methodById("fem").name}: {b.info.size_note}.
71+ The two share the plate, the hammer, the damping constants and the radiation model, and
72+ nothing else: different plate theory, different discretization, different eigensolver,
73+ different contact.
74+ </div>
75+
76+ <div className="pl-plot-title">The contact</div>
77+ <table className="modes-table">
78+ <thead>
79+ <tr>
80+ <th>Quantity</th>
81+ <th>Rayleigh-Ritz</th>
82+ <th>Finite elements</th>
83+ <th>Difference</th>
84+ </tr>
85+ </thead>
86+ <tbody>
87+ <tr>
88+ <td>Contact time</td>
89+ <td>{(ca.tc * 1e6).toPrecision(4)} µs</td>
90+ <td>{(cb.tc * 1e6).toPrecision(4)} µs</td>
91+ <td>{pct(ca.tc, cb.tc)}</td>
92+ </tr>
93+ <tr>
94+ <td>Peak force</td>
95+ <td>{ca.peak_force.toPrecision(4)} N</td>
96+ <td>{cb.peak_force.toPrecision(4)} N</td>
97+ <td>{pct(ca.peak_force, cb.peak_force)}</td>
98+ </tr>
99+ <tr>
100+ <td>Impulse</td>
101+ <td>{(ca.impulse * 1e3).toPrecision(4)} mN s</td>
102+ <td>{(cb.impulse * 1e3).toPrecision(4)} mN s</td>
103+ <td>{pct(ca.impulse, cb.impulse)}</td>
104+ </tr>
105+ <tr>
106+ <td>Plate under the hammer at separation</td>
107+ <td>{ca.plate_exit_v.toPrecision(3)} m/s</td>
108+ <td>{cb.plate_exit_v.toPrecision(3)} m/s</td>
109+ <td>{pct(ca.plate_exit_v, cb.plate_exit_v)}</td>
110+ </tr>
111+ <tr>
112+ <td>Hammer at separation</td>
113+ <td>not modelled</td>
114+ <td>{cb.hammer_exit_v.toPrecision(3)} m/s into the plate</td>
115+ <td></td>
116+ </tr>
117+ <tr>
118+ <td>Effective restitution</td>
119+ <td>{ca.effective_restitution} (nominal)</td>
120+ <td>{cb.effective_restitution.toPrecision(3)}</td>
121+ <td></td>
122+ </tr>
123+ <tr>
124+ <td>Audible modes</td>
125+ <td>{a.info.n_audible}</td>
126+ <td>{b.info.n_audible}</td>
127+ <td></td>
128+ </tr>
129+ <tr>
130+ <td>Modes stage</td>
131+ <td>{fmtS(a.info.modes_wall_s)}</td>
132+ <td>{fmtS(b.info.modes_wall_s)}</td>
133+ <td></td>
134+ </tr>
135+ </tbody>
136+ </table>
137+ <div className="pl-small pl-muted">
138+ The resonances agree to a percent or two although the theories, bases and eigensolvers
139+ differ; the contact numbers differ sharply for a reason that can be named: the pulse is the
140+ force on a rigid target, while the coupled model lets this very light plate move away under
141+ the hammer, so the contact lasts longer, the force is far lower and less momentum is
142+ transferred.
143+ </div>
144+
145+ <LinePlot
146+ title="Contact force"
147+ series={[
148+ { x: ca.t, y: ca.force, label: "Rayleigh-Ritz (prescribed)", color: RR_COLOR },
149+ { x: cb.t, y: cb.force, label: "finite elements (coupled)", color: FEM_COLOR },
150+ ]}
151+ xScale={1e6}
152+ xLabel="time [µs]"
153+ yLabel="F [N]"
154+ yUnit="N"
155+ height={170}
156+ {...forceAxis}
157+ />
158+
159+ <div className="pl-plot-title">Modes, in order</div>
160+ <div style={{ overflowX: "auto" }}>
161+ <table className="modes-table">
162+ <thead>
163+ <tr>
164+ <th>#</th>
165+ <th>RR f [Hz]</th>
166+ <th>FEM f [Hz]</th>
167+ <th>Difference</th>
168+ <th>RR |at the strike|</th>
169+ <th>FEM |at the strike|</th>
170+ <th>RR Q</th>
171+ <th>FEM Q</th>
172+ </tr>
173+ </thead>
174+ <tbody>
175+ {rows.map((r) => (
176+ <tr key={r.k}>
177+ <td>{r.k + 1}</td>
178+ <td>{r.fa === null ? "" : r.fa.toFixed(1)}</td>
179+ <td>{r.fb === null ? "" : r.fb.toFixed(1)}</td>
180+ <td>{r.fa !== null && r.fb !== null ? pct(r.fa, r.fb) : ""}</td>
181+ <td>{r.ha === null ? "" : r.ha.toPrecision(3)}</td>
182+ <td>{r.hb === null ? "" : r.hb.toPrecision(3)}</td>
183+ <td>{r.qa === null ? "" : r.qa.toFixed(0)}</td>
184+ <td>{r.qb === null ? "" : r.qb.toFixed(0)}</td>
185+ </tr>
186+ ))}
187+ </tbody>
188+ </table>
189+ </div>
190+ <div className="pl-small pl-muted">
191+ Audible modes matched by order; the rigid-body modes of the finite-element method are left
192+ out. The finite-element frequencies converge from above as its mesh is refined (the element
193+ locks in shear), so at a coarse mesh they run high. Degenerate pairs of a square plate can
194+ swap places between the two lists.
195+ </div>
196+
197+ <LinePlot
198+ title="Amplitude spectrum of the sound"
199+ series={[
200+ { x: a.spectrum.f, y: a.spectrum.p, label: "Rayleigh-Ritz", color: RR_COLOR },
201+ { x: b.spectrum.f, y: b.spectrum.p, label: "finite elements", color: FEM_COLOR },
202+ ]}
203+ defaultXRange={[0, Math.max(a.info.f_max, b.info.f_max)]}
204+ logY
205+ xLabel="frequency [Hz]"
206+ yLabel="amplitude [Pa]"
207+ height={200}
208+ {...spectrumAxis}
209+ />
210+ <LinePlot
211+ title="Pressure at the microphone, unnormalized"
212+ series={[
213+ { x: audioT(a), y: a.audio.p, label: "Rayleigh-Ritz", color: RR_COLOR },
214+ { x: audioT(b), y: b.audio.p, label: "finite elements", color: FEM_COLOR },
215+ ]}
216+ defaultXRange={[0, 0.003]}
217+ xLabel="time [s]"
218+ yLabel="p [Pa]"
219+ yUnit="Pa"
220+ height={150}
221+ {...axis}
222+ />
223+ <div className="pl-small pl-muted">
224+ The absolute levels differ with the impulse; the exports below are each peak-normalized, so
225+ compare pitch, spectral balance and decay, not loudness.
226+ </div>
227+ <div className="two-up">
228+ <div>
229+ <div className="pl-plot-title">Rayleigh-Ritz</div>
230+ <AudioPlayer
231+ p={a.audio.p}
232+ fs={a.audio.fs}
233+ normalizeDbfs={a.audio.normalize_dbfs}
234+ filename="plate-rr"
235+ />
236+ </div>
237+ <div>
238+ <div className="pl-plot-title">Finite elements</div>
239+ <AudioPlayer
240+ p={b.audio.p}
241+ fs={b.audio.fs}
242+ normalizeDbfs={b.audio.normalize_dbfs}
243+ filename="plate-fem"
244+ />
245+ </div>
246+ </div>
247+ </div>
248+ );
249+}
250+
251+interface ModeRow {
252+ k: number;
253+ fa: number | null;
254+ fb: number | null;
255+ ha: number | null;
256+ hb: number | null;
257+ qa: number | null;
258+ qb: number | null;
259+}
260+
261+function modeRows(a: PlateResult, b: PlateResult): ModeRow[] {
262+ const pick = (r: PlateResult) => {
263+ const idx: number[] = [];
264+ r.modes.audible.forEach((ok, k) => ok && idx.push(k));
265+ return idx;
266+ };
267+ const ia = pick(a);
268+ const ib = pick(b);
269+ const n = Math.max(ia.length, ib.length);
270+ const rows: ModeRow[] = [];
271+ for (let k = 0; k < n; k++) {
272+ const ka = ia[k];
273+ const kb = ib[k];
274+ rows.push({
275+ k,
276+ fa: ka === undefined ? null : a.modes.freq[ka],
277+ fb: kb === undefined ? null : b.modes.freq[kb],
278+ ha: ka === undefined ? null : Math.abs(a.modes.hit[ka]),
279+ hb: kb === undefined ? null : Math.abs(b.modes.hit[kb]),
280+ qa: ka === undefined ? null : a.modes.q[ka],
281+ qb: kb === undefined ? null : b.modes.q[kb],
282+ });
283+ }
284+ return rows;
285+}
286+
287+/** Whether two specs describe the same physical problem (the numerics may differ). */
288+function sameProblem(x: ProblemSpec, y: ProblemSpec): boolean {
289+ const strip = (s: ProblemSpec) => JSON.stringify({ ...s, rr: undefined, fem: undefined });
290+ return strip(x) === strip(y);
291+}
292+
293+function fmtS(s: number): string {
294+ return s < 1 ? `${(s * 1000).toFixed(0)} ms` : `${s.toFixed(2)} s`;
295+}
apps/lab/src/views/ContactPlots.tsxadded+95−0View file
@@ -0,0 +1,95 @@
1+import type { PlateResult, ProblemSpec } from "@plate-dyn/core";
2+import { LinePlot, useSharedAxis } from "@plate-dyn/components";
3+
4+/** The contact: the force, the hammer and the plate under it, and the numbers. */
5+export function ContactPlots({ result, spec }: { result: PlateResult; spec: ProblemSpec }) {
6+ const c = result.contact;
7+ const axis = useSharedAxis();
8+ const coupled = c.hammer_v.length > 0;
9+ const velocities = coupled
10+ ? [
11+ { x: c.t, y: c.hammer_v, label: "hammer" },
12+ { x: c.t, y: c.plate_v, label: "plate under it", color: "#d1495b" },
13+ ]
14+ : [{ x: c.t, y: c.plate_v, label: "plate under the hammer", color: "#d1495b" }];
15+ return (
16+ <div>
17+ <div className="pl-plot-title">The contact</div>
18+ <LinePlot
19+ title="Contact force"
20+ series={[{ x: c.t, y: c.force, label: "F" }]}
21+ xScale={1e6}
22+ xLabel="time [µs]"
23+ yLabel="F [N]"
24+ yUnit="N"
25+ height={150}
26+ {...axis}
27+ />
28+ <LinePlot
29+ title="Velocities, positive into the plate"
30+ series={velocities}
31+ xScale={1e6}
32+ xLabel="time [µs]"
33+ yLabel="v [m/s]"
34+ yUnit="m/s"
35+ height={150}
36+ {...axis}
37+ />
38+ <table className="modes-table" style={{ marginTop: 6 }}>
39+ <tbody>
40+ <tr>
41+ <td>Model</td>
42+ <td>{c.model}</td>
43+ </tr>
44+ <tr>
45+ <td>Contact time</td>
46+ <td>
47+ {(c.tc * 1e6).toPrecision(4)} µs (Hertz estimate on a rigid target{" "}
48+ {(c.hertz_duration * 1e6).toPrecision(4)} µs)
49+ </td>
50+ </tr>
51+ <tr>
52+ <td>Peak force</td>
53+ <td>
54+ {c.peak_force.toPrecision(4)} N (rigid target {c.hertz_peak_force.toPrecision(4)} N)
55+ </td>
56+ </tr>
57+ <tr>
58+ <td>Impulse</td>
59+ <td>
60+ {(c.impulse * 1e3).toPrecision(4)} mN s (rigid target{" "}
61+ {(c.hertz_impulse * 1e3).toPrecision(4)} mN s; the hammer arrived with{" "}
62+ {(spec.hammer.mass * spec.hammer.speed * 1e3).toPrecision(3)} mN s)
63+ </td>
64+ </tr>
65+ {coupled && (
66+ <tr>
67+ <td>At separation</td>
68+ <td>
69+ the hammer still moves into the plate at {c.hammer_exit_v.toPrecision(3)} m/s, the
70+ plate under it at {c.plate_exit_v.toPrecision(3)} m/s; effective restitution{" "}
71+ {c.effective_restitution.toPrecision(3)} against the nominal{" "}
72+ {spec.hammer.restitution}
73+ </td>
74+ </tr>
75+ )}
76+ {!coupled && (
77+ <tr>
78+ <td>At separation</td>
79+ <td>
80+ the plate under the hammer moves at {c.plate_exit_v.toPrecision(3)} m/s; the hammer
81+ is not part of this model, its impulse being prescribed by the nominal restitution{" "}
82+ {spec.hammer.restitution}
83+ </td>
84+ </tr>
85+ )}
86+ </tbody>
87+ </table>
88+ <div className="pl-small pl-muted">
89+ {coupled
90+ ? "The plate is very light: it moves away under the hammer, so the force is lower, the contact longer and the momentum transfer smaller than on a rigid target, and the hammer follows the plate into the strike."
91+ : "The pulse is that of a rigid target, applied as given: what the plate does during the contact does not feed back on the force. Compare with the coupled contact of the finite-element method."}
92+ </div>
93+ </div>
94+ );
95+}
apps/lab/src/views/ModeTable.tsxadded+83−0View file
@@ -0,0 +1,83 @@
1+import type { PlateResult } from "@plate-dyn/core";
2+
3+export interface ModeTableProps {
4+ result: PlateResult;
5+ selected: number | null;
6+ onSelect: (k: number) => void;
7+}
8+
9+/**
10+ * One row per mode: frequency, quality, the shape at the strike point (what
11+ * the hammer can put into it), at the hole (what the thread's dashpot takes
12+ * out) and its volume participation (what radiates). Click a row to see the
13+ * shape.
14+ */
15+export function ModeTable({ result, selected, onSelect }: ModeTableProps) {
16+ const m = result.modes;
17+ const hitPeak = Math.max(...Array.from(m.hit).map(Math.abs), 1e-300);
18+ return (
19+ <div>
20+ <div className="pl-plot-title">Modes</div>
21+ <div style={{ overflowX: "auto" }}>
22+ <table className="modes-table">
23+ <thead>
24+ <tr>
25+ <th>#</th>
26+ <th>f [Hz]</th>
27+ <th>Q</th>
28+ <th>at the strike</th>
29+ <th>at the hole</th>
30+ <th>radiation</th>
31+ <th>excited</th>
32+ </tr>
33+ </thead>
34+ <tbody>
35+ {Array.from(m.freq).map((f, k) => {
36+ const rigid = !m.audible[k];
37+ const rel = Math.abs(m.hit[k]) / hitPeak;
38+ return (
39+ <tr
40+ key={k}
41+ className={selected === k ? "selected" : undefined}
42+ onClick={() => onSelect(k)}
43+ style={{ cursor: "pointer", opacity: rigid ? 0.6 : 1 }}
44+ title="Show this mode's shape"
45+ >
46+ <td>{k + 1}</td>
47+ <td>{rigid ? `${f.toFixed(2)} (rigid)` : f.toFixed(1)}</td>
48+ <td>{rigid ? "–" : m.q[k].toFixed(0)}</td>
49+ <td>{fmt(m.hit[k])}</td>
50+ <td>{fmt(m.support[k])}</td>
51+ <td>{rigid ? "–" : fmt(m.radiation[k])}</td>
52+ <td>
53+ <span
54+ className="bar"
55+ style={{ width: `${Math.round(60 * rel)}px` }}
56+ aria-label={`${(100 * rel).toFixed(0)} percent of the strongest`}
57+ />
58+ </td>
59+ </tr>
60+ );
61+ })}
62+ </tbody>
63+ </table>
64+ </div>
65+ <div className="pl-small pl-muted">
66+ Shapes are mass-normalized (units of 1/√kg), so "at the strike" times the impulse is the
67+ modal momentum the hammer delivers; "radiation" is the integral of the shape over the plate
68+ (m²/√kg), zero for antisymmetric modes, which a microphone on the normal cannot hear.
69+ {result.info.n_rigid > 0
70+ ? " The rigid-body modes take part in the collision and are left out of the sound."
71+ : ""}
72+ </div>
73+ </div>
74+ );
75+}
76+
77+function fmt(x: number): string {
78+ if (!Number.isFinite(x)) return "–";
79+ const a = Math.abs(x);
80+ if (a === 0) return "0";
81+ if (a < 1e-3 || a >= 1e4) return x.toExponential(2);
82+ return x.toPrecision(3);
83+}
apps/lab/src/views/ResultsView.tsxadded+519−0View file
@@ -0,0 +1,519 @@
1+import { useEffect, useMemo, useRef, useState } from "react";
2+import {
3+ holePosition,
4+ prepareAudio,
5+ rangeOf,
6+ symmetricRange,
7+ type Field2D,
8+ type FieldFrames,
9+ type MethodDefinition,
10+ type PlateResult,
11+ type ProblemSpec,
12+ type Range,
13+} from "@plate-dyn/core";
14+import {
15+ AudioPlayer,
16+ FieldView,
17+ FramePlayer,
18+ LinePlot,
19+ MovieExport,
20+ Plate3DView,
21+ ValidityNotes,
22+ useFramePlayer,
23+ useSharedAxis,
24+ type FieldMarker,
25+ type MovieOverlay,
26+ type Plate3DHandle,
27+} from "@plate-dyn/components";
28+import { ModeTable } from "./ModeTable";
29+import { ContactPlots } from "./ContactPlots";
30+
31+type Quantity = "w" | "w_total" | "vm" | "sp" | "env_vm" | "env_sp" | "mode";
32+
33+const QUANTITIES: {
34+ id: Quantity;
35+ label: string;
36+ unit: string;
37+ scale: number;
38+ symmetric: boolean;
39+ frames: boolean;
40+}[] = [
41+ {
42+ id: "w",
43+ label: "Displacement (audible modes)",
44+ unit: "µm",
45+ scale: 1e6,
46+ symmetric: true,
47+ frames: true,
48+ },
49+ {
50+ id: "w_total",
51+ label: "Displacement with the rigid-body modes",
52+ unit: "µm",
53+ scale: 1e6,
54+ symmetric: true,
55+ frames: true,
56+ },
57+ {
58+ id: "vm",
59+ label: "Von Mises stress (surface)",
60+ unit: "MPa",
61+ scale: 1e-6,
62+ symmetric: false,
63+ frames: true,
64+ },
65+ {
66+ id: "sp",
67+ label: "Principal stress, signed (surface)",
68+ unit: "MPa",
69+ scale: 1e-6,
70+ symmetric: true,
71+ frames: true,
72+ },
73+ {
74+ id: "env_vm",
75+ label: "Peak von Mises over the frames",
76+ unit: "MPa",
77+ scale: 1e-6,
78+ symmetric: false,
79+ frames: false,
80+ },
81+ {
82+ id: "env_sp",
83+ label: "Peak |principal stress| over the frames",
84+ unit: "MPa",
85+ scale: 1e-6,
86+ symmetric: false,
87+ frames: false,
88+ },
89+ { id: "mode", label: "A mode shape", unit: "", scale: 1, symmetric: true, frames: false },
90+];
91+
92+export interface ResultsViewProps {
93+ result: PlateResult;
94+ spec: ProblemSpec;
95+ method: MethodDefinition;
96+ fieldWidth?: number;
97+}
98+
99+/** The plate on its thread, the fields, the modes, the contact, the probe, the sound. */
100+export function ResultsView({ result, spec, method, fieldWidth = 460 }: ResultsViewProps) {
101+ const [quantity, setQuantity] = useState<Quantity>("w");
102+ const [rangeMode, setRangeMode] = useState<"global" | "frame">("global");
103+ const [exaggeration, setExaggeration] = useState(300);
104+ const [mode, setMode] = useState(firstAudible(result));
105+ const q = QUANTITIES.find((x) => x.id === quantity)!;
106+ const nf = result.frames.t.length;
107+ const player = useFramePlayer(nf, 30, false);
108+ const startFrame = useMemo(() => peakFrame(result.frames.w), [result]);
109+ const setIndex = player.setIndex;
110+ useEffect(() => setIndex(startFrame), [startFrame, setIndex]);
111+ useEffect(() => setMode(firstAudible(result)), [result]);
112+
113+ const sceneRef = useRef<Plate3DHandle>(null);
114+ const W = spec.plate.width;
115+ const H = spec.plate.height;
116+ const size = Math.max(W, H);
117+
118+ // mode shapes, each scaled to unit peak, for the picture and the scene
119+ const shapes: FieldFrames = useMemo(() => normalizeShapes(result.modes.shapes), [result]);
120+ const isMode = quantity === "mode";
121+ const frameIndex = isMode ? mode : player.index;
122+ const frames: FieldFrames | undefined =
123+ quantity === "w"
124+ ? result.frames.w
125+ : quantity === "w_total"
126+ ? result.frames.w_total
127+ : quantity === "vm"
128+ ? result.frames.vm
129+ : quantity === "sp"
130+ ? result.frames.sp
131+ : isMode
132+ ? shapes
133+ : undefined;
134+ const field: Field2D | undefined =
135+ quantity === "env_vm"
136+ ? result.envelope.vm
137+ : quantity === "env_sp"
138+ ? result.envelope.sp
139+ : undefined;
140+ const tFrame = result.frames.t[Math.min(player.index, nf - 1)] ?? 0;
141+
142+ // the scene is displaced by the displacement field (or the shape) and coloured by the quantity
143+ const deformFrames = isMode
144+ ? shapes
145+ : quantity === "w_total"
146+ ? result.frames.w_total
147+ : result.frames.w;
148+ const sceneScale = isMode ? 0.08 * size : exaggeration;
149+ const colorRange: Range | undefined = useMemo(() => {
150+ if (!frames) return undefined;
151+ if (isMode) return { min: -1, max: 1 };
152+ const r = rangeOf(frames.data);
153+ return q.symmetric ? symmetricRange(r) : r;
154+ }, [frames, q.symmetric, isMode]);
155+
156+ const hole = holePosition(spec);
157+ const markers: FieldMarker[] = useMemo(
158+ () => [
159+ { x: spec.strike.x + W / 2, y: spec.strike.y + H / 2, label: "strike", color: "#d1495b" },
160+ { x: result.probe.x + W / 2, y: result.probe.y + H / 2, label: "probe", color: "#1f5fa8" },
161+ { x: hole.x + W / 2, y: hole.y + H / 2, label: "hole", color: "#2a9d8f" },
162+ ],
163+ [spec, result.probe.x, result.probe.y, hole.x, hole.y, W, H],
164+ );
165+
166+ const onPlotClick = (t: number) => {
167+ let best = 0;
168+ for (let k = 1; k < nf; k++)
169+ if (Math.abs(result.frames.t[k] - t) < Math.abs(result.frames.t[best] - t)) best = k;
170+ player.setIndex(best);
171+ player.setPlaying(false);
172+ };
173+
174+ const axis = useSharedAxis();
175+ const spectrumAxis = useSharedAxis();
176+ const audioT = useMemo(
177+ () => Float64Array.from({ length: result.audio.p.length }, (_, i) => i / result.audio.fs),
178+ [result.audio],
179+ );
180+ const audioPrep = useMemo(
181+ () => prepareAudio(result.audio.p, result.audio.fs, 10 ** (result.audio.normalize_dbfs / 20)),
182+ [result.audio],
183+ );
184+ const movieSource = useMemo(
185+ () => ({
186+ canvas: () => sceneRef.current?.canvas() ?? null,
187+ drawFrame: (k: number, size?: { width: number; height: number }) =>
188+ sceneRef.current?.drawFrame(k, size),
189+ }),
190+ [],
191+ );
192+ const movieOverlay: MovieOverlay = useMemo(
193+ () => ({
194+ title: `${describePlate(spec)}, ${method.shortName}`,
195+ note: isMode
196+ ? `mode ${mode + 1}, ${result.modes.freq[mode].toFixed(0)} Hz`
197+ : `displacement ×${exaggeration}`,
198+ legend:
199+ frames && colorRange && !isMode
200+ ? {
201+ label: q.label,
202+ unit: q.unit,
203+ min: colorRange.min,
204+ max: colorRange.max,
205+ scale: q.scale,
206+ colormap: q.symmetric ? "bwr" : "viridis",
207+ }
208+ : undefined,
209+ }),
210+ [spec, method.shortName, exaggeration, frames, colorRange, q, isMode, mode, result.modes.freq],
211+ );
212+ const modeLines = useMemo(
213+ () =>
214+ Array.from(result.modes.freq)
215+ .filter((_, k) => result.modes.audible[k])
216+ .slice(0, 40),
217+ [result.modes],
218+ );
219+
220+ return (
221+ <div className="results">
222+ <div className="results-toolbar sound-bar">
223+ <AudioPlayer
224+ p={result.audio.p}
225+ fs={result.audio.fs}
226+ normalizeDbfs={result.audio.normalize_dbfs}
227+ label="sound"
228+ filename={`plate-${method.id}`}
229+ />
230+ </div>
231+
232+ <div className="pl-scene">
233+ <div className="pl-plot-title">The plate on its thread</div>
234+ <Plate3DView
235+ ref={sceneRef}
236+ result={result}
237+ spec={spec}
238+ frameIndex={frameIndex}
239+ frames={deformFrames}
240+ colorFrames={frames}
241+ colorRange={colorRange}
242+ colormap={q.symmetric ? "bwr" : "viridis"}
243+ scale={sceneScale}
244+ width={fieldWidth}
245+ height={Math.round(fieldWidth * 0.7)}
246+ />
247+ <div className="pl-scene-controls">
248+ <label>
249+ displacement ×{exaggeration}
250+ <input
251+ type="range"
252+ min={0}
253+ max={5}
254+ step={0.05}
255+ value={Math.log10(Math.max(1, exaggeration))}
256+ onChange={(e) => setExaggeration(Math.round(10 ** Number(e.target.value)))}
257+ disabled={isMode}
258+ />
259+ </label>
260+ <span className="pl-muted">drag to orbit, wheel to zoom</span>
261+ </div>
262+ <MovieExport
263+ source={movieSource}
264+ frameTimes={isMode ? new Float64Array(0) : result.frames.t}
265+ audio={audioPrep}
266+ overlay={movieOverlay}
267+ aspect={0.7}
268+ filename={`plate-${method.id}`}
269+ restoreFrame={frameIndex}
270+ onRecording={(on) => on && player.setPlaying(false)}
271+ />
272+ <div className="pl-small pl-muted">
273+ The hammer's tip sits at the strike point on the near face; the thread rises from the
274+ hole. The surface is displaced by{" "}
275+ {isMode ? "the chosen mode shape" : "the displacement, exaggerated by the slider"}, and
276+ coloured by the quantity chosen below.
277+ </div>
278+ </div>
279+
280+ {frames && !isMode && (
281+ <FramePlayer {...player} times={result.frames.t} timeScale={1e6} timeUnit="µs" />
282+ )}
283+
284+ <div className="results-toolbar">
285+ <label className="pl-small pl-muted">show</label>
286+ <select
287+ className="pl-select"
288+ style={{ width: "auto" }}
289+ value={quantity}
290+ onChange={(e) => setQuantity(e.target.value as Quantity)}
291+ >
292+ {QUANTITIES.map((x) => (
293+ <option key={x.id} value={x.id}>
294+ {x.label}
295+ </option>
296+ ))}
297+ </select>
298+ {isMode && (
299+ <label className="pl-small pl-muted">
300+ mode{" "}
301+ <select
302+ className="pl-select"
303+ style={{ width: "auto" }}
304+ value={mode}
305+ onChange={(e) => setMode(Number(e.target.value))}
306+ >
307+ {Array.from(result.modes.freq).map((f, k) => (
308+ <option key={k} value={k}>
309+ {k + 1}: {f < 20 ? `${f.toFixed(2)} Hz (rigid)` : `${f.toFixed(1)} Hz`}
310+ </option>
311+ ))}
312+ </select>
313+ </label>
314+ )}
315+ {frames && !isMode && (
316+ <label className="pl-small pl-muted">
317+ colour range{" "}
318+ <select
319+ className="pl-select"
320+ style={{ width: "auto" }}
321+ value={rangeMode}
322+ onChange={(e) => setRangeMode(e.target.value as "global" | "frame")}
323+ >
324+ <option value="global">fixed over all frames</option>
325+ <option value="frame">rescaled each frame</option>
326+ </select>
327+ </label>
328+ )}
329+ </div>
330+ <FieldView
331+ frames={frames}
332+ field={field}
333+ frameIndex={frameIndex}
334+ Lx={W}
335+ Ly={H}
336+ title={isMode ? `Mode ${mode + 1}, ${result.modes.freq[mode].toFixed(1)} Hz` : q.label}
337+ unit={q.unit}
338+ scale={q.scale}
339+ symmetric={q.symmetric}
340+ range={isMode ? "frame" : rangeMode}
341+ markers={markers}
342+ width={fieldWidth}
343+ caption={captionFor(quantity, result)}
344+ />
345+
346+ <ModeTable
347+ result={result}
348+ selected={isMode ? mode : null}
349+ onSelect={(k) => {
350+ setMode(k);
351+ setQuantity("mode");
352+ }}
353+ />
354+
355+ <ContactPlots result={result} spec={spec} />
356+
357+ <LinePlot
358+ title={`Displacement at the probe (${(result.probe.x * 1000).toFixed(1)}, ${(result.probe.y * 1000).toFixed(1)}) mm`}
359+ series={[{ x: result.t, y: result.probe.w, label: "w" }]}
360+ yScale={1e6}
361+ defaultXRange={[0, Math.min(result.t[result.t.length - 1], 0.02)]}
362+ xLabel="time [s]"
363+ yLabel="w [µm]"
364+ yUnit="µm"
365+ cursorX={tFrame}
366+ onClick={onPlotClick}
367+ height={170}
368+ {...axis}
369+ />
370+ <LinePlot
371+ title="Surface stress at the probe"
372+ series={[
373+ { x: result.t, y: result.probe.vm, label: "von Mises" },
374+ { x: result.t, y: result.probe.sp, label: "principal (signed)", color: "#d1495b" },
375+ ]}
376+ yScale={1e-6}
377+ defaultXRange={[0, Math.min(result.t[result.t.length - 1], 0.02)]}
378+ xLabel="time [s]"
379+ yLabel="σ [MPa]"
380+ yUnit="MPa"
381+ cursorX={tFrame}
382+ onClick={onPlotClick}
383+ height={170}
384+ {...axis}
385+ />
386+ <div className="pl-small pl-muted">
387+ The first 20 ms are shown; drag to zoom, double-click to reset. The whole{" "}
388+ {result.t[result.t.length - 1].toFixed(1)} s record is there.
389+ </div>
390+
391+ <LinePlot
392+ title="Amplitude spectrum of the sound (Hann window)"
393+ series={[
394+ { x: result.spectrum.f, y: result.spectrum.p, label: "pressure [Pa]" },
395+ ...modeLines.map((f) => ({
396+ x: Float64Array.of(f, f),
397+ y: Float64Array.of(1e-12, 1),
398+ color: "rgba(42,157,143,0.45)",
399+ width: 1,
400+ dash: [3, 3],
401+ })),
402+ ]}
403+ defaultXRange={[0, result.info.f_max]}
404+ logY
405+ xLabel="frequency [Hz]"
406+ yLabel="amplitude"
407+ height={190}
408+ {...spectrumAxis}
409+ />
410+ <div className="pl-small pl-muted">
411+ Dashed lines mark the audible modes' frequencies. Resolution{" "}
412+ {(1 / (result.t[result.t.length - 1] - result.t[0])).toFixed(2)} Hz from a{" "}
413+ {(result.t[result.t.length - 1] - result.t[0]).toFixed(2)} s record; modes the strike does
414+ not reach show no peak.
415+ </div>
416+ <LinePlot
417+ title="Amplitude spectrum of the probe displacement"
418+ series={[{ x: result.spectrum.f, y: result.spectrum.w, label: "w [m]" }]}
419+ defaultXRange={[0, result.info.f_max]}
420+ logY
421+ xLabel="frequency [Hz]"
422+ yLabel="amplitude"
423+ height={150}
424+ {...spectrumAxis}
425+ />
426+
427+ <div>
428+ <div className="pl-plot-title">Pressure at the microphone</div>
429+ <LinePlot
430+ series={[{ x: audioT, y: result.audio.p, label: "pressure" }]}
431+ defaultXRange={[0, Math.min(audioT[audioT.length - 1], 0.005)]}
432+ xLabel="time [s]"
433+ yLabel="p [Pa]"
434+ yUnit="Pa"
435+ height={120}
436+ {...axis}
437+ />
438+ <div className="pl-small pl-muted">
439+ Compact-source estimate {spec.mic.distance * 1000} mm from the plate on its normal, with
440+ the front/back cancellation of an unbaffled plate below{" "}
441+ {(result.info.critical_frequency / 1000).toFixed(1)} kHz. The first 5 ms are shown. Peak{" "}
442+ {result.audio.peak_pa.toExponential(2)} Pa before normalization.
443+ </div>
444+ </div>
445+
446+ <details>
447+ <summary className="pl-small">What this result idealizes</summary>
448+ <ValidityNotes method={method} result={result} />
449+ </details>
450+ </div>
451+ );
452+}
453+
454+/** The stock, for the movie's title: "30 × 30 × 1 mm, 6061-T6 aluminium". */
455+function describePlate(spec: ProblemSpec): string {
456+ const p = spec.plate;
457+ const mm = (x: number) => Number((x * 1000).toPrecision(3)).toString();
458+ return `${mm(p.width)} × ${mm(p.height)} × ${mm(p.thickness)} mm, ${spec.material.name}`;
459+}
460+
461+function captionFor(q: Quantity, result: PlateResult): string {
462+ switch (q) {
463+ case "w":
464+ return "The sum of the audible modes: what radiates. Positive along the plate normal, towards the hammer.";
465+ case "w_total":
466+ return result.info.n_rigid > 0
467+ ? "With the three rigid-body modes the finite-element method keeps: the recoil and the rotation the hammer leaves the whole plate with."
468+ : "This method has no rigid-body modes, so this is the same field as the audible one.";
469+ case "vm":
470+ case "sp":
471+ return "Surface stress at z = h/2 from the modal curvatures; the opposite face carries the opposite sign.";
472+ case "env_vm":
473+ case "env_sp":
474+ return "Peak over the saved frames, which cover the strike transient; a sample of the true peak.";
475+ case "mode":
476+ return "The mass-normalized shape scaled to unit peak; the table below gives its part in this strike.";
477+ }
478+}
479+
480+function firstAudible(result: PlateResult): number {
481+ const k = result.modes.audible.findIndex((a) => a);
482+ return k < 0 ? 0 : k;
483+}
484+
485+/** Each shape divided by its own peak, so that all are drawn at the same amplitude. */
486+function normalizeShapes(shapes: FieldFrames): FieldFrames {
487+ const n = shapes.nx * shapes.ny;
488+ const data = new Float64Array(shapes.data.length);
489+ for (let k = 0; k < shapes.nf; k++) {
490+ let peak = 0;
491+ for (let i = k * n; i < (k + 1) * n; i++) peak = Math.max(peak, Math.abs(shapes.data[i]));
492+ const g = peak > 0 ? 1 / peak : 1;
493+ for (let i = k * n; i < (k + 1) * n; i++) data[i] = shapes.data[i] * g;
494+ }
495+ return { ...shapes, data };
496+}
497+
498+/**
499+ * The frame of largest displacement. The player starts here so a run that
500+ * has just finished shows the plate in motion.
501+ */
502+function peakFrame(f: FieldFrames | undefined): number {
503+ if (!f) return 0;
504+ const n = f.nx * f.ny;
505+ let best = 0;
506+ let bestv = -1;
507+ for (let k = 0; k < f.nf; k++) {
508+ let v = 0;
509+ for (let i = k * n; i < (k + 1) * n; i++) {
510+ const a = Math.abs(f.data[i]);
511+ if (a > v) v = a;
512+ }
513+ if (v > bestv) {
514+ bestv = v;
515+ best = k;
516+ }
517+ }
518+ return best;
519+}
apps/lab/tsconfig.jsonadded+9−0View file
@@ -0,0 +1,9 @@
1+{
2+ "extends": "../../tsconfig.base.json",
3+ "compilerOptions": {
4+ "noEmit": true
5+ },
6+ "include": [
7+ "src"
8+ ]
9+}
apps/lab/vite.config.tsadded+13−0View file
@@ -0,0 +1,13 @@
1+import { defineConfig } from "vite";
2+import react from "@vitejs/plugin-react";
3+import { numblDefine } from "../../vite.shared";
4+
5+// The vault serves a site either at the root of an origin of its own (when the
6+// repository has a site label) or under /<collection>/<repo>/site/, so the base
7+// path is not knowable here. CI reads it from configure-pages and passes it in.
8+export default defineConfig({
9+ base: process.env.DEPLOY_BASE ?? "/",
10+ plugins: [react()],
11+ define: numblDefine(),
12+ worker: { format: "es" },
13+});
package-lock.jsonadded+4236−0View file
This diff is 4,241 lines long and is not shown.
package.jsonadded+29−0View file
@@ -0,0 +1,29 @@
1+{
2+ "name": "plate-dynamics-lab-2",
3+ "private": true,
4+ "version": "0.1.0",
5+ "description": "Plate dynamics lab 2: a hammer striking a thread-suspended metal plate, by Rayleigh-Ritz and by Mindlin-Reissner finite elements, both modal, driven by numbl-executed MATLAB source in the browser",
6+ "type": "module",
7+ "workspaces": [
8+ "packages/*",
9+ "apps/*"
10+ ],
11+ "scripts": {
12+ "dev": "npm run dev -w apps/lab",
13+ "build": "npm run build -w packages/core -w packages/components -w apps/lab --if-present",
14+ "typecheck": "tsc -p tsconfig.json",
15+ "test": "vitest run",
16+ "format": "prettier --write ."
17+ },
18+ "devDependencies": {
19+ "@types/node": "^24.10.1",
20+ "@types/react": "^19.2.5",
21+ "@types/react-dom": "^19.2.3",
22+ "@vitejs/plugin-react": "^5.1.1",
23+ "@webgpu/types": "^0.1.72",
24+ "prettier": "^3.8.1",
25+ "typescript": "~5.9.3",
26+ "vite": "^7.2.4",
27+ "vitest": "^4.0.18"
28+ }
29+}
packages/components/package.jsonadded+30−0View file
@@ -0,0 +1,30 @@
1+{
2+ "name": "@plate-dyn/components",
3+ "version": "0.1.0",
4+ "private": true,
5+ "type": "module",
6+ "description": "Composable React components over @plate-dyn/core: code view, parameter panel, field and 3D views, plots, equations, run status, audio, movie export.",
7+ "exports": {
8+ ".": "./src/index.ts",
9+ "./styles.css": "./src/styles.css"
10+ },
11+ "scripts": {
12+ "typecheck": "tsc -p tsconfig.json --noEmit"
13+ },
14+ "dependencies": {
15+ "@plate-dyn/core": "*",
16+ "@codemirror/language": "^6.11.0",
17+ "@codemirror/legacy-modes": "^6.5.0",
18+ "codemirror": "^6.0.1",
19+ "katex": "^0.16.22",
20+ "@codemirror/state": "^6.5.2",
21+ "@codemirror/view": "^6.38.1"
22+ },
23+ "peerDependencies": {
24+ "react": ">=18",
25+ "react-dom": ">=18"
26+ },
27+ "devDependencies": {
28+ "@types/katex": "^0.16.7"
29+ }
30+}
packages/components/src/audio/AudioPlayer.tsxadded+86−0View file
@@ -0,0 +1,86 @@
1+import { useEffect, useMemo, useRef, useState } from "react";
2+import { encodeWav, prepareAudio, toAudioBuffer } from "@plate-dyn/core";
3+
4+export interface AudioPlayerProps {
5+ /** Pressure signal [Pa] */
6+ p: Float64Array;
7+ fs: number;
8+ /** Peak level of the export and the playback [dBFS]; default -1 */
9+ normalizeDbfs?: number;
10+ label?: string;
11+ /** Base name for the exported .wav file (no extension) */
12+ filename?: string;
13+}
14+
15+/**
16+ * Plays the radiated-pressure estimate once, exactly as long as it is, and
17+ * offers the same clip as a 24-bit mono WAV download, peak-normalized as the
18+ * reference simulators write theirs. The displayed peak in pascals is not
19+ * normalized.
20+ */
21+export function AudioPlayer({
22+ p,
23+ fs,
24+ normalizeDbfs = -1,
25+ label,
26+ filename = "plate-sound",
27+}: AudioPlayerProps) {
28+ const prep = useMemo(
29+ () => prepareAudio(p, fs, 10 ** (normalizeDbfs / 20)),
30+ [p, fs, normalizeDbfs],
31+ );
32+ const ctxRef = useRef<AudioContext | null>(null);
33+ const srcRef = useRef<AudioBufferSourceNode | null>(null);
34+ const [playing, setPlaying] = useState(false);
35+ useEffect(() => () => stop(), []);
36+
37+ const stop = () => {
38+ srcRef.current?.stop();
39+ srcRef.current = null;
40+ setPlaying(false);
41+ };
42+ const play = () => {
43+ if (!ctxRef.current) ctxRef.current = new AudioContext();
44+ const ctx = ctxRef.current;
45+ void ctx.resume();
46+ stop();
47+ const src = ctx.createBufferSource();
48+ src.buffer = toAudioBuffer(ctx, prep);
49+ src.connect(ctx.destination);
50+ src.start();
51+ src.onended = () => setPlaying(false);
52+ srcRef.current = src;
53+ setPlaying(true);
54+ };
55+ const download = () => {
56+ const blob = new Blob([encodeWav(prep.samples, Math.round(prep.fs), 24) as BlobPart], {
57+ type: "audio/wav",
58+ });
59+ const url = URL.createObjectURL(blob);
60+ const a = document.createElement("a");
61+ a.href = url;
62+ a.download = `${filename}.wav`;
63+ a.click();
64+ setTimeout(() => URL.revokeObjectURL(url), 0);
65+ };
66+
67+ return (
68+ <div className="pl-row pl-small">
69+ <button className="pl-btn" onClick={playing ? stop : play} disabled={p.length < 2}>
70+ {playing ? "Stop" : `Play${label ? ` ${label}` : ""}`}
71+ </button>
72+ <button
73+ className="pl-btn"
74+ onClick={download}
75+ disabled={p.length < 2}
76+ title="Download as a 24-bit mono WAV file at the simulation's sample rate"
77+ >
78+ Save .wav
79+ </button>
80+ <span className="pl-muted">
81+ {prep.durationS.toFixed(2)} s at {fs} Hz, peak {prep.peakPa.toExponential(2)} Pa; normalized
82+ to {normalizeDbfs} dBFS for playback and export
83+ </span>
84+ </div>
85+ );
86+}
packages/components/src/code/CodeView.tsxadded+162−0View file
@@ -0,0 +1,162 @@
1+import { useEffect, useMemo, useRef, useState } from "react";
2+import { EditorView, basicSetup } from "codemirror";
3+import { EditorState, Compartment } from "@codemirror/state";
4+import { StreamLanguage } from "@codemirror/language";
5+import { octave } from "@codemirror/legacy-modes/mode/octave";
6+import type { SourceFile } from "@plate-dyn/core";
7+
8+export interface CodeViewProps {
9+ /** The generated sources */
10+ files: SourceFile[];
11+ /** User edits by file name; a file absent here shows its generated text */
12+ edits: Record<string, string>;
13+ onEditsChange: (edits: Record<string, string>) => void;
14+ /** Tab order; files not listed follow in their original order */
15+ order?: string[];
16+ /** One-line descriptions shown under the tabs */
17+ summaries?: Record<string, string>;
18+ readOnly?: boolean;
19+ /** Called with the active file name */
20+ onActiveChange?: (name: string) => void;
21+ initialFile?: string;
22+ height?: number | string;
23+}
24+
25+/**
26+ * Tabbed source view of exactly the `.m` files a run executes. Editing a file
27+ * records an edit; the run then uses the edited text, and a modified tab shows
28+ * an asterisk with a way back to the generated version.
29+ */
30+export function CodeView(props: CodeViewProps) {
31+ const {
32+ files,
33+ edits,
34+ onEditsChange,
35+ order,
36+ summaries,
37+ readOnly,
38+ onActiveChange,
39+ initialFile,
40+ height,
41+ } = props;
42+ const ordered = useMemo(() => {
43+ const byName = new Map(files.map((f) => [f.name, f]));
44+ const out: SourceFile[] = [];
45+ for (const n of order ?? []) if (byName.has(n)) out.push(byName.get(n)!);
46+ for (const f of files) if (!out.includes(f)) out.push(f);
47+ return out;
48+ }, [files, order]);
49+
50+ const [active, setActive] = useState(initialFile ?? ordered[0]?.name ?? "");
51+ useEffect(() => {
52+ if (!ordered.some((f) => f.name === active) && ordered[0]) setActive(ordered[0].name);
53+ }, [ordered, active]);
54+ useEffect(() => onActiveChange?.(active), [active, onActiveChange]);
55+
56+ const hostRef = useRef<HTMLDivElement>(null);
57+ const viewRef = useRef<EditorView | null>(null);
58+ const editsRef = useRef(edits);
59+ editsRef.current = edits;
60+ const filesRef = useRef(files);
61+ filesRef.current = files;
62+ const activeRef = useRef(active);
63+ activeRef.current = active;
64+ const onEditsRef = useRef(onEditsChange);
65+ onEditsRef.current = onEditsChange;
66+ const readOnlyCompartment = useMemo(() => new Compartment(), []);
67+
68+ // One editor for the lifetime of the component
69+ useEffect(() => {
70+ if (!hostRef.current) return;
71+ const view = new EditorView({
72+ parent: hostRef.current,
73+ state: EditorState.create({
74+ doc: "",
75+ extensions: [
76+ basicSetup,
77+ StreamLanguage.define(octave),
78+ readOnlyCompartment.of(EditorState.readOnly.of(!!readOnly)),
79+ EditorView.updateListener.of((u) => {
80+ if (!u.docChanged) return;
81+ const name = activeRef.current;
82+ const text = u.state.doc.toString();
83+ const original = filesRef.current.find((f) => f.name === name)?.source;
84+ const next = { ...editsRef.current };
85+ if (text === original) delete next[name];
86+ else next[name] = text;
87+ onEditsRef.current(next);
88+ }),
89+ ],
90+ }),
91+ });
92+ viewRef.current = view;
93+ return () => {
94+ view.destroy();
95+ viewRef.current = null;
96+ };
97+ // eslint-disable-next-line react-hooks/exhaustive-deps
98+ }, []);
99+
100+ useEffect(() => {
101+ viewRef.current?.dispatch({
102+ effects: readOnlyCompartment.reconfigure(EditorState.readOnly.of(!!readOnly)),
103+ });
104+ }, [readOnly, readOnlyCompartment]);
105+
106+ // Load the active file's text (edited or generated) into the editor whenever it changes externally
107+ const shown = edits[active] ?? files.find((f) => f.name === active)?.source ?? "";
108+ useEffect(() => {
109+ const view = viewRef.current;
110+ if (!view) return;
111+ const current = view.state.doc.toString();
112+ if (current !== shown) {
113+ view.dispatch({ changes: { from: 0, to: current.length, insert: shown } });
114+ }
115+ }, [shown, active]);
116+
117+ const modified = active in edits;
118+ const restore = () => {
119+ const next = { ...edits };
120+ delete next[active];
121+ onEditsChange(next);
122+ };
123+ const restoreAll = () => onEditsChange({});
124+ const nModified = Object.keys(edits).length;
125+
126+ return (
127+ <div className="pl-code" style={{ height }}>
128+ <div className="pl-code-tabs" role="tablist">
129+ {ordered.map((f) => (
130+ <button
131+ key={f.name}
132+ role="tab"
133+ aria-selected={f.name === active}
134+ className={`pl-code-tab${f.name === active ? " pl-active" : ""}${f.name in edits ? " pl-modified" : ""}`}
135+ onClick={() => setActive(f.name)}
136+ title={summaries?.[f.name]}
137+ >
138+ {f.name}
139+ </button>
140+ ))}
141+ </div>
142+ <div className="pl-code-toolbar">
143+ <span>{summaries?.[active] ?? ""}</span>
144+ <span style={{ flex: 1 }} />
145+ {modified && (
146+ <>
147+ <span className="pl-badge pl-badge-warn">edited</span>
148+ <button className="pl-btn" onClick={restore}>
149+ Restore this file
150+ </button>
151+ </>
152+ )}
153+ {nModified > 1 && (
154+ <button className="pl-btn" onClick={restoreAll}>
155+ Restore all ({nModified})
156+ </button>
157+ )}
158+ </div>
159+ <div className="pl-code-editor" ref={hostRef} />
160+ </div>
161+ );
162+}
packages/components/src/field/Colorbar.tsxadded+53−0View file
@@ -0,0 +1,53 @@
1+import { useEffect, useRef } from "react";
2+import { colormapTable, getColormap, type Range } from "@plate-dyn/core";
3+
4+export function Colorbar({
5+ range,
6+ colormap,
7+ unit,
8+ scale = 1,
9+ height = 200,
10+}: {
11+ range: Range;
12+ colormap: string;
13+ unit?: string;
14+ scale?: number;
15+ height?: number;
16+}) {
17+ const ref = useRef<HTMLCanvasElement>(null);
18+ useEffect(() => {
19+ const c = ref.current;
20+ if (!c) return;
21+ const ctx = c.getContext("2d");
22+ if (!ctx) return;
23+ const table = colormapTable(getColormap(colormap));
24+ const h = 256;
25+ c.width = 1;
26+ c.height = h;
27+ const img = ctx.createImageData(1, h);
28+ for (let k = 0; k < h; k++) {
29+ const t = 255 - k;
30+ img.data[4 * k] = table[3 * t];
31+ img.data[4 * k + 1] = table[3 * t + 1];
32+ img.data[4 * k + 2] = table[3 * t + 2];
33+ img.data[4 * k + 3] = 255;
34+ }
35+ ctx.putImageData(img, 0, 0);
36+ }, [colormap]);
37+ return (
38+ <div className="pl-colorbar" style={{ height }}>
39+ <span>{fmt(range.max * scale)}</span>
40+ <canvas ref={ref} />
41+ <span>
42+ {fmt(range.min * scale)} {unit}
43+ </span>
44+ </div>
45+ );
46+}
47+
48+export function fmt(x: number): string {
49+ if (x === 0) return "0";
50+ const a = Math.abs(x);
51+ if (a >= 1e4 || a < 1e-2) return x.toExponential(2);
52+ return x.toPrecision(3);
53+}
packages/components/src/field/FieldView.tsxadded+259−0View file
@@ -0,0 +1,259 @@
1+import { useEffect, useMemo, useRef } from "react";
2+import {
3+ frameOf,
4+ getColormap,
5+ rangeOf,
6+ rasterizeField,
7+ symmetricRange,
8+ type Field2D,
9+ type FieldFrames,
10+ type Range,
11+} from "@plate-dyn/core";
12+import { Colorbar, fmt } from "./Colorbar";
13+
14+export interface FieldMarker {
15+ x: number;
16+ y: number;
17+ label: string;
18+ color?: string;
19+}
20+
21+/** A rectangle drawn on the plate, in plate coordinates [m] */
22+export interface FieldRect {
23+ x0: number;
24+ y0: number;
25+ x1: number;
26+ y1: number;
27+ label?: string;
28+ color?: string;
29+}
30+
31+/** An arrow drawn from a point, in plate coordinates, with a direction in the plate's plane */
32+export interface FieldArrow {
33+ x: number;
34+ y: number;
35+ dx: number;
36+ dy: number;
37+ label?: string;
38+ color?: string;
39+}
40+
41+export interface FieldViewProps {
42+ /** Either a single field or a frame stack with an index */
43+ field?: Field2D;
44+ frames?: FieldFrames;
45+ frameIndex?: number;
46+ /** Physical extents of the bounding box [m] */
47+ Lx: number;
48+ Ly: number;
49+ title?: string;
50+ unit?: string;
51+ /** Display multiplier applied to values in labels (e.g. 1e-6 for MPa) */
52+ scale?: number;
53+ colormap?: string;
54+ /** "frame": rescale per frame; "global": one range over all frames; or a fixed range */
55+ range?: "frame" | "global" | Range;
56+ /** Force a symmetric range about zero (for signed fields) */
57+ symmetric?: boolean;
58+ markers?: FieldMarker[];
59+ rects?: FieldRect[];
60+ arrow?: FieldArrow;
61+ /** Canvas width in CSS pixels; height follows the aspect ratio */
62+ width?: number;
63+ caption?: string;
64+}
65+
66+/**
67+ * Paints a field on the plate outline. Values pass straight from the solver
68+ * through a colormap; nothing here changes them. Non-finite nodes come out
69+ * transparent. Coordinates run from 0 to Lx and 0 to Ly across the box.
70+ */
71+export function FieldView(props: FieldViewProps) {
72+ const {
73+ frames,
74+ frameIndex = 0,
75+ Lx,
76+ Ly,
77+ title,
78+ unit,
79+ scale = 1,
80+ colormap,
81+ symmetric,
82+ markers = [],
83+ rects = [],
84+ arrow,
85+ width = 420,
86+ caption,
87+ } = props;
88+ const field: Field2D | undefined =
89+ props.field ?? (frames ? frameOf(frames, Math.min(frameIndex, frames.nf - 1)) : undefined);
90+ const cmId = colormap ?? (symmetric ? "bwr" : "viridis");
91+ const ellipse = false;
92+
93+ const globalRange = useMemo(() => {
94+ const r = frames ? rangeOf(frames.data) : field ? rangeOf(field.data) : { min: 0, max: 1 };
95+ return symmetric ? symmetricRange(r) : r;
96+ }, [frames, field, symmetric]);
97+
98+ const range: Range = useMemo(() => {
99+ const mode = props.range ?? "global";
100+ if (typeof mode === "object") return mode;
101+ if (mode === "global" || !field) return globalRange;
102+ const r = rangeOf(field.data);
103+ return symmetric ? symmetricRange(r) : r;
104+ }, [props.range, field, globalRange, symmetric]);
105+
106+ const canvasRef = useRef<HTMLCanvasElement>(null);
107+ const offRef = useRef<HTMLCanvasElement | null>(null);
108+ const pixRef = useRef<Uint8ClampedArray | undefined>(undefined);
109+
110+ const height = Math.max(60, Math.round((width * Ly) / Lx));
111+
112+ useEffect(() => {
113+ const canvas = canvasRef.current;
114+ if (!canvas || !field) return;
115+ const ctx = canvas.getContext("2d");
116+ if (!ctx) return;
117+ const dpr = Math.min(2, window.devicePixelRatio || 1);
118+ canvas.width = Math.round(width * dpr);
119+ canvas.height = Math.round(height * dpr);
120+ // Rasterize at grid resolution, then scale with smoothing onto the plate outline
121+ if (!offRef.current) offRef.current = document.createElement("canvas");
122+ const off = offRef.current;
123+ off.width = field.nx;
124+ off.height = field.ny;
125+ const pix = rasterizeField(field, range, getColormap(cmId), pixRef.current);
126+ pixRef.current = pix;
127+ const octx = off.getContext("2d");
128+ if (!octx) return;
129+ octx.putImageData(
130+ new ImageData(pix as Uint8ClampedArray<ArrayBuffer>, field.nx, field.ny),
131+ 0,
132+ 0,
133+ );
134+ ctx.save();
135+ ctx.scale(dpr, dpr);
136+ ctx.clearRect(0, 0, width, height);
137+ ctx.imageSmoothingEnabled = true;
138+ const outline = () => {
139+ ctx.beginPath();
140+ ctx.ellipse(width / 2, height / 2, width / 2 - 0.75, height / 2 - 0.75, 0, 0, 2 * Math.PI);
141+ };
142+ if (ellipse) {
143+ ctx.save();
144+ outline();
145+ ctx.clip();
146+ }
147+ // Grid nodes sit at cell centres of the image; map node (0..nx-1) to the full extent
148+ const sx = width / (field.nx - 1);
149+ const sy = height / (field.ny - 1);
150+ ctx.drawImage(off, -sx / 2, -sy / 2, width + sx, height + sy);
151+ if (ellipse) ctx.restore();
152+ ctx.strokeStyle = "#333";
153+ ctx.lineWidth = 1;
154+ if (ellipse) {
155+ outline();
156+ ctx.stroke();
157+ } else {
158+ ctx.strokeRect(0.5, 0.5, width - 1, height - 1);
159+ }
160+ const toPx = (x: number, y: number): [number, number] => [
161+ (x / Lx) * width,
162+ height - (y / Ly) * height,
163+ ];
164+ for (const r of rects) {
165+ const [x0, y0] = toPx(r.x0, r.y1);
166+ const [x1, y1] = toPx(r.x1, r.y0);
167+ ctx.fillStyle = "rgba(60,60,70,0.18)";
168+ ctx.fillRect(x0, y0, x1 - x0, y1 - y0);
169+ ctx.strokeStyle = r.color ?? "#333";
170+ ctx.lineWidth = 1;
171+ ctx.strokeRect(x0, y0, x1 - x0, y1 - y0);
172+ if (r.label) {
173+ ctx.font = "11px system-ui, sans-serif";
174+ ctx.fillStyle = "#000";
175+ ctx.strokeStyle = "rgba(255,255,255,0.9)";
176+ ctx.lineWidth = 3;
177+ ctx.strokeText(r.label, x0 + 3, y1 - 4);
178+ ctx.fillText(r.label, x0 + 3, y1 - 4);
179+ }
180+ }
181+ if (arrow && (arrow.dx !== 0 || arrow.dy !== 0)) {
182+ const [x0, y0] = toPx(arrow.x, arrow.y);
183+ const len = 0.18 * Math.min(width, height);
184+ const n = Math.hypot(arrow.dx, arrow.dy);
185+ const ux = arrow.dx / n;
186+ const uy = -arrow.dy / n; // screen y is down
187+ const x1 = x0 + ux * len;
188+ const y1 = y0 + uy * len;
189+ ctx.strokeStyle = arrow.color ?? "#d1495b";
190+ ctx.fillStyle = arrow.color ?? "#d1495b";
191+ ctx.lineWidth = 2;
192+ ctx.beginPath();
193+ ctx.moveTo(x0, y0);
194+ ctx.lineTo(x1, y1);
195+ ctx.stroke();
196+ ctx.beginPath();
197+ ctx.moveTo(x1, y1);
198+ ctx.lineTo(x1 - ux * 8 - uy * 5, y1 - uy * 8 + ux * 5);
199+ ctx.lineTo(x1 - ux * 8 + uy * 5, y1 - uy * 8 - ux * 5);
200+ ctx.closePath();
201+ ctx.fill();
202+ if (arrow.label) {
203+ ctx.font = "11px system-ui, sans-serif";
204+ ctx.fillStyle = "#000";
205+ ctx.strokeStyle = "rgba(255,255,255,0.9)";
206+ ctx.lineWidth = 3;
207+ ctx.strokeText(arrow.label, x1 + 6, y1 + 4);
208+ ctx.fillText(arrow.label, x1 + 6, y1 + 4);
209+ }
210+ }
211+ for (const m of markers) {
212+ const [px, py] = toPx(m.x, m.y);
213+ ctx.beginPath();
214+ ctx.arc(px, py, 5, 0, 2 * Math.PI);
215+ ctx.fillStyle = "rgba(255,255,255,0.85)";
216+ ctx.fill();
217+ ctx.strokeStyle = m.color ?? "#000";
218+ ctx.lineWidth = 1.5;
219+ ctx.stroke();
220+ ctx.font = "11px system-ui, sans-serif";
221+ ctx.fillStyle = "#000";
222+ ctx.strokeStyle = "rgba(255,255,255,0.9)";
223+ ctx.lineWidth = 3;
224+ ctx.strokeText(m.label, px + 8, py - 6);
225+ ctx.fillText(m.label, px + 8, py - 6);
226+ }
227+ ctx.restore();
228+ }, [field, range, cmId, width, height, markers, rects, arrow, Lx, Ly, ellipse]);
229+
230+ const peak = field ? rangeOf(field.data) : null;
231+
232+ return (
233+ <div className="pl-fieldview">
234+ <div className="pl-fieldview-head">
235+ <strong>{title}</strong>
236+ {peak && (
237+ <span className="pl-muted pl-mono">
238+ {fmt(peak.min * scale)} to {fmt(peak.max * scale)} {unit}
239+ </span>
240+ )}
241+ </div>
242+ <div className="pl-fieldview-body">
243+ <canvas
244+ ref={canvasRef}
245+ style={{
246+ width,
247+ height,
248+ aspectRatio: `${Lx} / ${Ly}`,
249+ border: ellipse ? "none" : undefined,
250+ background: ellipse ? "transparent" : undefined,
251+ }}
252+ aria-label={title}
253+ />
254+ <Colorbar range={range} colormap={cmId} unit={unit} scale={scale} height={height} />
255+ </div>
256+ {caption && <div className="pl-small pl-muted">{caption}</div>}
257+ </div>
258+ );
259+}
packages/components/src/field/FramePlayer.tsxadded+76−0View file
@@ -0,0 +1,76 @@
1+import { useCallback, useEffect, useRef, useState } from "react";
2+
3+export interface FramePlayerState {
4+ index: number;
5+ setIndex: (i: number) => void;
6+ playing: boolean;
7+ setPlaying: (p: boolean) => void;
8+}
9+
10+/** Advance a frame index at a fixed rate while playing. */
11+export function useFramePlayer(nf: number, fps = 20, autoplay = false): FramePlayerState {
12+ const [index, setIndexState] = useState(0);
13+ const [playing, setPlaying] = useState(autoplay);
14+ const timer = useRef<number | null>(null);
15+ useEffect(() => {
16+ if (index >= nf) setIndexState(0);
17+ }, [nf, index]);
18+ useEffect(() => {
19+ if (!playing || nf <= 1) return;
20+ timer.current = window.setInterval(() => setIndexState((i) => (i + 1) % nf), 1000 / fps);
21+ return () => {
22+ if (timer.current !== null) window.clearInterval(timer.current);
23+ };
24+ }, [playing, nf, fps]);
25+ const setIndex = useCallback(
26+ (i: number) => setIndexState(Math.max(0, Math.min(nf - 1, i))),
27+ [nf],
28+ );
29+ return { index, setIndex, playing, setPlaying };
30+}
31+
32+export interface FramePlayerProps extends FramePlayerState {
33+ times: Float64Array;
34+ /** Show time in these units */
35+ timeScale?: number;
36+ timeUnit?: string;
37+}
38+
39+export function FramePlayer({
40+ index,
41+ setIndex,
42+ playing,
43+ setPlaying,
44+ times,
45+ timeScale = 1000,
46+ timeUnit = "ms",
47+}: FramePlayerProps) {
48+ const nf = times.length;
49+ const t = times[Math.min(index, nf - 1)] ?? 0;
50+ return (
51+ <div className="pl-player">
52+ <button
53+ className="pl-btn"
54+ onClick={() => setPlaying(!playing)}
55+ disabled={nf <= 1}
56+ aria-label={playing ? "pause" : "play"}
57+ >
58+ {playing ? "Pause" : "Play"}
59+ </button>
60+ <input
61+ type="range"
62+ min={0}
63+ max={Math.max(0, nf - 1)}
64+ value={Math.min(index, nf - 1)}
65+ onChange={(e) => setIndex(Number(e.target.value))}
66+ aria-label="frame"
67+ />
68+ <span className="pl-mono">
69+ {(t * timeScale).toFixed(2)} {timeUnit}
70+ </span>
71+ <span className="pl-muted pl-small">
72+ frame {Math.min(index, nf - 1) + 1}/{nf}
73+ </span>
74+ </div>
75+ );
76+}
packages/components/src/hooks/RunnerContext.tsxadded+28−0View file
@@ -0,0 +1,28 @@
1+import { createContext, useContext, useEffect, useRef, type ReactNode } from "react";
2+import { WorkerRunner, type Runner } from "@plate-dyn/core";
3+
4+const RunnerContext = createContext<Runner | null>(null);
5+
6+/**
7+ * Provides one numbl runner to a tree of components. By default a WorkerRunner
8+ * is created lazily; pass `runner` to share or substitute one.
9+ */
10+export function RunnerProvider({ children, runner }: { children: ReactNode; runner?: Runner }) {
11+ const ref = useRef<Runner | null>(null);
12+ if (!ref.current) ref.current = runner ?? new WorkerRunner();
13+ useEffect(() => {
14+ const r = ref.current;
15+ return () => {
16+ // Dispose terminates the worker; the runner recreates one on the next run,
17+ // so React StrictMode's mount-unmount-mount cycle is harmless.
18+ r?.dispose();
19+ };
20+ }, []);
21+ return <RunnerContext.Provider value={ref.current}>{children}</RunnerContext.Provider>;
22+}
23+
24+export function useRunner(): Runner {
25+ const r = useContext(RunnerContext);
26+ if (!r) throw new Error("useRunner must be used inside a RunnerProvider");
27+ return r;
28+}
packages/components/src/hooks/useSolve.tsadded+82−0View file
@@ -0,0 +1,82 @@
1+import { useCallback, useRef, useState } from "react";
2+import type { PlainValue, RunFailure, RunRequest, RunResult, RunSuccess } from "@plate-dyn/core";
3+import { useRunner } from "./RunnerContext";
4+
5+export type SolveState<T> =
6+ | { status: "idle" }
7+ | { status: "running"; startedAt: number; output: string[] }
8+ | { status: "done"; run: RunSuccess; value: T; output: string[] }
9+ | { status: "error"; run: RunFailure; output: string[] };
10+
11+export interface Solve<T> {
12+ state: SolveState<T>;
13+ /** Most recent successful value, kept while a new run is in progress */
14+ last: T | null;
15+ run: (req: RunRequest) => Promise<RunResult | undefined>;
16+ cancel: () => void;
17+ /** Forget the last value and go back to idle, so nothing stale is displayed */
18+ reset: () => void;
19+}
20+
21+/**
22+ * Drive one run at a time through the shared runner. `parse` types the raw
23+ * result; a parse failure is reported like any other error so a script that
24+ * returns the wrong shape never looks like a blank screen.
25+ */
26+export function useSolve<T>(parse: (v: PlainValue) => T): Solve<T> {
27+ const runner = useRunner();
28+ const [state, setState] = useState<SolveState<T>>({ status: "idle" });
29+ const [last, setLast] = useState<T | null>(null);
30+ const seq = useRef(0);
31+
32+ const run = useCallback(
33+ async (req: RunRequest) => {
34+ const id = ++seq.current;
35+ const output: string[] = [];
36+ setState({ status: "running", startedAt: performance.now(), output: [] });
37+ const res = await runner.run(req, (text) => {
38+ output.push(text);
39+ if (seq.current === id)
40+ setState((s) => (s.status === "running" ? { ...s, output: [...output] } : s));
41+ });
42+ if (seq.current !== id) return undefined;
43+ if (res.ok) {
44+ try {
45+ const value = parse(res.value);
46+ setLast(value);
47+ setState({ status: "done", run: res, value, output: res.output });
48+ } catch (e) {
49+ setState({
50+ status: "error",
51+ run: {
52+ ...res,
53+ ok: false,
54+ error: `the script ran, but its result has an unexpected shape: ${(e as Error).message}`,
55+ },
56+ output: res.output,
57+ });
58+ }
59+ } else {
60+ setState({ status: "error", run: res, output: res.output });
61+ }
62+ return res;
63+ },
64+ [runner, parse],
65+ );
66+
67+ const cancel = useCallback(() => {
68+ seq.current++;
69+ runner.cancel();
70+ setState({ status: "idle" });
71+ }, [runner]);
72+
73+ // Local state only: nothing is terminated, so the worker (and the numbl module it
74+ // has loaded) survives. Use `cancel` to stop a run that is actually in flight.
75+ const reset = useCallback(() => {
76+ seq.current++;
77+ setLast(null);
78+ setState({ status: "idle" });
79+ }, []);
80+
81+ return { state, last, run, cancel, reset };
82+}
packages/components/src/index.tsadded+53−0View file
@@ -0,0 +1,53 @@
1+export { RunnerProvider, useRunner } from "./hooks/RunnerContext";
2+export { useSolve, type Solve, type SolveState } from "./hooks/useSolve";
3+export { CodeView, type CodeViewProps } from "./code/CodeView";
4+export { NumberField, type NumberFieldProps } from "./params/NumberField";
5+export {
6+ ParameterPanel,
7+ ALL_SECTIONS,
8+ type ParameterPanelProps,
9+ type ParameterSection,
10+} from "./params/ParameterPanel";
11+export {
12+ FieldView,
13+ type FieldViewProps,
14+ type FieldMarker,
15+ type FieldRect,
16+ type FieldArrow,
17+} from "./field/FieldView";
18+export { Colorbar } from "./field/Colorbar";
19+export {
20+ FramePlayer,
21+ useFramePlayer,
22+ type FramePlayerProps,
23+ type FramePlayerState,
24+} from "./field/FramePlayer";
25+export { LinePlot, type LinePlotProps, type Series, type XRange } from "./plots/LinePlot";
26+export { useSharedAxis } from "./plots/useSharedAxis";
27+export { EquationsView } from "./status/EquationsView";
28+export {
29+ RunStatus,
30+ fmtMs,
31+ fmtTime,
32+ type RunStatusProps,
33+ type StagedStatus,
34+} from "./status/RunStatus";
35+export { ValidityNotes } from "./status/ValidityNotes";
36+export { AudioPlayer, type AudioPlayerProps } from "./audio/AudioPlayer";
37+export { Plate3DView, type Plate3DViewProps, type Plate3DHandle } from "./scene/Plate3DView";
38+export { MovieExport, type MovieExportProps } from "./video/MovieExport";
39+export {
40+ recordMovie,
41+ saveMovie,
42+ movieSupport,
43+ movieSpan,
44+ pickMovieFormat,
45+ frameAtTime,
46+ type MovieOptions,
47+ type MovieResult,
48+ type MovieSource,
49+ type MovieOverlay,
50+ type MovieLegend,
51+ type MovieSupport,
52+} from "./video/recordMovie";
53+export { withWebmDuration, readWebmDuration } from "./video/webmDuration";
packages/components/src/params/NumberField.tsxadded+70−0View file
@@ -0,0 +1,70 @@
1+import { useEffect, useState } from "react";
2+
3+export interface NumberFieldProps {
4+ label: string;
5+ value: number;
6+ onChange: (v: number) => void;
7+ /** Display = value * scale (e.g. 1000 to show metres as millimetres) */
8+ scale?: number;
9+ unit?: string;
10+ step?: number;
11+ min?: number;
12+ max?: number;
13+ integer?: boolean;
14+ invalid?: boolean;
15+ disabled?: boolean;
16+ title?: string;
17+}
18+
19+/** A numeric input that tolerates partial typing and commits parsed values. */
20+export function NumberField(props: NumberFieldProps) {
21+ const {
22+ label,
23+ value,
24+ onChange,
25+ scale = 1,
26+ unit,
27+ step,
28+ min,
29+ max,
30+ integer,
31+ invalid,
32+ disabled,
33+ title,
34+ } = props;
35+ const shown = formatShown(value * scale);
36+ const [text, setText] = useState(shown);
37+ useEffect(() => {
38+ // Sync from outside unless the text already means the same number
39+ if (Number(text) * 1 !== value * scale) setText(shown);
40+ // eslint-disable-next-line react-hooks/exhaustive-deps
41+ }, [value, scale]);
42+ return (
43+ <div className={`pl-field${disabled ? " pl-disabled" : ""}`} title={title}>
44+ <label>{label}</label>
45+ <input
46+ type="number"
47+ className={invalid ? "pl-invalid" : undefined}
48+ value={text}
49+ step={step ?? (integer ? 1 : "any")}
50+ min={min}
51+ max={max}
52+ disabled={disabled}
53+ onChange={(e) => {
54+ setText(e.target.value);
55+ const n = Number(e.target.value);
56+ if (e.target.value.trim() === "" || !Number.isFinite(n)) return;
57+ onChange(integer ? Math.round(n) : n / scale);
58+ }}
59+ onBlur={() => setText(shown)}
60+ />
61+ <span className="pl-unit">{unit ?? ""}</span>
62+ </div>
63+ );
64+}
65+
66+function formatShown(x: number): string {
67+ if (!Number.isFinite(x)) return "";
68+ const r = Number(x.toPrecision(10));
69+ return String(r);
70+}
packages/components/src/params/ParameterPanel.tsxadded+531−0View file
@@ -0,0 +1,531 @@
1+import type { ReactNode } from "react";
2+import {
3+ HAMMER_PRESETS,
4+ MATERIAL_PRESETS,
5+ PLATE_PRESETS,
6+ type ProblemSpec,
7+ type ValidationIssue,
8+} from "@plate-dyn/core";
9+import { NumberField } from "./NumberField";
10+
11+export type ParameterSection =
12+ | "plate"
13+ | "material"
14+ | "suspension"
15+ | "hammer"
16+ | "strike"
17+ | "probe"
18+ | "mic"
19+ | "render"
20+ | "rr"
21+ | "fem";
22+
23+export const ALL_SECTIONS: ParameterSection[] = [
24+ "plate",
25+ "material",
26+ "suspension",
27+ "hammer",
28+ "strike",
29+ "probe",
30+ "mic",
31+ "render",
32+ "rr",
33+ "fem",
34+];
35+
36+export interface ParameterPanelProps {
37+ spec: ProblemSpec;
38+ onChange: (spec: ProblemSpec) => void;
39+ /** Sections to show; default all */
40+ sections?: ParameterSection[];
41+ issues?: ValidationIssue[];
42+ /** Sections open initially; default all */
43+ open?: ParameterSection[];
44+}
45+
46+/** Controls that edit the spec. Every control maps to one field of the `params` struct. */
47+export function ParameterPanel(props: ParameterPanelProps) {
48+ const { spec, onChange, sections = ALL_SECTIONS, issues = [], open } = props;
49+ const set = (patch: Partial<ProblemSpec>) => onChange({ ...spec, ...patch });
50+ const issue = (path: string) =>
51+ issues.find((i) => i.path === path || i.path.startsWith(path + "."));
52+ const invalid = (path: string) => issue(path)?.severity === "error";
53+ const show = (s: ParameterSection) => sections.includes(s);
54+ const isOpen = (s: ParameterSection) => (open ? open.includes(s) : true);
55+
56+ const materialPresetId = MATERIAL_PRESETS.find(
57+ (p) => p.material.name === spec.material.name && p.material.E === spec.material.E,
58+ )?.id;
59+ const platePresetId = PLATE_PRESETS.find(
60+ (p) =>
61+ p.plate.width === spec.plate.width &&
62+ p.plate.height === spec.plate.height &&
63+ p.plate.thickness === spec.plate.thickness,
64+ )?.id;
65+ const hammerPresetId = HAMMER_PRESETS.find(
66+ (p) =>
67+ p.hammer.mass === spec.hammer.mass &&
68+ p.hammer.tip_radius === spec.hammer.tip_radius &&
69+ p.hammer.E === spec.hammer.E,
70+ )?.id;
71+ const half = { x: spec.plate.width / 2, y: spec.plate.height / 2 };
72+
73+ return (
74+ <div className="pl-params">
75+ {show("plate") && (
76+ <Group title="Plate" issues={issues} prefix="plate" open={isOpen("plate")}>
77+ <div className="pl-field pl-field-wide">
78+ <label>Stock</label>
79+ <select
80+ className="pl-select"
81+ value={platePresetId ?? ""}
82+ onChange={(ev) => {
83+ const p = PLATE_PRESETS.find((x) => x.id === ev.target.value);
84+ if (p) set({ plate: { ...p.plate } });
85+ }}
86+ >
87+ <option value="">custom</option>
88+ {PLATE_PRESETS.map((p) => (
89+ <option key={p.id} value={p.id}>
90+ {p.name}
91+ </option>
92+ ))}
93+ </select>
94+ </div>
95+ {platePresetId && (
96+ <div className="pl-small pl-muted">
97+ {PLATE_PRESETS.find((p) => p.id === platePresetId)?.note}
98+ </div>
99+ )}
100+ <NumberField
101+ label="Width (x)"
102+ value={spec.plate.width}
103+ scale={1000}
104+ unit="mm"
105+ invalid={invalid("plate.width")}
106+ onChange={(v) => set({ plate: { ...spec.plate, width: v } })}
107+ />
108+ <NumberField
109+ label="Height (y, the thread at the top)"
110+ value={spec.plate.height}
111+ scale={1000}
112+ unit="mm"
113+ invalid={invalid("plate.height")}
114+ onChange={(v) => set({ plate: { ...spec.plate, height: v } })}
115+ />
116+ <NumberField
117+ label="Thickness"
118+ value={spec.plate.thickness}
119+ scale={1000}
120+ unit="mm"
121+ invalid={invalid("plate.thickness")}
122+ onChange={(v) => set({ plate: { ...spec.plate, thickness: v } })}
123+ />
124+ </Group>
125+ )}
126+
127+ {show("material") && (
128+ <Group title="Material" issues={issues} prefix="material" open={isOpen("material")}>
129+ <div className="pl-field pl-field-wide">
130+ <label>Alloy</label>
131+ <select
132+ className="pl-select"
133+ value={materialPresetId ?? ""}
134+ onChange={(ev) => {
135+ const p = MATERIAL_PRESETS.find((x) => x.id === ev.target.value);
136+ if (p) set({ material: { ...p.material } });
137+ }}
138+ >
139+ <option value="">custom</option>
140+ {MATERIAL_PRESETS.map((p) => (
141+ <option key={p.id} value={p.id}>
142+ {p.material.name}
143+ </option>
144+ ))}
145+ </select>
146+ </div>
147+ {materialPresetId && (
148+ <div className="pl-small pl-muted">
149+ {MATERIAL_PRESETS.find((p) => p.id === materialPresetId)?.source}
150+ </div>
151+ )}
152+ <NumberField
153+ label="Young's modulus E"
154+ value={spec.material.E}
155+ scale={1e-9}
156+ unit="GPa"
157+ invalid={invalid("material.E")}
158+ onChange={(v) =>
159+ set({ material: { ...spec.material, E: v, name: customName(spec.material.name) } })
160+ }
161+ />
162+ <NumberField
163+ label="Poisson's ratio ν"
164+ value={spec.material.nu}
165+ step={0.01}
166+ invalid={invalid("material.nu")}
167+ onChange={(v) =>
168+ set({ material: { ...spec.material, nu: v, name: customName(spec.material.name) } })
169+ }
170+ />
171+ <NumberField
172+ label="Density ρ"
173+ value={spec.material.rho}
174+ unit="kg/m³"
175+ invalid={invalid("material.rho")}
176+ onChange={(v) =>
177+ set({ material: { ...spec.material, rho: v, name: customName(spec.material.name) } })
178+ }
179+ />
180+ <NumberField
181+ label="Structural Q"
182+ value={spec.material.Q}
183+ invalid={invalid("material.Q")}
184+ title="Internal loss alone: 1/Q per mode; air loading and the thread's dashpot are added per mode"
185+ onChange={(v) => set({ material: { ...spec.material, Q: v } })}
186+ />
187+ </Group>
188+ )}
189+
190+ {show("suspension") && (
191+ <Group title="Hole and thread" issues={issues} prefix="hole" open={isOpen("suspension")}>
192+ <NumberField
193+ label="Hole diameter"
194+ value={spec.hole.diameter}
195+ scale={1000}
196+ unit="mm"
197+ invalid={invalid("hole.diameter")}
198+ onChange={(v) => set({ hole: { ...spec.hole, diameter: v } })}
199+ />
200+ <NumberField
201+ label="Hole below the top edge"
202+ value={spec.hole.edge_offset}
203+ scale={1000}
204+ unit="mm"
205+ invalid={invalid("hole.edge_offset")}
206+ onChange={(v) => set({ hole: { ...spec.hole, edge_offset: v } })}
207+ />
208+ <NumberField
209+ label="Thread length"
210+ value={spec.thread.length}
211+ scale={1000}
212+ unit="mm"
213+ invalid={invalid("thread.length")}
214+ title="Sets the pendulum spring m g / L at the hole"
215+ onChange={(v) => set({ thread: { ...spec.thread, length: v } })}
216+ />
217+ <NumberField
218+ label="Dashpot at the hole"
219+ value={spec.thread.dashpot}
220+ scale={1000}
221+ unit="mN s/m"
222+ invalid={invalid("thread.dashpot")}
223+ onChange={(v) => set({ thread: { ...spec.thread, dashpot: v } })}
224+ />
225+ <div className="pl-small pl-muted">
226+ The thread is a soft point spring at the hole, not a clamp; the plate is free to rotate
227+ about it. The dashpot is the loss where the thread rubs the hole.
228+ </div>
229+ </Group>
230+ )}
231+
232+ {show("hammer") && (
233+ <Group title="Hammer" issues={issues} prefix="hammer" open={isOpen("hammer")}>
234+ <div className="pl-field pl-field-wide">
235+ <label>Hammer</label>
236+ <select
237+ className="pl-select"
238+ value={hammerPresetId ?? ""}
239+ onChange={(ev) => {
240+ const p = HAMMER_PRESETS.find((x) => x.id === ev.target.value);
241+ if (p) set({ hammer: { ...p.hammer, speed: spec.hammer.speed } });
242+ }}
243+ >
244+ <option value="">custom</option>
245+ {HAMMER_PRESETS.map((p) => (
246+ <option key={p.id} value={p.id}>
247+ {p.name}
248+ </option>
249+ ))}
250+ </select>
251+ </div>
252+ {hammerPresetId && (
253+ <div className="pl-small pl-muted">
254+ {HAMMER_PRESETS.find((p) => p.id === hammerPresetId)?.note}
255+ </div>
256+ )}
257+ <NumberField
258+ label="Mass"
259+ value={spec.hammer.mass}
260+ scale={1000}
261+ unit="g"
262+ invalid={invalid("hammer.mass")}
263+ onChange={(v) => set({ hammer: { ...spec.hammer, mass: v } })}
264+ />
265+ <NumberField
266+ label="Tip radius"
267+ value={spec.hammer.tip_radius}
268+ scale={1000}
269+ unit="mm"
270+ invalid={invalid("hammer.tip_radius")}
271+ onChange={(v) => set({ hammer: { ...spec.hammer, tip_radius: v } })}
272+ />
273+ <NumberField
274+ label="Approach speed"
275+ value={spec.hammer.speed}
276+ step={0.05}
277+ unit="m/s"
278+ invalid={invalid("hammer.speed")}
279+ onChange={(v) => set({ hammer: { ...spec.hammer, speed: v } })}
280+ />
281+ <NumberField
282+ label="Restitution e"
283+ value={spec.hammer.restitution}
284+ step={0.02}
285+ invalid={invalid("hammer.restitution")}
286+ title="Nominal: sets the impulse of the prescribed pulse and the loss of the coupled contact"
287+ onChange={(v) => set({ hammer: { ...spec.hammer, restitution: v } })}
288+ />
289+ <NumberField
290+ label="Tip modulus E"
291+ value={spec.hammer.E}
292+ scale={1e-9}
293+ unit="GPa"
294+ invalid={invalid("hammer.E")}
295+ onChange={(v) => set({ hammer: { ...spec.hammer, E: v } })}
296+ />
297+ <NumberField
298+ label="Tip Poisson's ratio"
299+ value={spec.hammer.nu}
300+ step={0.01}
301+ invalid={invalid("hammer.nu")}
302+ onChange={(v) => set({ hammer: { ...spec.hammer, nu: v } })}
303+ />
304+ </Group>
305+ )}
306+
307+ {show("strike") && (
308+ <Group title="Strike point" issues={issues} prefix="strike" open={isOpen("strike")}>
309+ <NumberField
310+ label="x from the centre"
311+ value={spec.strike.x}
312+ scale={1000}
313+ unit="mm"
314+ min={-half.x * 1000}
315+ max={half.x * 1000}
316+ invalid={invalid("strike")}
317+ onChange={(v) => set({ strike: { ...spec.strike, x: v } })}
318+ />
319+ <NumberField
320+ label="y from the centre"
321+ value={spec.strike.y}
322+ scale={1000}
323+ unit="mm"
324+ min={-half.y * 1000}
325+ max={half.y * 1000}
326+ invalid={invalid("strike")}
327+ title="Positive towards the threaded top edge"
328+ onChange={(v) => set({ strike: { ...spec.strike, y: v } })}
329+ />
330+ <div className="pl-small pl-muted">
331+ The centre lies on both symmetry axes and leaves most asymmetric modes silent; a few
332+ millimetres off centre wakes them.
333+ </div>
334+ </Group>
335+ )}
336+
337+ {show("probe") && (
338+ <Group title="Probe" issues={issues} prefix="probe" open={isOpen("probe")}>
339+ <NumberField
340+ label="x from the centre"
341+ value={spec.probe.x}
342+ scale={1000}
343+ unit="mm"
344+ invalid={invalid("probe")}
345+ onChange={(v) => set({ probe: { ...spec.probe, x: v } })}
346+ />
347+ <NumberField
348+ label="y from the centre"
349+ value={spec.probe.y}
350+ scale={1000}
351+ unit="mm"
352+ invalid={invalid("probe")}
353+ onChange={(v) => set({ probe: { ...spec.probe, y: v } })}
354+ />
355+ </Group>
356+ )}
357+
358+ {show("mic") && (
359+ <Group title="Microphone" issues={issues} prefix="mic" open={isOpen("mic")}>
360+ <NumberField
361+ label="Distance on the normal"
362+ value={spec.mic.distance}
363+ scale={1000}
364+ unit="mm"
365+ invalid={invalid("mic.distance")}
366+ onChange={(v) => set({ mic: { ...spec.mic, distance: v } })}
367+ />
368+ <NumberField
369+ label="Air density"
370+ value={spec.mic.air_density}
371+ step={0.01}
372+ unit="kg/m³"
373+ invalid={invalid("mic.air_density")}
374+ onChange={(v) => set({ mic: { ...spec.mic, air_density: v } })}
375+ />
376+ <NumberField
377+ label="Speed of sound"
378+ value={spec.mic.sound_speed}
379+ unit="m/s"
380+ invalid={invalid("mic.sound_speed")}
381+ onChange={(v) => set({ mic: { ...spec.mic, sound_speed: v } })}
382+ />
383+ </Group>
384+ )}
385+
386+ {show("render") && (
387+ <Group title="Render" issues={issues} prefix="render" open={isOpen("render")}>
388+ <NumberField
389+ label="Sample rate"
390+ value={spec.render.fs}
391+ unit="Hz"
392+ integer
393+ invalid={invalid("render.fs")}
394+ title="Modes are kept below 0.47 of this; 96 kHz reaches this small plate's ultrasonic partials"
395+ onChange={(v) => set({ render: { ...spec.render, fs: v } })}
396+ />
397+ <NumberField
398+ label="Duration"
399+ value={spec.render.duration}
400+ step={0.5}
401+ unit="s"
402+ invalid={invalid("render.duration")}
403+ onChange={(v) => set({ render: { ...spec.render, duration: v } })}
404+ />
405+ <NumberField
406+ label="Modes kept, at most"
407+ value={spec.render.modes}
408+ integer
409+ invalid={invalid("render.modes")}
410+ onChange={(v) => set({ render: { ...spec.render, modes: v } })}
411+ />
412+ <NumberField
413+ label="Export peak"
414+ value={spec.render.normalize_dbfs}
415+ step={0.5}
416+ unit="dBFS"
417+ invalid={invalid("render.normalize_dbfs")}
418+ onChange={(v) => set({ render: { ...spec.render, normalize_dbfs: v } })}
419+ />
420+ <NumberField
421+ label="Animation frames"
422+ value={spec.render.nframes}
423+ integer
424+ invalid={invalid("render.nframes")}
425+ onChange={(v) => set({ render: { ...spec.render, nframes: v } })}
426+ />
427+ <NumberField
428+ label="Frames cover the first"
429+ value={spec.render.frame_span}
430+ scale={1000}
431+ step={0.5}
432+ unit="ms"
433+ invalid={invalid("render.frame_span")}
434+ title="The strike transient: a wave leaving the hammer and filling the plate"
435+ onChange={(v) => set({ render: { ...spec.render, frame_span: v } })}
436+ />
437+ </Group>
438+ )}
439+
440+ {show("rr") && (
441+ <Group title="Rayleigh-Ritz numerics" issues={issues} prefix="rr" open={isOpen("rr")}>
442+ <NumberField
443+ label="Legendre order per side"
444+ value={spec.rr.basis_order}
445+ integer
446+ invalid={invalid("rr.basis_order")}
447+ title="(order + 1)^2 coefficients; 12 was stable to 0.01 Hz against 14 on the first ten modes"
448+ onChange={(v) => set({ rr: { ...spec.rr, basis_order: v } })}
449+ />
450+ <NumberField
451+ label="Quadrature points per side"
452+ value={spec.rr.quadrature_order}
453+ integer
454+ invalid={invalid("rr.quadrature_order")}
455+ onChange={(v) => set({ rr: { ...spec.rr, quadrature_order: v } })}
456+ />
457+ <NumberField
458+ label="Display grid per side"
459+ value={spec.rr.grid}
460+ integer
461+ invalid={invalid("rr.grid")}
462+ onChange={(v) => set({ rr: { ...spec.rr, grid: v } })}
463+ />
464+ </Group>
465+ )}
466+
467+ {show("fem") && (
468+ <Group title="Finite-element numerics" issues={issues} prefix="fem" open={isOpen("fem")}>
469+ <NumberField
470+ label="Elements along x"
471+ value={spec.fem.mesh_x}
472+ integer
473+ invalid={invalid("fem.mesh_x")}
474+ onChange={(v) => set({ fem: { ...spec.fem, mesh_x: v } })}
475+ />
476+ <NumberField
477+ label="Elements along y"
478+ value={spec.fem.mesh_y}
479+ integer
480+ invalid={invalid("fem.mesh_y")}
481+ onChange={(v) => set({ fem: { ...spec.fem, mesh_y: v } })}
482+ />
483+ <div className="pl-small pl-muted">
484+ Full-integration Q4 locks in shear on a thin plate: the reference used 80 × 80, where
485+ the main partial is 1 percent above Rayleigh-Ritz; 48 × 48 is 3 percent above and solves
486+ in a few seconds, 80 × 80 in tens of seconds.
487+ </div>
488+ </Group>
489+ )}
490+ </div>
491+ );
492+}
493+
494+function customName(name: string): string {
495+ return name.endsWith(" (edited)") ? name : `${name} (edited)`;
496+}
497+
498+function Group(props: {
499+ title: string;
500+ prefix: string;
501+ issues: ValidationIssue[];
502+ open: boolean;
503+ disabled?: boolean;
504+ children: ReactNode;
505+}) {
506+ const prefixes = props.prefix === "hole" ? ["hole", "thread"] : [props.prefix];
507+ const related = props.issues.filter((i) =>
508+ prefixes.some((p) => i.path === p || i.path.startsWith(p + ".")),
509+ );
510+ return (
511+ <details className={`pl-group${props.disabled ? " pl-disabled" : ""}`} open={props.open}>
512+ <summary>
513+ {props.title}
514+ {related.some((i) => i.severity === "error") && (
515+ <span className="pl-badge pl-badge-error" style={{ marginLeft: 6 }}>
516+ check
517+ </span>
518+ )}
519+ </summary>
520+ {props.children}
521+ {related.map((i) => (
522+ <div
523+ key={i.path + i.message}
524+ className={`pl-issue${i.severity === "warning" ? " pl-issue-warning" : ""}`}
525+ >
526+ {i.path}: {i.message}
527+ </div>
528+ ))}
529+ </details>
530+ );
531+}
packages/components/src/plots/LinePlot.tsxadded+522−0View file
@@ -0,0 +1,522 @@
1+import { useCallback, useEffect, useLayoutEffect, useRef, useState } from "react";
2+
3+export interface Series {
4+ x: ArrayLike<number>;
5+ y: ArrayLike<number>;
6+ color?: string;
7+ label?: string;
8+ width?: number;
9+ dash?: number[];
10+}
11+
12+export type XRange = [number, number];
13+
14+export interface LinePlotProps {
15+ series: Series[];
16+ title?: string;
17+ xLabel?: string;
18+ yLabel?: string;
19+ /** Display multipliers (e.g. 1000 to plot seconds as milliseconds) */
20+ xScale?: number;
21+ yScale?: number;
22+ logY?: boolean;
23+ /** Visible x window in data units; null or undefined shows `defaultXRange` or everything */
24+ xRange?: XRange | null;
25+ /** Window shown when not zoomed (data units); zooming out past it up to the data extent is allowed */
26+ defaultXRange?: XRange;
27+ /** Enables zooming: drag to select, wheel to zoom about the pointer, double-click to reset. */
28+ onXRangeChange?: (r: XRange | null) => void;
29+ yRange?: [number, number];
30+ /** Vertical marker at this x (data units), e.g. the animation time */
31+ cursorX?: number;
32+ /** Crosshair with value readout at this x (data units), typically shared between plots */
33+ hoverX?: number | null;
34+ onHover?: (x: number | null) => void;
35+ onClick?: (x: number) => void;
36+ height?: number;
37+ /** Unit shown in the readout */
38+ yUnit?: string;
39+}
40+
41+const DEFAULT_COLORS = ["#1f5fa8", "#d1495b", "#2e8b57", "#8a5cbf", "#e08a1e", "#3aa7b8"];
42+const MARGIN = { left: 60, right: 12, top: 8, bottom: 30 };
43+
44+interface Geometry {
45+ width: number;
46+ height: number;
47+ xmin: number;
48+ xmax: number;
49+ ymin: number;
50+ ymax: number;
51+}
52+
53+/**
54+ * Canvas line plot. The data layer redraws only when the data or the window
55+ * changes; the cursor, crosshair and selection live on a separate overlay so
56+ * hovering costs nothing. Draws the solver's numbers; computes nothing about them.
57+ */
58+export function LinePlot(props: LinePlotProps) {
59+ const {
60+ series,
61+ title,
62+ xLabel,
63+ yLabel,
64+ xScale = 1,
65+ yScale = 1,
66+ logY,
67+ defaultXRange,
68+ onXRangeChange,
69+ yRange,
70+ cursorX,
71+ hoverX,
72+ onHover,
73+ onClick,
74+ height = 200,
75+ yUnit,
76+ } = props;
77+ const xRange = props.xRange ?? defaultXRange ?? null;
78+ const zoomed = !!props.xRange;
79+ const baseRef = useRef<HTMLCanvasElement>(null);
80+ const overRef = useRef<HTMLCanvasElement>(null);
81+ const boxRef = useRef<HTMLDivElement>(null);
82+ const geom = useRef<Geometry>({ width: 400, height, xmin: 0, xmax: 1, ymin: 0, ymax: 1 });
83+ const [width, setWidth] = useState(400);
84+ const [drag, setDrag] = useState<{ x0: number; x1: number } | null>(null);
85+ const dragRef = useRef<{ x0: number; x1: number } | null>(null);
86+
87+ // Track the container width
88+ useLayoutEffect(() => {
89+ const box = boxRef.current;
90+ if (!box) return;
91+ const update = () => setWidth(Math.max(120, box.clientWidth));
92+ update();
93+ const ro = new ResizeObserver(update);
94+ ro.observe(box);
95+ return () => ro.disconnect();
96+ }, []);
97+
98+ const pw = width - MARGIN.left - MARGIN.right;
99+ const ph = height - MARGIN.top - MARGIN.bottom;
100+
101+ // Data extents in display units
102+ const fullX = useRef<XRange>([0, 1]);
103+
104+ // ---- Data layer
105+ useEffect(() => {
106+ const canvas = baseRef.current;
107+ if (!canvas) return;
108+ const dpr = Math.min(2, window.devicePixelRatio || 1);
109+ canvas.width = Math.round(width * dpr);
110+ canvas.height = Math.round(height * dpr);
111+ const ctx = canvas.getContext("2d");
112+ if (!ctx) return;
113+ ctx.save();
114+ ctx.scale(dpr, dpr);
115+ ctx.clearRect(0, 0, width, height);
116+
117+ let dxmin = Infinity,
118+ dxmax = -Infinity;
119+ for (const s of series) {
120+ for (let i = 0; i < s.x.length; i++) {
121+ const x = s.x[i] * xScale;
122+ if (!Number.isFinite(x)) continue;
123+ if (x < dxmin) dxmin = x;
124+ if (x > dxmax) dxmax = x;
125+ }
126+ }
127+ if (!Number.isFinite(dxmin) || dxmin === dxmax) [dxmin, dxmax] = [0, 1];
128+ fullX.current = [dxmin, dxmax];
129+ let xmin = dxmin,
130+ xmax = dxmax;
131+ if (xRange) [xmin, xmax] = [xRange[0] * xScale, xRange[1] * xScale];
132+ if (xmin === xmax) xmax = xmin + 1;
133+
134+ // y extents over the visible window
135+ let ymin = Infinity,
136+ ymax = -Infinity;
137+ for (const s of series) {
138+ for (let i = 0; i < s.x.length; i++) {
139+ const x = s.x[i] * xScale;
140+ if (x < xmin || x > xmax) continue;
141+ let y = s.y[i] * yScale;
142+ if (!Number.isFinite(y)) continue;
143+ if (logY) {
144+ if (y <= 0) continue;
145+ y = Math.log10(y);
146+ }
147+ if (y < ymin) ymin = y;
148+ if (y > ymax) ymax = y;
149+ }
150+ }
151+ if (yRange)
152+ [ymin, ymax] = logY
153+ ? [Math.log10(yRange[0] * yScale), Math.log10(yRange[1] * yScale)]
154+ : [yRange[0] * yScale, yRange[1] * yScale];
155+ if (!Number.isFinite(ymin)) [ymin, ymax] = [0, 1];
156+ if (ymin === ymax) [ymin, ymax] = [ymin - 1, ymax + 1];
157+ if (!yRange && !logY) {
158+ const pad = 0.05 * (ymax - ymin);
159+ ymin -= pad;
160+ ymax += pad;
161+ }
162+ geom.current = { width, height, xmin, xmax, ymin, ymax };
163+ const X = (x: number) => MARGIN.left + ((x - xmin) / (xmax - xmin)) * pw;
164+ const Y = (y: number) => MARGIN.top + (1 - (y - ymin) / (ymax - ymin)) * ph;
165+
166+ ctx.font = "10px system-ui, sans-serif";
167+ ctx.fillStyle = "#5b6573";
168+ ctx.strokeStyle = "#e3e6ea";
169+ ctx.lineWidth = 1;
170+ for (const t of ticks(xmin, xmax, Math.max(3, Math.floor(pw / 70)))) {
171+ const px = X(t);
172+ ctx.beginPath();
173+ ctx.moveTo(px, MARGIN.top);
174+ ctx.lineTo(px, MARGIN.top + ph);
175+ ctx.stroke();
176+ ctx.textAlign = "center";
177+ ctx.fillText(fmtTick(t), px, MARGIN.top + ph + 12);
178+ }
179+ const yt = logY ? logTicks(ymin, ymax) : ticks(ymin, ymax, 5);
180+ for (const t of yt) {
181+ const py = Y(t);
182+ ctx.beginPath();
183+ ctx.moveTo(MARGIN.left, py);
184+ ctx.lineTo(MARGIN.left + pw, py);
185+ ctx.stroke();
186+ ctx.textAlign = "right";
187+ ctx.fillText(logY ? fmtTick(Math.pow(10, t)) : fmtTick(t), MARGIN.left - 4, py + 3);
188+ }
189+ ctx.strokeStyle = "#8a93a0";
190+ ctx.strokeRect(MARGIN.left + 0.5, MARGIN.top + 0.5, pw - 1, ph - 1);
191+ if (xLabel) {
192+ ctx.textAlign = "center";
193+ ctx.fillText(xLabel, MARGIN.left + pw / 2, height - 4);
194+ }
195+ if (yLabel) {
196+ ctx.save();
197+ ctx.translate(10, MARGIN.top + ph / 2);
198+ ctx.rotate(-Math.PI / 2);
199+ ctx.textAlign = "center";
200+ ctx.fillText(yLabel, 0, 0);
201+ ctx.restore();
202+ }
203+
204+ ctx.save();
205+ ctx.beginPath();
206+ ctx.rect(MARGIN.left, MARGIN.top, pw, ph);
207+ ctx.clip();
208+ series.forEach((s, si) => {
209+ ctx.strokeStyle = s.color ?? DEFAULT_COLORS[si % DEFAULT_COLORS.length];
210+ ctx.lineWidth = s.width ?? 1.2;
211+ ctx.setLineDash(s.dash ?? []);
212+ ctx.beginPath();
213+ let pen = false;
214+ const n = s.x.length;
215+ // Visible index window (x assumed increasing), decimated to about two points per pixel
216+ const i0 = Math.max(0, lowerBound(s.x, xmin / xScale) - 1);
217+ const i1 = Math.min(n - 1, lowerBound(s.x, xmax / xScale) + 1);
218+ const stride = Math.max(1, Math.floor((i1 - i0) / (pw * 2)));
219+ for (let i = i0; i <= i1; i += stride) {
220+ const x = s.x[i] * xScale;
221+ let y = s.y[i] * yScale;
222+ if (logY) y = y > 0 ? Math.log10(y) : NaN;
223+ if (!Number.isFinite(x) || !Number.isFinite(y)) {
224+ pen = false;
225+ continue;
226+ }
227+ const px = X(x),
228+ py = Y(y);
229+ if (pen) ctx.lineTo(px, py);
230+ else {
231+ ctx.moveTo(px, py);
232+ pen = true;
233+ }
234+ }
235+ ctx.stroke();
236+ });
237+ ctx.setLineDash([]);
238+ ctx.restore();
239+ ctx.restore();
240+ }, [series, xScale, yScale, logY, xRange, yRange, height, width, xLabel, yLabel, pw, ph]);
241+
242+ // ---- Overlay layer: cursor, crosshair with readout, drag selection
243+ useEffect(() => {
244+ const canvas = overRef.current;
245+ if (!canvas) return;
246+ const dpr = Math.min(2, window.devicePixelRatio || 1);
247+ canvas.width = Math.round(width * dpr);
248+ canvas.height = Math.round(height * dpr);
249+ const ctx = canvas.getContext("2d");
250+ if (!ctx) return;
251+ ctx.save();
252+ ctx.scale(dpr, dpr);
253+ ctx.clearRect(0, 0, width, height);
254+ const g = geom.current;
255+ const X = (x: number) => MARGIN.left + ((x - g.xmin) / (g.xmax - g.xmin)) * pw;
256+ const Y = (y: number) => MARGIN.top + (1 - (y - g.ymin) / (g.ymax - g.ymin)) * ph;
257+ ctx.beginPath();
258+ ctx.rect(MARGIN.left, MARGIN.top, pw, ph);
259+ ctx.clip();
260+ if (cursorX !== undefined && Number.isFinite(cursorX)) {
261+ const px = X(cursorX * xScale);
262+ ctx.strokeStyle = "#333";
263+ ctx.lineWidth = 1;
264+ ctx.beginPath();
265+ ctx.moveTo(px, MARGIN.top);
266+ ctx.lineTo(px, MARGIN.top + ph);
267+ ctx.stroke();
268+ }
269+ if (hoverX !== undefined && hoverX !== null && Number.isFinite(hoverX)) {
270+ const hx = hoverX * xScale;
271+ if (hx >= g.xmin && hx <= g.xmax) {
272+ const px = X(hx);
273+ ctx.strokeStyle = "rgba(0,0,0,0.35)";
274+ ctx.lineWidth = 1;
275+ ctx.setLineDash([3, 3]);
276+ ctx.beginPath();
277+ ctx.moveTo(px, MARGIN.top);
278+ ctx.lineTo(px, MARGIN.top + ph);
279+ ctx.stroke();
280+ ctx.setLineDash([]);
281+ ctx.font = "10px system-ui, sans-serif";
282+ const lines: { text: string; color: string; py: number }[] = [];
283+ series.forEach((s, si) => {
284+ const i = nearestIndex(s.x, hoverX);
285+ if (i < 0) return;
286+ const y = s.y[i] * yScale;
287+ if (!Number.isFinite(y)) return;
288+ const color = s.color ?? DEFAULT_COLORS[si % DEFAULT_COLORS.length];
289+ const py = Y(logY ? (y > 0 ? Math.log10(y) : g.ymin) : y);
290+ ctx.fillStyle = color;
291+ ctx.beginPath();
292+ ctx.arc(px, py, 3, 0, 2 * Math.PI);
293+ ctx.fill();
294+ lines.push({
295+ text: `${s.label ? s.label + ": " : ""}${fmtTick(y)}${yUnit ? " " + yUnit : ""}`,
296+ color,
297+ py,
298+ });
299+ });
300+ const boxW = Math.max(60, ...lines.map((l) => ctx.measureText(l.text).width + 10));
301+ const right = px + 8 + boxW > MARGIN.left + pw;
302+ const bx = right ? px - 8 - boxW : px + 8;
303+ const by = MARGIN.top + 4;
304+ const boxH = 14 * (lines.length + 1) + 4;
305+ ctx.fillStyle = "rgba(255,255,255,0.92)";
306+ ctx.fillRect(bx, by, boxW, boxH);
307+ ctx.strokeStyle = "#c9ced5";
308+ ctx.strokeRect(bx + 0.5, by + 0.5, boxW - 1, boxH - 1);
309+ ctx.textAlign = "left";
310+ ctx.fillStyle = "#5b6573";
311+ ctx.fillText(
312+ `${xLabel ? xLabel.replace(/\s*\[.*\]\s*$/, "") + " " : ""}${fmtTick(hx)}${unitOf(xLabel)}`,
313+ bx + 5,
314+ by + 12,
315+ );
316+ lines.forEach((l, k) => {
317+ ctx.fillStyle = l.color;
318+ ctx.fillText(l.text, bx + 5, by + 12 + 14 * (k + 1));
319+ });
320+ }
321+ }
322+ if (drag) {
323+ const a = Math.min(drag.x0, drag.x1),
324+ b = Math.max(drag.x0, drag.x1);
325+ ctx.fillStyle = "rgba(31,95,168,0.15)";
326+ ctx.fillRect(a, MARGIN.top, b - a, ph);
327+ }
328+ ctx.restore();
329+ }, [
330+ cursorX,
331+ hoverX,
332+ drag,
333+ series,
334+ xScale,
335+ yScale,
336+ logY,
337+ width,
338+ height,
339+ pw,
340+ ph,
341+ xLabel,
342+ yUnit,
343+ xRange,
344+ ]);
345+
346+ // ---- Interaction
347+ const toDataX = useCallback(
348+ (clientX: number, el: HTMLElement) => {
349+ const rect = el.getBoundingClientRect();
350+ const px = clientX - rect.left;
351+ const g = geom.current;
352+ const f = (px - MARGIN.left) / pw;
353+ return { px, x: (g.xmin + f * (g.xmax - g.xmin)) / xScale, inside: f >= 0 && f <= 1 };
354+ },
355+ [pw, xScale],
356+ );
357+
358+ const clampRange = useCallback(
359+ (a: number, b: number): XRange | null => {
360+ const [fmin, fmax] = fullX.current.map((v) => v / xScale) as XRange;
361+ let lo = Math.max(fmin, Math.min(a, b));
362+ let hi = Math.min(fmax, Math.max(a, b));
363+ if (hi - lo < (fmax - fmin) * 1e-4) return null;
364+ if (lo <= fmin && hi >= fmax) return null;
365+ return [lo, hi];
366+ },
367+ [xScale],
368+ );
369+
370+ // Wheel zoom needs a non-passive listener to prevent page scroll
371+ useEffect(() => {
372+ const el = overRef.current;
373+ if (!el || !onXRangeChange) return;
374+ const onWheel = (e: WheelEvent) => {
375+ const { x, inside } = toDataX(e.clientX, el);
376+ if (!inside) return;
377+ e.preventDefault();
378+ const g = geom.current;
379+ const factor = Math.exp(e.deltaY * 0.0015);
380+ const lo = x - (x - g.xmin / xScale) * factor;
381+ const hi = x + (g.xmax / xScale - x) * factor;
382+ onXRangeChange(clampRange(lo, hi));
383+ };
384+ el.addEventListener("wheel", onWheel, { passive: false });
385+ return () => el.removeEventListener("wheel", onWheel);
386+ }, [onXRangeChange, toDataX, clampRange, xScale]);
387+
388+ return (
389+ <div className="pl-plot" ref={boxRef}>
390+ {(title || zoomed) && (
391+ <div className="pl-plot-title">
392+ {title}
393+ {zoomed && onXRangeChange && (
394+ <button
395+ className="pl-linkbtn"
396+ onClick={() => onXRangeChange(null)}
397+ title="double-click the plot also resets"
398+ >
399+ reset zoom
400+ </button>
401+ )}
402+ </div>
403+ )}
404+ <div style={{ position: "relative", height }}>
405+ <canvas ref={baseRef} style={{ position: "absolute", inset: 0, width: "100%", height }} />
406+ <canvas
407+ ref={overRef}
408+ style={{
409+ position: "absolute",
410+ inset: 0,
411+ width: "100%",
412+ height,
413+ cursor: onXRangeChange ? "crosshair" : onClick ? "pointer" : "default",
414+ }}
415+ onMouseMove={(e) => {
416+ const { px, x, inside } = toDataX(e.clientX, e.currentTarget);
417+ if (dragRef.current) {
418+ dragRef.current = { ...dragRef.current, x1: px };
419+ setDrag(dragRef.current);
420+ }
421+ onHover?.(inside ? x : null);
422+ }}
423+ onMouseLeave={() => onHover?.(null)}
424+ onMouseDown={(e) => {
425+ if (!onXRangeChange || e.button !== 0) return;
426+ const { px, inside } = toDataX(e.clientX, e.currentTarget);
427+ if (!inside) return;
428+ dragRef.current = { x0: px, x1: px };
429+ setDrag(dragRef.current);
430+ }}
431+ onMouseUp={(e) => {
432+ const d = dragRef.current;
433+ dragRef.current = null;
434+ setDrag(null);
435+ const { x } = toDataX(e.clientX, e.currentTarget);
436+ if (d && Math.abs(d.x1 - d.x0) > 4 && onXRangeChange) {
437+ const g = geom.current;
438+ const toX = (px: number) =>
439+ (g.xmin + ((px - MARGIN.left) / pw) * (g.xmax - g.xmin)) / xScale;
440+ onXRangeChange(clampRange(toX(d.x0), toX(d.x1)));
441+ } else if (onClick) {
442+ onClick(x);
443+ }
444+ }}
445+ onDoubleClick={() => onXRangeChange?.(null)}
446+ />
447+ </div>
448+ {series.some((s) => s.label) && (
449+ <div className="pl-legend">
450+ {series.map((s, i) => (
451+ <span
452+ key={i}
453+ style={{ ["--c" as string]: s.color ?? DEFAULT_COLORS[i % DEFAULT_COLORS.length] }}
454+ >
455+ {s.label}
456+ </span>
457+ ))}
458+ {onXRangeChange && (
459+ <span className="pl-hint">drag or scroll to zoom, double-click to reset</span>
460+ )}
461+ </div>
462+ )}
463+ </div>
464+ );
465+}
466+
467+function unitOf(label?: string): string {
468+ const m = label?.match(/\[(.*)\]\s*$/);
469+ return m ? ` ${m[1]}` : "";
470+}
471+
472+/** First index with x[i] >= v (x increasing). */
473+function lowerBound(x: ArrayLike<number>, v: number): number {
474+ let lo = 0,
475+ hi = x.length;
476+ while (lo < hi) {
477+ const mid = (lo + hi) >> 1;
478+ if (x[mid] < v) lo = mid + 1;
479+ else hi = mid;
480+ }
481+ return lo;
482+}
483+
484+function nearestIndex(x: ArrayLike<number>, v: number): number {
485+ const n = x.length;
486+ if (n === 0) return -1;
487+ const i = lowerBound(x, v);
488+ if (i <= 0) return 0;
489+ if (i >= n) return n - 1;
490+ return Math.abs(x[i] - v) < Math.abs(x[i - 1] - v) ? i : i - 1;
491+}
492+
493+export function ticks(min: number, max: number, n: number): number[] {
494+ const span = max - min;
495+ if (!(span > 0)) return [min];
496+ const raw = span / n;
497+ const mag = Math.pow(10, Math.floor(Math.log10(raw)));
498+ const norm = raw / mag;
499+ const step = (norm < 1.5 ? 1 : norm < 3.5 ? 2 : norm < 7.5 ? 5 : 10) * mag;
500+ const out: number[] = [];
501+ for (let t = Math.ceil(min / step) * step; t <= max + 1e-9 * span; t += step)
502+ out.push(Number(t.toPrecision(12)));
503+ return out;
504+}
505+
506+function logTicks(lmin: number, lmax: number): number[] {
507+ const lo = Math.ceil(lmin),
508+ hi = Math.floor(lmax);
509+ if (hi < lo) return [lmin, lmax];
510+ // at most about six labelled decades, so wide dynamic ranges stay legible
511+ const step = Math.max(1, Math.ceil((hi - lo + 1) / 6));
512+ const out: number[] = [];
513+ for (let e = lo; e <= hi; e += step) out.push(e);
514+ return out;
515+}
516+
517+export function fmtTick(x: number): string {
518+ if (x === 0) return "0";
519+ const a = Math.abs(x);
520+ if (a >= 1e5 || a < 1e-3) return x.toExponential(2).replace("e+", "e");
521+ return String(Number(x.toPrecision(4)));
522+}
packages/components/src/plots/useSharedAxis.tsadded+18−0View file
@@ -0,0 +1,18 @@
1+import { useState } from "react";
2+import type { XRange } from "./LinePlot";
3+
4+/**
5+ * One x window and one hover position shared by several plots, so zooming or
6+ * pointing at one moves them all. Spread the result into each LinePlot.
7+ */
8+export function useSharedAxis() {
9+ const [xRange, setXRange] = useState<XRange | null>(null);
10+ const [hoverX, setHoverX] = useState<number | null>(null);
11+ return {
12+ xRange,
13+ hoverX,
14+ onXRangeChange: setXRange,
15+ onHover: setHoverX,
16+ reset: () => setXRange(null),
17+ };
18+}
packages/components/src/scene/Plate3DView.tsxadded+415−0View file
@@ -0,0 +1,415 @@
1+import { forwardRef, useEffect, useImperativeHandle, useMemo, useRef, useState } from "react";
2+import {
3+ colormapTable,
4+ getColormap,
5+ holePosition,
6+ type FieldFrames,
7+ type PlateResult,
8+ type ProblemSpec,
9+ type Range,
10+} from "@plate-dyn/core";
11+
12+export interface Plate3DViewProps {
13+ result: PlateResult;
14+ spec: ProblemSpec;
15+ frameIndex: number;
16+ /** Frames that displace the surface along the normal (the flexural field, or a mode shape) */
17+ frames?: FieldFrames;
18+ /** Frames that colour the surface (any per-node field) and their colour range */
19+ colorFrames?: FieldFrames;
20+ colorRange?: Range;
21+ colormap?: string;
22+ /** Exaggeration of the displacement */
23+ scale: number;
24+ width?: number;
25+ height?: number;
26+}
27+
28+type Vec3 = [number, number, number];
29+
30+const VS = `
31+attribute vec3 aPos;
32+attribute vec3 aNormal;
33+attribute vec3 aColor;
34+uniform mat4 uProj;
35+uniform mat4 uView;
36+varying vec3 vColor;
37+varying vec3 vNormal;
38+void main() {
39+ gl_Position = uProj * uView * vec4(aPos, 1.0);
40+ vColor = aColor;
41+ vNormal = (uView * vec4(aNormal, 0.0)).xyz;
42+}
43+`;
44+const FS = `
45+precision mediump float;
46+varying vec3 vColor;
47+varying vec3 vNormal;
48+uniform float uFlat;
49+void main() {
50+ vec3 n = normalize(vNormal);
51+ float diff = abs(dot(n, normalize(vec3(0.3, 0.6, 1.0))));
52+ float shade = mix(0.55 + 0.45 * diff, 1.0, uFlat);
53+ gl_FragColor = vec4(vColor * shade, 1.0);
54+}
55+`;
56+
57+function perspective(fovy: number, aspect: number, near: number, far: number): Float32Array {
58+ const f = 1 / Math.tan(fovy / 2);
59+ const out = new Float32Array(16);
60+ out[0] = f / aspect;
61+ out[5] = f;
62+ out[10] = (far + near) / (near - far);
63+ out[11] = -1;
64+ out[14] = (2 * far * near) / (near - far);
65+ return out;
66+}
67+
68+function lookAt(eye: Vec3, center: Vec3, up: Vec3): Float32Array {
69+ const z = norm(sub(eye, center));
70+ const x = norm(cross(up, z));
71+ const y = cross(z, x);
72+ const out = new Float32Array(16);
73+ out[0] = x[0];
74+ out[4] = x[1];
75+ out[8] = x[2];
76+ out[1] = y[0];
77+ out[5] = y[1];
78+ out[9] = y[2];
79+ out[2] = z[0];
80+ out[6] = z[1];
81+ out[10] = z[2];
82+ out[12] = -dot(x, eye);
83+ out[13] = -dot(y, eye);
84+ out[14] = -dot(z, eye);
85+ out[15] = 1;
86+ return out;
87+}
88+const sub = (a: Vec3, b: Vec3): Vec3 => [a[0] - b[0], a[1] - b[1], a[2] - b[2]];
89+const add = (a: Vec3, b: Vec3): Vec3 => [a[0] + b[0], a[1] + b[1], a[2] + b[2]];
90+const dot = (a: Vec3, b: Vec3) => a[0] * b[0] + a[1] * b[1] + a[2] * b[2];
91+const cross = (a: Vec3, b: Vec3): Vec3 => [
92+ a[1] * b[2] - a[2] * b[1],
93+ a[2] * b[0] - a[0] * b[2],
94+ a[0] * b[1] - a[1] * b[0],
95+];
96+const norm = (a: Vec3): Vec3 => {
97+ const l = Math.hypot(a[0], a[1], a[2]) || 1;
98+ return [a[0] / l, a[1] / l, a[2] / l];
99+};
100+
101+/** What the movie recorder needs of the scene: its canvas, and a frame drawn on demand. */
102+export interface Plate3DHandle {
103+ canvas: () => HTMLCanvasElement | null;
104+ /** Draw frame k; `size` overrides the backing store, for recording at a larger size */
105+ drawFrame: (k: number, size?: { width: number; height: number }) => void;
106+}
107+
108+/**
109+ * The plate as it hangs: the thread rising from the hole near the top edge,
110+ * the hammer's tip at the strike point on the near face, the surface
111+ * displaced along the normal by the chosen frames (exaggerated by `scale`)
112+ * and coloured by the chosen quantity. The plate's x runs across the screen
113+ * and its y up it. Drag to orbit, wheel to zoom.
114+ */
115+export const Plate3DView = forwardRef<Plate3DHandle, Plate3DViewProps>(
116+ function Plate3DView(props, ref) {
117+ const {
118+ result,
119+ spec,
120+ frameIndex,
121+ frames,
122+ colorFrames,
123+ colorRange,
124+ colormap = "bwr",
125+ scale,
126+ width = 460,
127+ height = 340,
128+ } = props;
129+ const canvasRef = useRef<HTMLCanvasElement>(null);
130+ const glRef = useRef<{
131+ gl: WebGLRenderingContext;
132+ prog: WebGLProgram;
133+ buffers: { pos: WebGLBuffer; nrm: WebGLBuffer; col: WebGLBuffer; idx: WebGLBuffer };
134+ lines: { pos: WebGLBuffer; col: WebGLBuffer; nrm: WebGLBuffer };
135+ loc: Record<string, number | WebGLUniformLocation | null>;
136+ } | null>(null);
137+ const [cam, setCam] = useState({ az: 0.5, el: 0.3, zoom: 1 });
138+ const drag = useRef<{ x: number; y: number } | null>(null);
139+
140+ const nx = result.grid.x.length;
141+ const ny = result.grid.y.length;
142+ const W = spec.plate.width;
143+ const H = spec.plate.height;
144+ const size = Math.max(W, H);
145+
146+ const mesh = useMemo(() => {
147+ const idx: number[] = [];
148+ for (let j = 0; j < nx - 1; j++)
149+ for (let i = 0; i < ny - 1; i++) {
150+ const a = i + j * ny;
151+ const b = i + 1 + j * ny;
152+ const c = i + (j + 1) * ny;
153+ const d = i + 1 + (j + 1) * ny;
154+ idx.push(a, b, c, b, d, c);
155+ }
156+ return { index: new Uint16Array(idx) };
157+ }, [nx, ny]);
158+
159+ useEffect(() => {
160+ const canvas = canvasRef.current;
161+ if (!canvas) return;
162+ const gl = canvas.getContext("webgl", { antialias: true });
163+ if (!gl) return;
164+ const compile = (type: number, src: string) => {
165+ const sh = gl.createShader(type)!;
166+ gl.shaderSource(sh, src);
167+ gl.compileShader(sh);
168+ return sh;
169+ };
170+ const prog = gl.createProgram()!;
171+ gl.attachShader(prog, compile(gl.VERTEX_SHADER, VS));
172+ gl.attachShader(prog, compile(gl.FRAGMENT_SHADER, FS));
173+ gl.linkProgram(prog);
174+ const mk = () => gl.createBuffer()!;
175+ glRef.current = {
176+ gl,
177+ prog,
178+ buffers: { pos: mk(), nrm: mk(), col: mk(), idx: mk() },
179+ lines: { pos: mk(), col: mk(), nrm: mk() },
180+ loc: {
181+ aPos: gl.getAttribLocation(prog, "aPos"),
182+ aNormal: gl.getAttribLocation(prog, "aNormal"),
183+ aColor: gl.getAttribLocation(prog, "aColor"),
184+ uProj: gl.getUniformLocation(prog, "uProj"),
185+ uView: gl.getUniformLocation(prog, "uView"),
186+ uFlat: gl.getUniformLocation(prog, "uFlat"),
187+ },
188+ };
189+ return () => {
190+ glRef.current = null;
191+ };
192+ }, []);
193+
194+ const draw = (kIndex: number, out?: { width: number; height: number }) => {
195+ const r = glRef.current;
196+ const canvas = canvasRef.current;
197+ if (!r || !canvas) return;
198+ const { gl, prog, buffers, lines, loc } = r;
199+ const vw = out?.width ?? width;
200+ const vh = out?.height ?? height;
201+ const dpr = out ? 1 : Math.min(2, window.devicePixelRatio || 1);
202+ canvas.width = Math.round(vw * dpr);
203+ canvas.height = Math.round(vh * dpr);
204+ gl.viewport(0, 0, canvas.width, canvas.height);
205+
206+ // ---- node positions: plate x across, y up, the normal towards the viewer
207+ const N = nx * ny;
208+ const kf = frames ? Math.min(kIndex, frames.nf - 1) : -1;
209+ const pos = new Float32Array(3 * N);
210+ const labPos: Vec3[] = new Array(N);
211+ for (let j = 0; j < nx; j++)
212+ for (let i = 0; i < ny; i++) {
213+ const n = i + j * ny;
214+ const f = frames && kf >= 0 ? frames.data[n + kf * N] : 0;
215+ const lp: Vec3 = [
216+ result.grid.x[j],
217+ result.grid.y[i],
218+ scale * (Number.isFinite(f) ? f : 0),
219+ ];
220+ labPos[n] = lp;
221+ pos[3 * n] = lp[0];
222+ pos[3 * n + 1] = lp[1];
223+ pos[3 * n + 2] = lp[2];
224+ }
225+ const nrm = new Float32Array(3 * N);
226+ for (let j = 0; j < nx; j++)
227+ for (let i = 0; i < ny; i++) {
228+ const n = i + j * ny;
229+ const jl = Math.max(0, j - 1);
230+ const jr = Math.min(nx - 1, j + 1);
231+ const il = Math.max(0, i - 1);
232+ const ir = Math.min(ny - 1, i + 1);
233+ const tx = sub(labPos[i + jr * ny], labPos[i + jl * ny]);
234+ const ty = sub(labPos[ir + j * ny], labPos[il + j * ny]);
235+ const nn = norm(cross(tx, ty));
236+ nrm[3 * n] = nn[0];
237+ nrm[3 * n + 1] = nn[1];
238+ nrm[3 * n + 2] = nn[2];
239+ }
240+ const col = new Float32Array(3 * N);
241+ const table = colormapTable(getColormap(colormap));
242+ const range = colorRange ?? { min: -1, max: 1 };
243+ const span = range.max - range.min || 1;
244+ const kc = colorFrames ? Math.min(kIndex, colorFrames.nf - 1) : -1;
245+ for (let n = 0; n < N; n++) {
246+ let v = colorFrames && kc >= 0 ? colorFrames.data[n + kc * N] : NaN;
247+ if (!Number.isFinite(v)) v = 0.5 * (range.min + range.max);
248+ let t = (v - range.min) / span;
249+ t = t < 0 ? 0 : t > 1 ? 1 : t;
250+ const k = Math.round(t * 255) * 3;
251+ col[3 * n] = table[k] / 255;
252+ col[3 * n + 1] = table[k + 1] / 255;
253+ col[3 * n + 2] = table[k + 2] / 255;
254+ }
255+
256+ // ---- the thread, the hammer, the outline
257+ const L: number[] = [];
258+ const LC: number[] = [];
259+ const line = (a: Vec3, b: Vec3, rgb: Vec3) => {
260+ L.push(...a, ...b);
261+ LC.push(...rgb, ...rgb);
262+ };
263+ const dark: Vec3 = [0.15, 0.15, 0.18];
264+ const red: Vec3 = [0.82, 0.29, 0.36];
265+ const pale: Vec3 = [0.75, 0.75, 0.78];
266+ const grey: Vec3 = [0.4, 0.4, 0.45];
267+ const circle = (centre: Vec3, r: number, rgb: Vec3, axis: "z" | "y" = "z") => {
268+ const n = 24;
269+ for (let k = 0; k < n; k++) {
270+ const a0 = (2 * Math.PI * k) / n;
271+ const a1 = (2 * Math.PI * (k + 1)) / n;
272+ const p = (a: number): Vec3 =>
273+ axis === "z"
274+ ? [centre[0] + r * Math.cos(a), centre[1] + r * Math.sin(a), centre[2]]
275+ : [centre[0] + r * Math.cos(a), centre[1], centre[2] + r * Math.sin(a)];
276+ line(p(a0), p(a1), rgb);
277+ }
278+ };
279+ const hole = holePosition(spec);
280+ const hookUp = Math.min(spec.thread.length, 0.7 * size);
281+ line([hole.x, hole.y, 0], [hole.x, hole.y + hookUp, 0], dark);
282+ line(
283+ [hole.x - 0.03 * size, hole.y + hookUp, 0],
284+ [hole.x + 0.03 * size, hole.y + hookUp, 0],
285+ dark,
286+ );
287+ circle([hole.x, hole.y, 0], spec.hole.diameter / 2, dark);
288+ // the hammer: its tip resting on the near face, its shank along the normal
289+ const R = spec.hammer.tip_radius;
290+ const s: Vec3 = [spec.strike.x, spec.strike.y, 0];
291+ circle([s[0], s[1], R], R, red);
292+ circle([s[0], s[1], R], R, red, "y");
293+ line([s[0], s[1], 2 * R], [s[0], s[1], 2 * R + 0.35 * size], red);
294+ // the probe
295+ line(
296+ [spec.probe.x - 0.02 * size, spec.probe.y, 0],
297+ [spec.probe.x + 0.02 * size, spec.probe.y, 0],
298+ grey,
299+ );
300+ line(
301+ [spec.probe.x, spec.probe.y - 0.02 * size, 0],
302+ [spec.probe.x, spec.probe.y + 0.02 * size, 0],
303+ grey,
304+ );
305+ // outline of the undeformed plate, faint
306+ line([-W / 2, -H / 2, 0], [W / 2, -H / 2, 0], pale);
307+ line([W / 2, -H / 2, 0], [W / 2, H / 2, 0], pale);
308+ line([W / 2, H / 2, 0], [-W / 2, H / 2, 0], pale);
309+ line([-W / 2, H / 2, 0], [-W / 2, -H / 2, 0], pale);
310+
311+ // ---- camera
312+ const centre: Vec3 = [0, 0.08 * size, 0];
313+ const dist = (2.4 * size) / cam.zoom;
314+ const eye: Vec3 = add(centre, [
315+ dist * Math.cos(cam.el) * Math.sin(cam.az),
316+ dist * Math.sin(cam.el),
317+ dist * Math.cos(cam.el) * Math.cos(cam.az),
318+ ]);
319+ const view = lookAt(eye, centre, [0, 1, 0]);
320+ const proj = perspective(0.6, vw / vh, 0.01 * size, 20 * size);
321+
322+ gl.clearColor(0.97, 0.97, 0.975, 1);
323+ gl.enable(gl.DEPTH_TEST);
324+ gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
325+ gl.useProgram(prog);
326+ gl.uniformMatrix4fv(loc.uProj as WebGLUniformLocation, false, proj);
327+ gl.uniformMatrix4fv(loc.uView as WebGLUniformLocation, false, view);
328+ const attr = (buf: WebGLBuffer, data: Float32Array, a: number) => {
329+ gl.bindBuffer(gl.ARRAY_BUFFER, buf);
330+ gl.bufferData(gl.ARRAY_BUFFER, data, gl.DYNAMIC_DRAW);
331+ gl.enableVertexAttribArray(a);
332+ gl.vertexAttribPointer(a, 3, gl.FLOAT, false, 0, 0);
333+ };
334+ gl.uniform1f(loc.uFlat as WebGLUniformLocation, 0);
335+ attr(buffers.pos, pos, loc.aPos as number);
336+ attr(buffers.nrm, nrm, loc.aNormal as number);
337+ attr(buffers.col, col, loc.aColor as number);
338+ gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, buffers.idx);
339+ gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, mesh.index, gl.STATIC_DRAW);
340+ gl.drawElements(gl.TRIANGLES, mesh.index.length, gl.UNSIGNED_SHORT, 0);
341+ gl.uniform1f(loc.uFlat as WebGLUniformLocation, 1);
342+ const lp = new Float32Array(L);
343+ attr(lines.pos, lp, loc.aPos as number);
344+ attr(lines.nrm, new Float32Array(L.length), loc.aNormal as number);
345+ attr(lines.col, new Float32Array(LC), loc.aColor as number);
346+ gl.drawArrays(gl.LINES, 0, lp.length / 3);
347+ };
348+
349+ const drawRef = useRef(draw);
350+ drawRef.current = draw;
351+ useImperativeHandle(
352+ ref,
353+ () => ({
354+ canvas: () => canvasRef.current,
355+ drawFrame: (k: number, out?: { width: number; height: number }) => drawRef.current(k, out),
356+ }),
357+ [],
358+ );
359+
360+ useEffect(() => {
361+ drawRef.current(frameIndex);
362+ }, [
363+ result,
364+ spec,
365+ frameIndex,
366+ frames,
367+ colorFrames,
368+ colorRange,
369+ colormap,
370+ scale,
371+ width,
372+ height,
373+ cam,
374+ mesh,
375+ nx,
376+ ny,
377+ W,
378+ H,
379+ size,
380+ ]);
381+
382+ return (
383+ <canvas
384+ ref={canvasRef}
385+ style={{ width, height, display: "block", cursor: "grab", borderRadius: 4 }}
386+ onPointerDown={(e) => {
387+ drag.current = { x: e.clientX, y: e.clientY };
388+ (e.target as HTMLElement).setPointerCapture(e.pointerId);
389+ }}
390+ onPointerMove={(e) => {
391+ if (!drag.current) return;
392+ const dx = e.clientX - drag.current.x;
393+ const dy = e.clientY - drag.current.y;
394+ drag.current = { x: e.clientX, y: e.clientY };
395+ setCam((c) => ({
396+ ...c,
397+ az: c.az - dx * 0.01,
398+ el: Math.max(-1.4, Math.min(1.4, c.el + dy * 0.01)),
399+ }));
400+ }}
401+ onPointerUp={() => {
402+ drag.current = null;
403+ }}
404+ onWheel={(e) => {
405+ e.preventDefault();
406+ setCam((c) => ({
407+ ...c,
408+ zoom: Math.max(0.3, Math.min(5, c.zoom * Math.exp(-e.deltaY * 0.001))),
409+ }));
410+ }}
411+ aria-label="the plate on its thread"
412+ />
413+ );
414+ },
415+);
packages/components/src/status/EquationsView.tsxadded+23−0View file
@@ -0,0 +1,23 @@
1+import { useMemo } from "react";
2+import katex from "katex";
3+import "katex/dist/katex.min.css";
4+import type { Equation } from "@plate-dyn/core";
5+
6+/** Renders a method's equations with their captions. */
7+export function EquationsView({ equations }: { equations: Equation[] }) {
8+ const html = useMemo(
9+ () =>
10+ equations.map((e) => katex.renderToString(e.tex, { displayMode: true, throwOnError: false })),
11+ [equations],
12+ );
13+ return (
14+ <div className="pl-equations">
15+ {equations.map((e, i) => (
16+ <div className="pl-equation" key={i}>
17+ <div dangerouslySetInnerHTML={{ __html: html[i] }} />
18+ <div className="pl-equation-caption">{e.caption}</div>
19+ </div>
20+ ))}
21+ </div>
22+ );
23+}
packages/components/src/status/RunStatus.tsxadded+156−0View file
@@ -0,0 +1,156 @@
1+import type { BackendInfo, MethodDefinition, PlateResult } from "@plate-dyn/core";
2+
3+export type StagedStatus =
4+ | { status: "idle" }
5+ | { status: "running"; stage: "modes" | "strike" | "script"; startedAt: number; output: string[] }
6+ | {
7+ status: "done";
8+ output: string[];
9+ wallMs: number;
10+ /** Wall time of the modes stage in this run, or null when the modes were reused */
11+ modesMs: number | null;
12+ strikeMs: number;
13+ backend: BackendInfo;
14+ }
15+ | {
16+ status: "error";
17+ error: string;
18+ output: string[];
19+ wallMs: number;
20+ backend: BackendInfo;
21+ cancelled?: boolean;
22+ };
23+
24+export interface RunStatusProps {
25+ state: StagedStatus;
26+ result?: PlateResult | null;
27+ method?: MethodDefinition;
28+ backend?: BackendInfo | null;
29+ /** Predicted wall times for the next run [ms], shown while idle */
30+ estimate?: { modesMs: number | null; strikeMs: number };
31+ onCancel?: () => void;
32+}
33+
34+/** Makes the execution visible: what ran, where, how big it was and how long it took. */
35+export function RunStatus({ state, result, method, backend, estimate, onCancel }: RunStatusProps) {
36+ const be = state.status === "done" || state.status === "error" ? state.backend : backend;
37+ return (
38+ <div className="pl-status">
39+ <div className="pl-row">
40+ {state.status === "idle" && <span className="pl-badge pl-badge-info">ready</span>}
41+ {state.status === "running" && (
42+ <>
43+ <span className="pl-badge pl-badge-info">
44+ {state.stage === "modes"
45+ ? "solving the modes"
46+ : state.stage === "strike"
47+ ? "striking"
48+ : "running the script"}
49+ </span>
50+ {onCancel && (
51+ <button className="pl-btn pl-btn-danger" onClick={onCancel}>
52+ Cancel
53+ </button>
54+ )}
55+ </>
56+ )}
57+ {state.status === "done" && <span className="pl-badge pl-badge-ok">finished</span>}
58+ {state.status === "error" && (
59+ <span className="pl-badge pl-badge-error">{state.cancelled ? "cancelled" : "error"}</span>
60+ )}
61+ {method && <span className="pl-muted">{method.name}</span>}
62+ </div>
63+ {state.status === "running" && (
64+ <div className="pl-progress">
65+ <div />
66+ </div>
67+ )}
68+ {state.status === "error" && <div className="pl-error">{state.error}</div>}
69+ <table>
70+ <tbody>
71+ <tr>
72+ <td>Backend</td>
73+ <td>
74+ {be
75+ ? `${be.engine} ${be.version}, ${be.optimization === "1" ? "JS JIT (opt 1)" : "interpreter (opt 0)"}, ${be.host === "web-worker" ? "Web Worker in this browser" : "Node.js"}, double precision`
76+ : "numbl (not yet started)"}
77+ </td>
78+ </tr>
79+ <tr>
80+ <td>Wall time</td>
81+ <td>
82+ {state.status === "done" ? (
83+ <>
84+ {fmtMs(state.wallMs)} in total:{" "}
85+ {state.modesMs === null
86+ ? "the modes were kept from an earlier run"
87+ : `${fmtMs(state.modesMs)} for the modes`}
88+ , {fmtMs(state.strikeMs)} for the strike and the synthesis
89+ </>
90+ ) : state.status === "running" ? (
91+ "…"
92+ ) : estimate ? (
93+ `about ${estimate.modesMs === null ? "" : `${fmtMs(estimate.modesMs)} for the modes and `}${fmtMs(estimate.strikeMs)} for the strike expected`
94+ ) : (
95+ "–"
96+ )}
97+ </td>
98+ </tr>
99+ {result && (
100+ <tr>
101+ <td>Problem size</td>
102+ <td>
103+ {result.info.size_note}; {result.info.nmodes} modes kept ({result.info.n_audible}{" "}
104+ audible below {(result.info.f_max / 1000).toFixed(1)} kHz
105+ {result.info.n_rigid > 0 ? `, ${result.info.n_rigid} rigid-body` : ""}),{" "}
106+ {result.info.nsamples.toLocaleString()} samples at {result.info.fs} Hz,{" "}
107+ {result.frames.t.length} frames
108+ </td>
109+ </tr>
110+ )}
111+ {result && (
112+ <tr>
113+ <td>Inside the solver</td>
114+ <td>
115+ {fmtMs(result.info.modes_wall_s * 1000)} for the modes (by tic/toc, when they were
116+ computed), {fmtMs(result.info.strike_wall_s * 1000)} for the strike
117+ {result.info.extra.eig_iterations !== undefined
118+ ? `; eigensolver ${result.info.extra.eig_iterations} iterations`
119+ : ""}
120+ </td>
121+ </tr>
122+ )}
123+ </tbody>
124+ </table>
125+ {result && result.info.notes.length > 0 && (
126+ <div>
127+ {result.info.notes.map((n, i) => (
128+ <div className="pl-note" key={i}>
129+ {n}
130+ </div>
131+ ))}
132+ </div>
133+ )}
134+ {(state.status === "running" || state.status === "done" || state.status === "error") &&
135+ state.output.length > 0 && (
136+ <details>
137+ <summary className="pl-small pl-muted">
138+ Script output ({state.output.length} lines)
139+ </summary>
140+ <div className="pl-log">{state.output.join("")}</div>
141+ </details>
142+ )}
143+ </div>
144+ );
145+}
146+
147+export function fmtMs(ms: number): string {
148+ if (ms < 1000) return `${ms.toFixed(0)} ms`;
149+ return `${(ms / 1000).toFixed(2)} s`;
150+}
151+
152+export function fmtTime(s: number): string {
153+ if (s >= 1) return `${s.toPrecision(3)} s`;
154+ if (s >= 1e-3) return `${(s * 1e3).toPrecision(3)} ms`;
155+ return `${(s * 1e6).toPrecision(3)} µs`;
156+}
packages/components/src/status/ValidityNotes.tsxadded+32−0View file
@@ -0,0 +1,32 @@
1+import type { MethodDefinition, PlateResult } from "@plate-dyn/core";
2+
3+/** What the method idealizes in general, and what this particular run approximated. */
4+export function ValidityNotes({
5+ method,
6+ result,
7+}: {
8+ method: MethodDefinition;
9+ result?: PlateResult | null;
10+}) {
11+ return (
12+ <div className="pl-small">
13+ <ul style={{ margin: "4px 0", paddingLeft: 18 }}>
14+ {method.validity.map((v, i) => (
15+ <li key={i}>{v}</li>
16+ ))}
17+ </ul>
18+ {result && result.info.notes.length > 0 && (
19+ <>
20+ <div className="pl-muted" style={{ marginTop: 6 }}>
21+ This run:
22+ </div>
23+ <ul style={{ margin: "4px 0", paddingLeft: 18 }}>
24+ {result.info.notes.map((n, i) => (
25+ <li key={i}>{n}</li>
26+ ))}
27+ </ul>
28+ </>
29+ )}
30+ </div>
31+ );
32+}
packages/components/src/styles.cssadded+481−0View file
@@ -0,0 +1,481 @@
1+/* Plate lab components. Prefix: pl- */
2+:root {
3+ --pl-bg: #ffffff;
4+ --pl-panel: #f6f7f9;
5+ --pl-border: #d9dde3;
6+ --pl-text: #1c2430;
7+ --pl-muted: #5b6573;
8+ --pl-accent: #1f5fa8;
9+ --pl-warn: #a35a00;
10+ --pl-warn-bg: #fff4e0;
11+ --pl-error: #a11d2b;
12+ --pl-error-bg: #fde9eb;
13+ --pl-ok: #1f7a3a;
14+ --pl-mono: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
15+ --pl-font: system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
16+}
17+.pl-panel {
18+ background: var(--pl-panel);
19+ border: 1px solid var(--pl-border);
20+ border-radius: 6px;
21+ padding: 10px 12px;
22+}
23+.pl-panel h3 {
24+ margin: 0 0 8px;
25+ font-size: 13px;
26+ text-transform: uppercase;
27+ letter-spacing: 0.04em;
28+ color: var(--pl-muted);
29+}
30+.pl-muted {
31+ color: var(--pl-muted);
32+}
33+.pl-small {
34+ font-size: 12px;
35+}
36+.pl-mono {
37+ font-family: var(--pl-mono);
38+}
39+.pl-row {
40+ display: flex;
41+ gap: 8px;
42+ align-items: center;
43+ flex-wrap: wrap;
44+}
45+.pl-badge {
46+ display: inline-block;
47+ padding: 1px 7px;
48+ border-radius: 10px;
49+ font-size: 11px;
50+ font-weight: 600;
51+}
52+.pl-badge-ok {
53+ background: #e3f4e8;
54+ color: var(--pl-ok);
55+}
56+.pl-badge-warn {
57+ background: var(--pl-warn-bg);
58+ color: var(--pl-warn);
59+}
60+.pl-badge-error {
61+ background: var(--pl-error-bg);
62+ color: var(--pl-error);
63+}
64+.pl-badge-info {
65+ background: #e6eef9;
66+ color: var(--pl-accent);
67+}
68+.pl-note {
69+ padding: 6px 8px;
70+ border-left: 3px solid var(--pl-warn);
71+ background: var(--pl-warn-bg);
72+ font-size: 12px;
73+ margin: 4px 0;
74+}
75+.pl-error {
76+ padding: 8px 10px;
77+ border-left: 3px solid var(--pl-error);
78+ background: var(--pl-error-bg);
79+ font-family: var(--pl-mono);
80+ font-size: 12px;
81+ white-space: pre-wrap;
82+}
83+button.pl-btn {
84+ font: inherit;
85+ padding: 5px 12px;
86+ border-radius: 5px;
87+ border: 1px solid var(--pl-border);
88+ background: #fff;
89+ cursor: pointer;
90+ color: var(--pl-text);
91+}
92+button.pl-btn:hover {
93+ border-color: var(--pl-accent);
94+}
95+button.pl-btn:disabled {
96+ opacity: 0.5;
97+ cursor: default;
98+}
99+button.pl-btn-primary {
100+ background: var(--pl-accent);
101+ color: #fff;
102+ border-color: var(--pl-accent);
103+}
104+button.pl-btn-danger {
105+ background: #fff;
106+ color: var(--pl-error);
107+ border-color: var(--pl-error);
108+}
109+
110+/* Parameter panel */
111+.pl-params {
112+ display: flex;
113+ flex-direction: column;
114+ gap: 10px;
115+}
116+.pl-group {
117+ border: 1px solid var(--pl-border);
118+ border-radius: 6px;
119+ padding: 8px 10px;
120+ background: #fff;
121+}
122+.pl-group > summary,
123+.pl-group-title {
124+ font-weight: 600;
125+ font-size: 13px;
126+ cursor: pointer;
127+ margin-bottom: 6px;
128+}
129+.pl-field {
130+ display: grid;
131+ grid-template-columns: 1fr 100px 46px;
132+ gap: 6px;
133+ align-items: center;
134+ font-size: 12px;
135+ margin: 3px 0;
136+}
137+.pl-field label {
138+ color: var(--pl-text);
139+}
140+.pl-field input,
141+.pl-field select,
142+.pl-select {
143+ font: inherit;
144+ font-size: 12px;
145+ padding: 3px 5px;
146+ border: 1px solid var(--pl-border);
147+ border-radius: 4px;
148+ width: 100%;
149+ box-sizing: border-box;
150+}
151+.pl-field input.pl-invalid {
152+ border-color: var(--pl-error);
153+ background: var(--pl-error-bg);
154+}
155+.pl-field .pl-unit {
156+ color: var(--pl-muted);
157+ font-size: 11px;
158+}
159+.pl-field-wide {
160+ grid-template-columns: 1fr 2fr;
161+}
162+.pl-slider {
163+ display: grid;
164+ grid-template-columns: 1fr 70px;
165+ gap: 8px;
166+ align-items: center;
167+ font-size: 12px;
168+}
169+.pl-slider input[type="range"] {
170+ width: 100%;
171+}
172+.pl-issue {
173+ font-size: 11px;
174+ color: var(--pl-error);
175+ margin: 2px 0 4px;
176+}
177+.pl-issue-warning {
178+ color: var(--pl-warn);
179+}
180+.pl-disabled {
181+ opacity: 0.55;
182+}
183+.pl-gpu {
184+ margin-top: 8px;
185+ padding-top: 6px;
186+ border-top: 1px dashed var(--pl-border);
187+}
188+.pl-check {
189+ display: flex;
190+ gap: 6px;
191+ align-items: center;
192+ font-size: 12px;
193+ cursor: pointer;
194+}
195+.pl-check input {
196+ margin: 0;
197+}
198+
199+/* Code view */
200+.pl-code {
201+ display: flex;
202+ flex-direction: column;
203+ border: 1px solid var(--pl-border);
204+ border-radius: 6px;
205+ overflow: hidden;
206+ background: #fff;
207+ min-height: 0;
208+}
209+.pl-code-tabs {
210+ display: flex;
211+ flex-wrap: wrap;
212+ gap: 2px;
213+ padding: 4px 6px;
214+ background: var(--pl-panel);
215+ border-bottom: 1px solid var(--pl-border);
216+}
217+.pl-code-tab {
218+ font: inherit;
219+ font-size: 12px;
220+ font-family: var(--pl-mono);
221+ padding: 3px 8px;
222+ border: 1px solid transparent;
223+ background: transparent;
224+ border-radius: 4px;
225+ cursor: pointer;
226+ color: var(--pl-muted);
227+}
228+.pl-code-tab.pl-active {
229+ background: #fff;
230+ border-color: var(--pl-border);
231+ color: var(--pl-text);
232+}
233+.pl-code-tab.pl-modified::after {
234+ content: " *";
235+ color: var(--pl-warn);
236+}
237+.pl-code-toolbar {
238+ display: flex;
239+ gap: 8px;
240+ align-items: center;
241+ padding: 4px 8px;
242+ font-size: 12px;
243+ border-bottom: 1px solid var(--pl-border);
244+ color: var(--pl-muted);
245+}
246+.pl-code-editor {
247+ flex: 1;
248+ min-height: 200px;
249+ overflow: auto;
250+}
251+.pl-code-editor .cm-editor {
252+ height: 100%;
253+ font-size: 12.5px;
254+}
255+.pl-code-editor .cm-scroller {
256+ font-family: var(--pl-mono);
257+}
258+
259+/* Field view */
260+.pl-fieldview {
261+ display: flex;
262+ flex-direction: column;
263+ gap: 6px;
264+}
265+.pl-fieldview-head {
266+ display: flex;
267+ justify-content: space-between;
268+ align-items: baseline;
269+ font-size: 12px;
270+}
271+.pl-fieldview-body {
272+ display: grid;
273+ grid-template-columns: 1fr 56px;
274+ gap: 8px;
275+ align-items: start;
276+}
277+.pl-fieldview canvas {
278+ width: 100%;
279+ height: auto;
280+ display: block;
281+ image-rendering: auto;
282+ border: 1px solid var(--pl-border);
283+ background: #fff;
284+}
285+.pl-colorbar {
286+ display: grid;
287+ grid-template-rows: auto minmax(0, 1fr) auto;
288+ font-size: 10px;
289+ color: var(--pl-muted);
290+ text-align: left;
291+}
292+.pl-colorbar canvas {
293+ width: 14px;
294+ height: 100%;
295+ min-height: 0;
296+ border: 1px solid var(--pl-border);
297+}
298+.pl-player {
299+ display: grid;
300+ grid-template-columns: auto 1fr auto auto;
301+ gap: 8px;
302+ align-items: center;
303+ font-size: 12px;
304+}
305+.pl-player input[type="range"] {
306+ width: 100%;
307+}
308+
309+/* Plots */
310+.pl-plot {
311+ position: relative;
312+}
313+.pl-plot canvas {
314+ width: 100%;
315+ height: auto;
316+ display: block;
317+}
318+.pl-plot-title {
319+ font-size: 12px;
320+ font-weight: 600;
321+ margin-bottom: 2px;
322+}
323+.pl-legend {
324+ display: flex;
325+ gap: 12px;
326+ font-size: 11px;
327+ color: var(--pl-muted);
328+ flex-wrap: wrap;
329+}
330+.pl-legend span::before {
331+ content: "";
332+ display: inline-block;
333+ width: 12px;
334+ height: 3px;
335+ margin-right: 4px;
336+ vertical-align: middle;
337+ background: var(--c);
338+}
339+
340+/* Equations */
341+.pl-equations {
342+ display: flex;
343+ flex-direction: column;
344+ gap: 10px;
345+}
346+.pl-equation {
347+ font-size: 13px;
348+}
349+.pl-equation .katex-display {
350+ margin: 4px 0;
351+ overflow-x: auto;
352+ overflow-y: hidden;
353+}
354+.pl-equation-caption {
355+ font-size: 12px;
356+ color: var(--pl-muted);
357+}
358+
359+/* Status */
360+.pl-status {
361+ font-size: 12px;
362+ display: flex;
363+ flex-direction: column;
364+ gap: 6px;
365+}
366+.pl-status table {
367+ border-collapse: collapse;
368+}
369+.pl-status td {
370+ padding: 1px 10px 1px 0;
371+ vertical-align: top;
372+}
373+.pl-status td:first-child {
374+ color: var(--pl-muted);
375+}
376+.pl-log {
377+ font-family: var(--pl-mono);
378+ font-size: 11px;
379+ white-space: pre-wrap;
380+ max-height: 120px;
381+ overflow: auto;
382+ background: #fff;
383+ border: 1px solid var(--pl-border);
384+ padding: 4px 6px;
385+}
386+.pl-progress {
387+ height: 4px;
388+ background: var(--pl-border);
389+ border-radius: 2px;
390+ overflow: hidden;
391+}
392+.pl-progress > div {
393+ height: 100%;
394+ background: var(--pl-accent);
395+ animation: pl-indeterminate 1.2s infinite ease-in-out;
396+ width: 30%;
397+}
398+@keyframes pl-indeterminate {
399+ 0% {
400+ margin-left: -30%;
401+ }
402+ 100% {
403+ margin-left: 100%;
404+ }
405+}
406+
407+/* Sub-groups (pads) */
408+.pl-subgroup {
409+ border-top: 1px dashed var(--pl-border);
410+ margin-top: 6px;
411+ padding-top: 4px;
412+}
413+.pl-linkbtn:disabled {
414+ opacity: 0.4;
415+ cursor: default;
416+}
417+
418+/* Movie export */
419+.pl-movie {
420+ display: flex;
421+ gap: 10px;
422+ align-items: center;
423+ flex-wrap: wrap;
424+ font-size: 12px;
425+}
426+.pl-movie .pl-bar {
427+ width: 130px;
428+ height: 7px;
429+ border: 1px solid var(--pl-border);
430+ border-radius: 4px;
431+ overflow: hidden;
432+ background: #fff;
433+}
434+.pl-movie .pl-bar > div {
435+ height: 100%;
436+ background: var(--pl-accent);
437+ transition: width 0.1s linear;
438+}
439+
440+/* 3D scene */
441+.pl-scene {
442+ display: flex;
443+ flex-direction: column;
444+ gap: 6px;
445+}
446+.pl-scene-controls {
447+ display: flex;
448+ gap: 12px;
449+ align-items: center;
450+ flex-wrap: wrap;
451+ font-size: 12px;
452+}
453+.pl-scene-controls label {
454+ display: flex;
455+ gap: 6px;
456+ align-items: center;
457+}
458+.pl-scene-controls input[type="range"] {
459+ width: 110px;
460+}
461+
462+/* Plot interaction */
463+.pl-linkbtn {
464+ font: inherit;
465+ font-size: 11px;
466+ font-weight: 400;
467+ margin-left: 8px;
468+ border: 0;
469+ background: none;
470+ color: var(--pl-accent);
471+ cursor: pointer;
472+ text-decoration: underline;
473+ padding: 0;
474+}
475+.pl-hint {
476+ margin-left: auto;
477+ font-style: italic;
478+}
479+.pl-legend .pl-hint::before {
480+ display: none;
481+}
packages/components/src/video/MovieExport.tsxadded+177−0View file
@@ -0,0 +1,177 @@
1+import { useMemo, useRef, useState } from "react";
2+import type { AudioPrep } from "@plate-dyn/core";
3+import {
4+ movieSpan,
5+ movieSupport,
6+ recordMovie,
7+ saveMovie,
8+ type MovieOverlay,
9+ type MovieResult,
10+ type MovieSource,
11+} from "./recordMovie";
12+
13+export interface MovieExportProps {
14+ source: MovieSource;
15+ /** Simulated time of every frame [s] */
16+ frameTimes: Float64Array;
17+ audio?: AudioPrep;
18+ overlay?: MovieOverlay;
19+ /** Height of the scene as a fraction of the width */
20+ aspect?: number;
21+ width?: number;
22+ /** Base name of the downloaded file, without extension */
23+ filename?: string;
24+ /** Frame to leave on screen when the recording ends */
25+ restoreFrame?: number;
26+ /** Told when the recording starts and stops, so the caller can pause its player */
27+ onRecording?: (recording: boolean) => void;
28+}
29+
30+const SPEEDS = [
31+ { value: 1, label: "real time, with the sound" },
32+ { value: 0.5, label: "half speed, with the sound" },
33+ { value: 0.25, label: "quarter speed, with the sound" },
34+ { value: 0, label: "slow motion of the frames, no sound" },
35+];
36+
37+/** Frames per second of the silent slow-motion movie */
38+const SLOW_FPS = 30;
39+
40+type State =
41+ | { status: "idle" }
42+ | { status: "recording"; fraction: number }
43+ | { status: "done"; result: MovieResult; speed: number }
44+ | { status: "error"; message: string };
45+
46+/**
47+ * Records the scene as it stands, at its present orientation and colouring,
48+ * with the radiated sound under it. The recording is real time, so it takes as
49+ * long as the movie lasts; below real time the sound is slowed with the
50+ * picture and drops in pitch, as a tape would.
51+ */
52+export function MovieExport(props: MovieExportProps) {
53+ const { frameTimes, audio, overlay, aspect, width, filename = "plate", onRecording } = props;
54+ const support = useMemo(() => movieSupport(), []);
55+ const [speed, setSpeed] = useState(1);
56+ const [state, setState] = useState<State>({ status: "idle" });
57+ const sourceRef = useRef(props.source);
58+ sourceRef.current = props.source;
59+ const restoreRef = useRef(props.restoreFrame ?? 0);
60+ restoreRef.current = props.restoreFrame ?? 0;
61+ const abort = useRef<AbortController | null>(null);
62+
63+ // Slow motion plays the frames one after another at SLOW_FPS without the
64+ // sound, which could not be slowed a thousandfold; the strike transient of a
65+ // few milliseconds then lasts a few seconds on screen.
66+ const slow = speed === 0;
67+ const frameSpan = movieSpan(frameTimes);
68+ const slowSpeed = frameSpan > 0 ? frameSpan / (frameTimes.length / SLOW_FPS) : 1;
69+ const effectiveSpeed = slow ? slowSpeed : speed;
70+ const effectiveAudio = slow ? undefined : audio;
71+ const span = useMemo(() => movieSpan(frameTimes, effectiveAudio), [frameTimes, effectiveAudio]);
72+ const length = span / effectiveSpeed;
73+ const recording = state.status === "recording";
74+ // The recorder draws on the display's own refresh, so a run whose frames
75+ // come faster than that loses some of them; a slower speed spreads them out.
76+ const fps = length > 0 ? frameTimes.length / length : 0;
77+ const crowded = fps > 60;
78+
79+ const start = async () => {
80+ if (recording || support.status !== "available") return;
81+ const controller = new AbortController();
82+ abort.current = controller;
83+ setState({ status: "recording", fraction: 0 });
84+ onRecording?.(true);
85+ try {
86+ const result = await recordMovie({
87+ source: sourceRef.current,
88+ frameTimes,
89+ audio: effectiveAudio,
90+ overlay,
91+ aspect,
92+ width,
93+ speed: effectiveSpeed,
94+ restoreFrame: restoreRef.current,
95+ signal: controller.signal,
96+ onProgress: (fraction) => setState({ status: "recording", fraction }),
97+ });
98+ saveMovie(
99+ result,
100+ `${filename}-${slow ? "slow-motion" : speed === 1 ? "realtime" : `${speed}x`}`,
101+ );
102+ setState({ status: "done", result, speed });
103+ } catch (e) {
104+ setState({ status: "error", message: e instanceof Error ? e.message : String(e) });
105+ } finally {
106+ abort.current = null;
107+ onRecording?.(false);
108+ }
109+ };
110+
111+ return (
112+ <div className="pl-movie">
113+ <button
114+ className="pl-btn"
115+ onClick={() => void start()}
116+ disabled={recording || support.status !== "available" || frameTimes.length < 2}
117+ title="Record the scene as it is oriented"
118+ >
119+ Save movie
120+ </button>
121+ <label className="pl-muted">
122+ speed{" "}
123+ <select
124+ className="pl-select"
125+ style={{ width: "auto" }}
126+ value={speed}
127+ disabled={recording}
128+ onChange={(e) => setSpeed(Number(e.target.value))}
129+ >
130+ {SPEEDS.map((s) => (
131+ <option key={s.value} value={s.value}>
132+ {s.label}
133+ </option>
134+ ))}
135+ </select>
136+ </label>
137+ {recording && (
138+ <>
139+ <div className="pl-bar" aria-label="recording progress">
140+ <div style={{ width: `${Math.round(state.fraction * 100)}%` }} />
141+ </div>
142+ <button className="pl-btn pl-btn-danger" onClick={() => abort.current?.abort()}>
143+ Stop
144+ </button>
145+ <span className="pl-muted">
146+ recording in real time, {Math.max(0, length * (1 - state.fraction)).toFixed(1)} s left;
147+ keep this tab in front
148+ </span>
149+ </>
150+ )}
151+ {!recording && support.status !== "available" && (
152+ <span className="pl-muted">Not available here ({support.reason}).</span>
153+ )}
154+ {!recording && support.status === "available" && state.status !== "done" && (
155+ <span className="pl-muted">
156+ {length.toFixed(1)} s of {support.ext.toUpperCase()}
157+ {slow
158+ ? `: the ${frameTimes.length} frames of the first ${(frameSpan * 1e3).toPrecision(3)} ms at ${SLOW_FPS} a second, silent`
159+ : " with the sound, written in real time"}
160+ {speed === 1 || slow ? "" : "; the sound is slowed with the picture and drops in pitch"}.
161+ {crowded
162+ ? ` The run's ${frameTimes.length} frames would need ${fps.toFixed(0)} a second here, more than the display refreshes: choose a slower speed to keep them all.`
163+ : ""}
164+ </span>
165+ )}
166+ {!recording && state.status === "done" && (
167+ <span className="pl-muted">
168+ Saved {state.result.seconds.toFixed(1)} s, {state.result.width} × {state.result.height},{" "}
169+ {(state.result.blob.size / 1e6).toFixed(1)} MB, {state.result.drawn} of{" "}
170+ {state.result.frames} frames drawn
171+ {state.result.drawn < state.result.frames ? " (the rest were dropped to keep time)" : ""}.
172+ </span>
173+ )}
174+ {state.status === "error" && <span className="pl-issue">{state.message}</span>}
175+ </div>
176+ );
177+}
packages/components/src/video/recordMovie.tsadded+336−0View file
@@ -0,0 +1,336 @@
1+/**
2+ * The scene as a movie, with the radiated sound as its audio track.
3+ *
4+ * The browser's own recorder does the encoding: the composite canvas below is
5+ * captured as a video track, the sound is played into a MediaStream audio
6+ * track, and MediaRecorder muxes the two into WebM (or MP4 where the browser
7+ * writes it). That makes the recording real time, which is the price of using
8+ * nothing but the platform: a three-second movie takes three seconds to write,
9+ * and a machine too busy to keep up drops frames rather than slowing down.
10+ * The result reports how many of the run's frames were actually drawn.
11+ *
12+ * The composite canvas is the scene, drawn at the movie's own resolution
13+ * rather than the one on screen, with a title bar carrying the simulated time
14+ * and a caption bar carrying the field, its colour scale and the exaggeration.
15+ * Everything a viewer needs to read the movie is therefore in the file.
16+ */
17+import { colormapTable, getColormap, toAudioBuffer, type AudioPrep } from "@plate-dyn/core";
18+import { withWebmDuration } from "./webmDuration";
19+
20+export interface MovieLegend {
21+ label: string;
22+ unit: string;
23+ /** Range of the colour scale, in SI; `scale` multiplies it for the labels */
24+ min: number;
25+ max: number;
26+ scale: number;
27+ colormap: string;
28+}
29+
30+export interface MovieOverlay {
31+ title: string;
32+ /** A second line: what is exaggerated, and by how much */
33+ note?: string;
34+ legend?: MovieLegend;
35+}
36+
37+/** The scene the recorder draws from (Plate3DView's imperative handle). */
38+export interface MovieSource {
39+ canvas: () => HTMLCanvasElement | null;
40+ drawFrame: (k: number, size?: { width: number; height: number }) => void;
41+}
42+
43+export interface MovieOptions {
44+ source: MovieSource;
45+ /** Simulated time of every frame [s] */
46+ frameTimes: Float64Array;
47+ /** The sound, normalized as the player and the WAV export have it */
48+ audio?: AudioPrep;
49+ /** 1 is real time; below that the movie and the sound are slowed together */
50+ speed?: number;
51+ /** Width of the movie in pixels; the height follows `aspect` plus the bars */
52+ width?: number;
53+ /** Height of the scene as a fraction of the width */
54+ aspect?: number;
55+ overlay?: MovieOverlay;
56+ /** Frame to leave on screen when the recording ends */
57+ restoreFrame?: number;
58+ onProgress?: (fraction: number) => void;
59+ signal?: AbortSignal;
60+}
61+
62+export interface MovieResult {
63+ blob: Blob;
64+ ext: string;
65+ mimeType: string;
66+ /** Length of the movie [s] */
67+ seconds: number;
68+ width: number;
69+ height: number;
70+ /** Frames the run has, and how many of them the recording drew */
71+ frames: number;
72+ drawn: number;
73+}
74+
75+/**
76+ * Containers to try, in order. WebM with Opus comes first because it is the
77+ * one Chrome and Firefox record with the sound: their MP4 recording accepts no
78+ * audio codec at all (`isTypeSupported` says no to every `video/mp4` string
79+ * that names one), and a movie without its sound is not what was asked for.
80+ * The MP4 entries are for Safari, which records MP4 with AAC and no WebM.
81+ */
82+const FORMATS = [
83+ { mimeType: "video/webm;codecs=vp9,opus", ext: "webm" },
84+ { mimeType: "video/webm;codecs=vp8,opus", ext: "webm" },
85+ { mimeType: "video/webm", ext: "webm" },
86+ { mimeType: 'video/mp4;codecs="avc1.42E01E,mp4a.40.2"', ext: "mp4" },
87+ { mimeType: "video/mp4", ext: "mp4" },
88+];
89+
90+const defaultSupported = (mime: string): boolean =>
91+ typeof MediaRecorder !== "undefined" && MediaRecorder.isTypeSupported(mime);
92+
93+/** The first container this browser can write, or null. */
94+export function pickMovieFormat(
95+ isSupported: (mime: string) => boolean = defaultSupported,
96+): { mimeType: string; ext: string } | null {
97+ return FORMATS.find((f) => isSupported(f.mimeType)) ?? null;
98+}
99+
100+export type MovieSupport =
101+ | { status: "available"; mimeType: string; ext: string }
102+ | { status: "unavailable"; reason: string };
103+
104+/** Whether this browser can record the scene at all, and in what. */
105+export function movieSupport(): MovieSupport {
106+ if (typeof MediaRecorder === "undefined")
107+ return { status: "unavailable", reason: "this browser has no MediaRecorder" };
108+ if (
109+ typeof HTMLCanvasElement === "undefined" ||
110+ typeof HTMLCanvasElement.prototype.captureStream !== "function"
111+ )
112+ return { status: "unavailable", reason: "this browser cannot capture a canvas as video" };
113+ const f = pickMovieFormat();
114+ if (!f)
115+ return { status: "unavailable", reason: "this browser writes no video format the lab can use" };
116+ return { status: "available", mimeType: f.mimeType, ext: f.ext };
117+}
118+
119+/**
120+ * The last frame whose time has arrived at simulated time t, searching forward
121+ * from `from`. Frames are monotonic, so a run that falls behind skips to the
122+ * frame it should be showing rather than replaying the ones it missed.
123+ */
124+export function frameAtTime(frameTimes: ArrayLike<number>, t: number, from = 0): number {
125+ let k = Math.max(0, Math.min(from, frameTimes.length - 1));
126+ while (k + 1 < frameTimes.length && frameTimes[k + 1] <= t) k++;
127+ return k;
128+}
129+
130+/** Simulated span of the movie [s]: the frames, or the sound if it lasts longer. */
131+export function movieSpan(frameTimes: ArrayLike<number>, audio?: AudioPrep): number {
132+ const last = frameTimes.length ? frameTimes[frameTimes.length - 1] : 0;
133+ return Math.max(last, audio?.durationS ?? 0);
134+}
135+
136+const even = (x: number): number => 2 * Math.round(x / 2);
137+
138+const TEXT = "#1c2430";
139+const MUTED = "#5b6573";
140+const RULE = "#d9dde3";
141+
142+function fmt(x: number): string {
143+ if (x === 0) return "0";
144+ const a = Math.abs(x);
145+ if (a >= 1e4 || a < 1e-2) return x.toExponential(2);
146+ return Number(x.toPrecision(3)).toString();
147+}
148+
149+/** Record the scene, resolving with the finished file. */
150+export async function recordMovie(opts: MovieOptions): Promise<MovieResult> {
151+ const { source, frameTimes, audio, overlay, onProgress, signal } = opts;
152+ const speed = opts.speed ?? 1;
153+ const support = movieSupport();
154+ if (support.status !== "available") throw new Error(support.reason);
155+ if (!frameTimes.length) throw new Error("the run kept no frames to record");
156+ if (!(speed > 0)) throw new Error("the speed must be positive");
157+
158+ const width = even(opts.width ?? 960);
159+ const sceneH = even(width * (opts.aspect ?? 0.7));
160+ const titleH = overlay ? 44 : 0;
161+ const legendH = overlay ? (overlay.legend ? 62 : 32) : 0;
162+ const height = even(sceneH + titleH + legendH);
163+
164+ const out = document.createElement("canvas");
165+ out.width = width;
166+ out.height = height;
167+ const ctx = out.getContext("2d");
168+ if (!ctx) throw new Error("this browser gave no 2-D canvas to compose the movie on");
169+
170+ const drawOverlay = (t: number) => {
171+ ctx.fillStyle = "#fff";
172+ ctx.fillRect(0, 0, width, titleH);
173+ ctx.fillRect(0, titleH + sceneH, width, legendH);
174+ ctx.strokeStyle = RULE;
175+ ctx.lineWidth = 1;
176+ ctx.beginPath();
177+ ctx.moveTo(0, titleH + 0.5);
178+ ctx.lineTo(width, titleH + 0.5);
179+ ctx.moveTo(0, titleH + sceneH + 0.5);
180+ ctx.lineTo(width, titleH + sceneH + 0.5);
181+ ctx.stroke();
182+ if (!overlay) return;
183+ ctx.textBaseline = "alphabetic";
184+ ctx.fillStyle = TEXT;
185+ ctx.font = "600 19px system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif";
186+ ctx.textAlign = "left";
187+ ctx.fillText(overlay.title, 16, 29);
188+ ctx.textAlign = "right";
189+ ctx.font = "17px ui-monospace, SFMono-Regular, Menlo, Consolas, monospace";
190+ ctx.fillText(
191+ t < 0.1 ? `t = ${(t * 1e3).toFixed(3)} ms` : `t = ${t.toFixed(3)} s`,
192+ width - 16,
193+ 29,
194+ );
195+ ctx.textAlign = "left";
196+ const base = titleH + sceneH;
197+ const lg = overlay.legend;
198+ if (lg) {
199+ ctx.font = "13px system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif";
200+ ctx.fillStyle = TEXT;
201+ ctx.fillText(`${lg.label} [${lg.unit}]`, 16, base + 20);
202+ const bx = 16;
203+ const by = base + 30;
204+ const bw = 240;
205+ const bh = 10;
206+ const table = colormapTable(getColormap(lg.colormap));
207+ for (let i = 0; i < bw; i++) {
208+ const k = Math.round((i / (bw - 1)) * 255) * 3;
209+ ctx.fillStyle = `rgb(${table[k]},${table[k + 1]},${table[k + 2]})`;
210+ ctx.fillRect(bx + i, by, 1, bh);
211+ }
212+ ctx.strokeStyle = RULE;
213+ ctx.strokeRect(bx + 0.5, by + 0.5, bw - 1, bh - 1);
214+ ctx.fillStyle = MUTED;
215+ ctx.font = "12px ui-monospace, SFMono-Regular, Menlo, Consolas, monospace";
216+ ctx.fillText(fmt(lg.min * lg.scale), bx, by + bh + 14);
217+ ctx.textAlign = "right";
218+ ctx.fillText(fmt(lg.max * lg.scale), bx + bw, by + bh + 14);
219+ ctx.textAlign = "left";
220+ }
221+ if (overlay.note) {
222+ ctx.fillStyle = MUTED;
223+ ctx.font = "13px system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif";
224+ ctx.textAlign = "right";
225+ ctx.fillText(overlay.note, width - 16, base + (lg ? 40 : 21));
226+ ctx.textAlign = "left";
227+ }
228+ };
229+
230+ let drawn = 0;
231+ const render = (k: number) => {
232+ source.drawFrame(k, { width, height: sceneH });
233+ const src = source.canvas();
234+ ctx.fillStyle = "#fff";
235+ ctx.fillRect(0, titleH, width, sceneH);
236+ if (src) ctx.drawImage(src, 0, titleH, width, sceneH);
237+ drawOverlay(frameTimes[k] ?? 0);
238+ drawn++;
239+ };
240+
241+ const stream = out.captureStream(0);
242+ const video = stream.getVideoTracks()[0] as CanvasCaptureMediaStreamTrack;
243+ let actx: AudioContext | undefined;
244+ let node: AudioBufferSourceNode | undefined;
245+ const chunks: Blob[] = [];
246+ let rec: MediaRecorder | undefined;
247+ try {
248+ if (audio && audio.samples.length > 1) {
249+ actx = new AudioContext();
250+ await actx.resume();
251+ const dest = actx.createMediaStreamDestination();
252+ node = actx.createBufferSource();
253+ node.buffer = toAudioBuffer(actx, audio);
254+ // below real time the sound is slowed like a tape, and drops in pitch with it
255+ node.playbackRate.value = speed;
256+ node.connect(dest);
257+ const track = dest.stream.getAudioTracks()[0];
258+ if (track) stream.addTrack(track);
259+ }
260+
261+ rec = new MediaRecorder(stream, { mimeType: support.mimeType, videoBitsPerSecond: 8e6 });
262+ rec.ondataavailable = (e) => {
263+ if (e.data.size > 0) chunks.push(e.data);
264+ };
265+ const stopped = new Promise<void>((resolve) => {
266+ rec!.onstop = () => resolve();
267+ });
268+
269+ const span = movieSpan(frameTimes, audio);
270+ const wall = span / speed;
271+ render(0);
272+ video.requestFrame();
273+ const tRec = performance.now();
274+ rec.start(250);
275+ node?.start();
276+
277+ const t0 = performance.now();
278+ let last = 0;
279+ await new Promise<void>((resolve) => {
280+ const step = () => {
281+ const elapsed = (performance.now() - t0) / 1000;
282+ const k = frameAtTime(frameTimes, elapsed * speed, last);
283+ if (k !== last) {
284+ last = k;
285+ render(k);
286+ }
287+ video.requestFrame();
288+ onProgress?.(Math.min(1, elapsed / Math.max(wall, 1e-6)));
289+ if (signal?.aborted || elapsed >= wall + 0.15) resolve();
290+ else requestAnimationFrame(step);
291+ };
292+ requestAnimationFrame(step);
293+ });
294+
295+ rec.stop();
296+ await stopped;
297+ const seconds = (performance.now() - tRec) / 1000;
298+ let blob = new Blob(chunks, { type: support.mimeType });
299+ if (blob.size === 0) throw new Error("the browser's recorder produced an empty file");
300+ if (support.ext === "webm") {
301+ // the recorder leaves the length out, since it does not know it until now
302+ const patched = withWebmDuration(new Uint8Array(await blob.arrayBuffer()), seconds);
303+ blob = new Blob([patched as BlobPart], { type: support.mimeType });
304+ }
305+ return {
306+ blob,
307+ ext: support.ext,
308+ mimeType: support.mimeType,
309+ seconds,
310+ width,
311+ height,
312+ frames: frameTimes.length,
313+ drawn,
314+ };
315+ } finally {
316+ try {
317+ node?.stop();
318+ } catch {
319+ /* already stopped */
320+ }
321+ void actx?.close();
322+ for (const t of stream.getTracks()) t.stop();
323+ // back to the size and the frame the app is showing
324+ source.drawFrame(opts.restoreFrame ?? 0);
325+ }
326+}
327+
328+/** Hand the finished movie to the browser as a download. */
329+export function saveMovie(result: MovieResult, filename: string): void {
330+ const url = URL.createObjectURL(result.blob);
331+ const a = document.createElement("a");
332+ a.href = url;
333+ a.download = `${filename}.${result.ext}`;
334+ a.click();
335+ setTimeout(() => URL.revokeObjectURL(url), 0);
336+}
packages/components/src/video/webmDuration.tsadded+201−0View file
@@ -0,0 +1,201 @@
1+/**
2+ * A duration for the WebM the browser records.
3+ *
4+ * MediaRecorder writes a live stream: the Segment is left with an unknown
5+ * size and the Info section carries no Duration, because neither is known
6+ * while the recording is still running. Browsers cope by scanning the file,
7+ * but many other players do not: ffmpeg reports "Duration: N/A", editors and
8+ * upload previews show 0:00, and seeking is unreliable. Since the lab knows
9+ * exactly how long it recorded, it can say so.
10+ *
11+ * `withWebmDuration` splices a Duration element into the Info section (or
12+ * overwrites one that is already there) and returns the patched bytes. Only
13+ * the Info section moves; everything after it is copied unchanged, and the
14+ * Segment's unknown size stays unknown, which is legal and is what every
15+ * player already handles. Anything that does not parse as WebM is returned
16+ * untouched, so a browser that writes something else loses nothing.
17+ *
18+ * The format: an EBML element is an id, then a size as a variable-length
19+ * integer whose leading zeros give its byte count, then the payload. Duration
20+ * (id 0x4489) is a big-endian float in units of the Segment's TimecodeScale
21+ * (id 0x2AD7B1, nanoseconds, one millisecond by default).
22+ */
23+
24+const ID_EBML = 0x1a45dfa3;
25+const ID_SEGMENT = 0x18538067;
26+const ID_INFO = 0x1549a966;
27+const ID_TIMECODE_SCALE = 0x2ad7b1;
28+const ID_DURATION = 0x4489;
29+
30+interface Element {
31+ id: number;
32+ /** Offset of the id, the lengths of the id and size fields, then the payload's bounds */
33+ start: number;
34+ idLen: number;
35+ sizeLen: number;
36+ body: number;
37+ end: number;
38+ /** The size field said "unknown", so the payload runs to the end of its parent */
39+ unknownSize: boolean;
40+}
41+
42+/** Length in bytes of the variable-length integer starting with this byte, or 0 if invalid. */
43+function vintLength(first: number): number {
44+ for (let i = 0; i < 8; i++) if (first & (0x80 >> i)) return i + 1;
45+ return 0;
46+}
47+
48+/** Read one element header at `pos`, or null when the buffer runs out. */
49+function readElement(b: Uint8Array, pos: number, parentEnd: number): Element | null {
50+ if (pos >= parentEnd) return null;
51+ const idLen = vintLength(b[pos]);
52+ if (idLen === 0 || pos + idLen > parentEnd) return null;
53+ let id = 0;
54+ for (let i = 0; i < idLen; i++) id = id * 256 + b[pos + i];
55+ const sizePos = pos + idLen;
56+ if (sizePos >= parentEnd) return null;
57+ const sizeLen = vintLength(b[sizePos]);
58+ if (sizeLen === 0 || sizePos + sizeLen > parentEnd) return null;
59+ let size = b[sizePos] & (0xff >> sizeLen);
60+ let allOnes = size === 0xff >> sizeLen;
61+ for (let i = 1; i < sizeLen; i++) {
62+ size = size * 256 + b[sizePos + i];
63+ allOnes = allOnes && b[sizePos + i] === 0xff;
64+ }
65+ const body = sizePos + sizeLen;
66+ const end = allOnes ? parentEnd : Math.min(body + size, parentEnd);
67+ return { id, start: pos, idLen, sizeLen, body, end, unknownSize: allOnes };
68+}
69+
70+/** The first child with this id, searching the children of `parent` only. */
71+function findChild(b: Uint8Array, parent: Element, id: number): Element | null {
72+ let pos = parent.body;
73+ for (;;) {
74+ const el = readElement(b, pos, parent.end);
75+ if (!el) return null;
76+ if (el.id === id) return el;
77+ pos = el.end;
78+ if (pos <= el.start) return null; // a zero-length element would loop forever
79+ }
80+}
81+
82+/** An unsigned integer payload, for TimecodeScale. */
83+function readUint(b: Uint8Array, el: Element): number {
84+ let v = 0;
85+ for (let i = el.body; i < el.end; i++) v = v * 256 + b[i];
86+ return v;
87+}
88+
89+/** The EBML size field for `value`, at least `minLength` bytes long. */
90+export function writeVintSize(value: number, minLength = 1): Uint8Array {
91+ let len = minLength;
92+ while (len < 8 && value >= 2 ** (7 * len) - 1) len++;
93+ const out = new Uint8Array(len);
94+ let v = value;
95+ for (let i = len - 1; i >= 0; i--) {
96+ out[i] = v % 256;
97+ v = Math.floor(v / 256);
98+ }
99+ out[0] |= 0x80 >> (len - 1);
100+ return out;
101+}
102+
103+/** The Duration element: its id, its size, and the value as a big-endian double. */
104+function durationElement(ticks: number): Uint8Array {
105+ const out = new Uint8Array(11);
106+ out[0] = 0x44;
107+ out[1] = 0x89;
108+ out[2] = 0x88; // an 8-byte payload
109+ new DataView(out.buffer).setFloat64(3, ticks, false);
110+ return out;
111+}
112+
113+/**
114+ * Return the recording with its length written into the Info section. Bytes
115+ * that are not WebM, or that carry no Info section, come back unchanged.
116+ */
117+export function withWebmDuration(bytes: Uint8Array, seconds: number): Uint8Array {
118+ if (!(seconds > 0) || bytes.length < 8) return bytes;
119+ const header = readElement(bytes, 0, bytes.length);
120+ if (!header || header.id !== ID_EBML) return bytes;
121+ let segment: Element | null = null;
122+ let pos = header.end;
123+ while (pos < bytes.length) {
124+ const el = readElement(bytes, pos, bytes.length);
125+ if (!el || el.end <= el.start) break;
126+ if (el.id === ID_SEGMENT) {
127+ segment = el;
128+ break;
129+ }
130+ pos = el.end;
131+ }
132+ if (!segment) return bytes;
133+ const info = findChild(bytes, segment, ID_INFO);
134+ if (!info || info.unknownSize) return bytes;
135+
136+ const scaleEl = findChild(bytes, info, ID_TIMECODE_SCALE);
137+ const scale = scaleEl ? readUint(bytes, scaleEl) : 1e6; // nanoseconds per tick
138+ const ticks = (seconds * 1e9) / (scale > 0 ? scale : 1e6);
139+
140+ const existing = findChild(bytes, info, ID_DURATION);
141+ if (existing && existing.end - existing.body === 8) {
142+ const out = bytes.slice();
143+ new DataView(out.buffer, out.byteOffset, out.byteLength).setFloat64(
144+ existing.body,
145+ ticks,
146+ false,
147+ );
148+ return out;
149+ }
150+ if (existing) return bytes; // a 4-byte float: leave a file we did not write alone
151+
152+ // Splice the element in, and grow the Info size field to match
153+ const add = durationElement(ticks);
154+ const payload = info.end - info.body;
155+ const idBytes = bytes.subarray(info.start, info.start + info.idLen);
156+ const newSize = writeVintSize(payload + add.length, info.sizeLen);
157+ const out = new Uint8Array(
158+ info.start + idBytes.length + newSize.length + payload + add.length + (bytes.length - info.end),
159+ );
160+ let o = 0;
161+ out.set(bytes.subarray(0, info.start), o);
162+ o += info.start;
163+ out.set(idBytes, o);
164+ o += idBytes.length;
165+ out.set(newSize, o);
166+ o += newSize.length;
167+ out.set(bytes.subarray(info.body, info.end), o);
168+ o += payload;
169+ out.set(add, o);
170+ o += add.length;
171+ out.set(bytes.subarray(info.end), o);
172+ return out;
173+}
174+
175+/** The duration an EBML file declares [s], or null when it declares none. */
176+export function readWebmDuration(bytes: Uint8Array): number | null {
177+ const header = readElement(bytes, 0, bytes.length);
178+ if (!header || header.id !== ID_EBML) return null;
179+ let pos = header.end;
180+ let segment: Element | null = null;
181+ while (pos < bytes.length) {
182+ const el = readElement(bytes, pos, bytes.length);
183+ if (!el || el.end <= el.start) break;
184+ if (el.id === ID_SEGMENT) {
185+ segment = el;
186+ break;
187+ }
188+ pos = el.end;
189+ }
190+ if (!segment) return null;
191+ const info = findChild(bytes, segment, ID_INFO);
192+ if (!info) return null;
193+ const dur = findChild(bytes, info, ID_DURATION);
194+ if (!dur) return null;
195+ const scaleEl = findChild(bytes, info, ID_TIMECODE_SCALE);
196+ const scale = scaleEl ? readUint(bytes, scaleEl) : 1e6;
197+ const view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
198+ const ticks =
199+ dur.end - dur.body === 4 ? view.getFloat32(dur.body, false) : view.getFloat64(dur.body, false);
200+ return (ticks * scale) / 1e9;
201+}
packages/components/test/movie.test.tsadded+91−0View file
@@ -0,0 +1,91 @@
1+import { describe, expect, it } from "vitest";
2+import { frameAtTime, movieSpan, pickMovieFormat } from "../src/video/recordMovie";
3+import { readWebmDuration, withWebmDuration } from "../src/video/webmDuration";
4+import type { AudioPrep } from "@plate-dyn/core";
5+
6+const prep = (durationS: number): AudioPrep => ({
7+ samples: new Float32Array(2),
8+ fs: 44100,
9+ peakPa: 1,
10+ gain: 1,
11+ durationS,
12+});
13+
14+describe("the movie recorder's bookkeeping", () => {
15+ it("prefers WebM with sound, and falls back to what the browser writes", () => {
16+ // WebM with Opus first: it is the one that records with the sound
17+ expect(pickMovieFormat(() => true)?.mimeType).toBe("video/webm;codecs=vp9,opus");
18+ expect(pickMovieFormat((m) => m === "video/webm")?.ext).toBe("webm");
19+ expect(pickMovieFormat((m) => m.startsWith("video/mp4"))?.ext).toBe("mp4");
20+ expect(pickMovieFormat(() => false)).toBe(null);
21+ });
22+
23+ it("shows the frame whose time has arrived, skipping the ones it fell behind", () => {
24+ const t = Float64Array.from([0, 0.1, 0.2, 0.3, 0.4]);
25+ expect(frameAtTime(t, 0)).toBe(0);
26+ expect(frameAtTime(t, 0.05)).toBe(0);
27+ expect(frameAtTime(t, 0.1)).toBe(1);
28+ expect(frameAtTime(t, 0.25)).toBe(2);
29+ expect(frameAtTime(t, 99)).toBe(4);
30+ // a recording that fell behind jumps forward rather than replaying
31+ expect(frameAtTime(t, 0.35, 1)).toBe(3);
32+ // and never goes back, since the search starts where it left off
33+ expect(frameAtTime(t, 0.0, 3)).toBe(3);
34+ });
35+
36+ it("runs as long as the frames or the sound, whichever lasts longer", () => {
37+ const t = Float64Array.from([0, 0.5, 1.0]);
38+ expect(movieSpan(t)).toBe(1.0);
39+ expect(movieSpan(t, prep(2.5))).toBe(2.5);
40+ expect(movieSpan(t, prep(0.4))).toBe(1.0);
41+ expect(movieSpan(new Float64Array(0), prep(3))).toBe(3);
42+ });
43+});
44+
45+/** A minimal WebM: the header, a Segment of unknown size, an Info with a TimecodeScale, a Cluster. */
46+function fakeWebm(): Uint8Array {
47+ const header = [0x1a, 0x45, 0xdf, 0xa3, 0x84, 1, 2, 3, 4];
48+ const segment = [0x18, 0x53, 0x80, 0x67, 0x01, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff];
49+ const info = [0x15, 0x49, 0xa9, 0x66, 0x87, 0x2a, 0xd7, 0xb1, 0x83, 0x0f, 0x42, 0x40];
50+ const cluster = [0x1f, 0x43, 0xb6, 0x75, 0x84, 9, 8, 7, 6];
51+ return Uint8Array.from([...header, ...segment, ...info, ...cluster]);
52+}
53+
54+describe("the length written into a recorded WebM", () => {
55+ it("writes a duration the file did not have, and keeps everything after it", () => {
56+ const before = fakeWebm();
57+ expect(readWebmDuration(before)).toBe(null);
58+ const after = withWebmDuration(before, 2.5);
59+ expect(after.length).toBe(before.length + 11);
60+ expect(readWebmDuration(after)).toBeCloseTo(2.5, 9);
61+ // the Cluster that followed the Info section is byte for byte where it was
62+ expect(Array.from(after.subarray(after.length - 9))).toEqual([
63+ 0x1f, 0x43, 0xb6, 0x75, 0x84, 9, 8, 7, 6,
64+ ]);
65+ // and the Info section now declares the longer payload: its size byte sits
66+ // after the 9-byte header, the 12-byte Segment header and the 4-byte id
67+ expect(after[9 + 12 + 4]).toBe(0x87 + 11);
68+ });
69+
70+ it("overwrites a duration that is already there", () => {
71+ const once = withWebmDuration(fakeWebm(), 2.5);
72+ const twice = withWebmDuration(once, 7.25);
73+ expect(twice.length).toBe(once.length);
74+ expect(readWebmDuration(twice)).toBeCloseTo(7.25, 9);
75+ });
76+
77+ it("writes the length in the units the timecode scale sets", () => {
78+ // the fixture's scale is 0x0F4240 ns, a millisecond, so a second is a thousand ticks
79+ const patched = withWebmDuration(fakeWebm(), 1);
80+ const view = new DataView(patched.buffer, patched.byteOffset, patched.byteLength);
81+ expect(view.getFloat64(patched.length - 9 - 8, false)).toBeCloseTo(1000, 9);
82+ expect(readWebmDuration(patched)).toBeCloseTo(1, 9);
83+ });
84+
85+ it("leaves anything that is not WebM alone", () => {
86+ const notWebm = Uint8Array.from([0, 0, 0, 0x20, 0x66, 0x74, 0x79, 0x70, 1, 2, 3, 4]);
87+ expect(withWebmDuration(notWebm, 3)).toBe(notWebm);
88+ expect(readWebmDuration(notWebm)).toBe(null);
89+ expect(withWebmDuration(fakeWebm(), 0)).toEqual(fakeWebm());
90+ });
91+});
packages/components/tsconfig.jsonadded+9−0View file
@@ -0,0 +1,9 @@
1+{
2+ "extends": "../../tsconfig.base.json",
3+ "compilerOptions": {
4+ "noEmit": true
5+ },
6+ "include": [
7+ "src"
8+ ]
9+}
packages/core/package.jsonadded+19−0View file
@@ -0,0 +1,19 @@
1+{
2+ "name": "@plate-dyn/core",
3+ "version": "0.1.0",
4+ "private": true,
5+ "type": "module",
6+ "description": "Problem specification, .m source generation, numbl runner, result typing and field primitives for the plate dynamics lab 2. Framework-agnostic.",
7+ "exports": {
8+ ".": "./src/index.ts",
9+ "./worker": "./src/runner/worker.ts",
10+ "./node": "./src/node.ts"
11+ },
12+ "scripts": {
13+ "typecheck": "tsc -p tsconfig.json --noEmit",
14+ "test": "vitest run"
15+ },
16+ "dependencies": {
17+ "numbl": "0.4.21"
18+ }
19+}
packages/core/src/audio/buffer.tsadded+36−0View file
@@ -0,0 +1,36 @@
1+/**
2+ * Turn a pressure signal into something Web Audio can play. Peak normalization
3+ * is a playback convenience (the absolute level of the Rayleigh estimate is not
4+ * calibrated anyway) and is reported so the interface can say so.
5+ */
6+export interface AudioPrep {
7+ samples: Float32Array;
8+ fs: number;
9+ /** Peak of the original signal [Pa] */
10+ peakPa: number;
11+ /** Gain applied so the peak sits at `target` */
12+ gain: number;
13+ durationS: number;
14+}
15+
16+export function prepareAudio(p: Float64Array, fs: number, target = 0.8): AudioPrep {
17+ let peak = 0;
18+ for (let i = 0; i < p.length; i++) {
19+ const a = Math.abs(p[i]);
20+ if (a > peak) peak = a;
21+ }
22+ const gain = peak > 0 ? target / peak : 1;
23+ const samples = new Float32Array(p.length);
24+ for (let i = 0; i < p.length; i++) samples[i] = Number.isFinite(p[i]) ? p[i] * gain : 0;
25+ return { samples, fs, peakPa: peak, gain, durationS: p.length / fs };
26+}
27+
28+export function toAudioBuffer(ctx: BaseAudioContext, prep: AudioPrep): AudioBuffer {
29+ const n = Math.max(1, prep.samples.length);
30+ const buf = ctx.createBuffer(1, n, prep.fs);
31+ const data = (
32+ prep.samples.length ? prep.samples : new Float32Array(1)
33+ ) as Float32Array<ArrayBuffer>;
34+ buf.copyToChannel(data, 0);
35+ return buf;
36+}
packages/core/src/audio/wav.tsadded+44−0View file
@@ -0,0 +1,44 @@
1+/**
2+ * Encode a mono signal as a PCM RIFF/WAVE file, 24-bit by default (as the
3+ * reference simulators write) or 16-bit. The input is expected to be
4+ * peak-normalized already (see `prepareAudio`); values outside [-1, 1] are
5+ * clipped rather than wrapped. Non-finite samples are written as silence.
6+ */
7+export function encodeWav(samples: ArrayLike<number>, fs: number, bits: 16 | 24 = 24): Uint8Array {
8+ const n = samples.length;
9+ const bytesPerSample = bits / 8;
10+ const dataBytes = n * bytesPerSample;
11+ const buf = new ArrayBuffer(44 + dataBytes);
12+ const v = new DataView(buf);
13+ const ascii = (off: number, s: string) => {
14+ for (let i = 0; i < s.length; i++) v.setUint8(off + i, s.charCodeAt(i));
15+ };
16+ ascii(0, "RIFF");
17+ v.setUint32(4, 36 + dataBytes, true);
18+ ascii(8, "WAVE");
19+ ascii(12, "fmt ");
20+ v.setUint32(16, 16, true); // fmt chunk size
21+ v.setUint16(20, 1, true); // PCM
22+ v.setUint16(22, 1, true); // mono
23+ v.setUint32(24, fs, true);
24+ v.setUint32(28, fs * bytesPerSample, true); // byte rate
25+ v.setUint16(32, bytesPerSample, true); // block align
26+ v.setUint16(34, bits, true); // bits per sample
27+ ascii(36, "data");
28+ v.setUint32(40, dataBytes, true);
29+ let off = 44;
30+ const full = bits === 24 ? 8388607 : 32767;
31+ for (let i = 0; i < n; i++, off += bytesPerSample) {
32+ const x = samples[i];
33+ const c = Number.isFinite(x) ? Math.max(-1, Math.min(1, x)) : 0;
34+ const q = Math.round(c * full);
35+ if (bits === 24) {
36+ v.setUint8(off, q & 255);
37+ v.setUint8(off + 1, (q >> 8) & 255);
38+ v.setUint8(off + 2, (q >> 16) & 255);
39+ } else {
40+ v.setInt16(off, Math.max(-32768, q), true);
41+ }
42+ }
43+ return new Uint8Array(buf);
44+}
packages/core/src/cost.tsadded+36−0View file
@@ -0,0 +1,36 @@
1+import type { MethodDefinition } from "./methods/types";
2+
3+/**
4+ * Wall-time estimates for the two stages of a run. This is a calibration,
5+ * not physics: the sizes (the plan's `work` for the modes stage, samples
6+ * times modes for the strike) come from the method's plan function running in
7+ * numbl; only the milliseconds-per-unit constants live here, seeded from the
8+ * method definition and refined by each finished stage.
9+ */
10+export class CostModel {
11+ private rates = new Map<string, number>();
12+ private readonly overheadMs = 150;
13+
14+ private rate(method: MethodDefinition, stage: "modes" | "strike"): number {
15+ return (
16+ this.rates.get(`${method.id}:${stage}`) ??
17+ (stage === "modes" ? method.cost.msPerWork : method.cost.msPerSampleMode)
18+ );
19+ }
20+
21+ modesMs(method: MethodDefinition, work: number): number {
22+ return work * this.rate(method, "modes") + this.overheadMs;
23+ }
24+
25+ strikeMs(method: MethodDefinition, samples: number, modes: number): number {
26+ return samples * Math.max(modes, 1) * this.rate(method, "strike") + this.overheadMs;
27+ }
28+
29+ /** Blend a measurement in, weighting recent runs more. */
30+ observe(method: MethodDefinition, stage: "modes" | "strike", units: number, wallMs: number) {
31+ if (units <= 0 || wallMs <= 0) return;
32+ const measured = (wallMs - this.overheadMs) / units;
33+ if (!Number.isFinite(measured) || measured <= 0) return;
34+ this.rates.set(`${method.id}:${stage}`, 0.5 * this.rate(method, stage) + 0.5 * measured);
35+ }
36+}
packages/core/src/fields/colormap.tsadded+95−0View file
@@ -0,0 +1,95 @@
1+/**
2+ * Colormaps for field display. Display only: these map numbers the solver
3+ * produced to pixels and never change the numbers.
4+ */
5+export type RGB = [number, number, number];
6+
7+export interface Colormap {
8+ id: string;
9+ name: string;
10+ /** Kind of data it suits */
11+ kind: "sequential" | "diverging";
12+ /** t in [0, 1] -> RGB in [0, 255] */
13+ at(t: number): RGB;
14+}
15+
16+// Polynomial fit of matplotlib's viridis (Matt Zucker, public domain).
17+function viridis(t: number): RGB {
18+ const c0 = [0.2777273272234177, 0.005407344544966578, 0.3340998053353061];
19+ const c1 = [0.1050930431085774, 1.404613529898575, 1.384590162594685];
20+ const c2 = [-0.3308618287255563, 0.214847559468213, 0.09509516302823659];
21+ const c3 = [-4.634230498983486, -5.799100973351585, -19.33244095627987];
22+ const c4 = [6.228269936347081, 14.17993336680509, 56.69055260068105];
23+ const c5 = [4.776384997670288, -13.74514537774601, -65.35303263337234];
24+ const c6 = [-5.435455855934631, 4.645852612178535, 26.3124352495832];
25+ const out: RGB = [0, 0, 0];
26+ for (let i = 0; i < 3; i++) {
27+ const v =
28+ c0[i] + t * (c1[i] + t * (c2[i] + t * (c3[i] + t * (c4[i] + t * (c5[i] + t * c6[i])))));
29+ out[i] = Math.round(255 * Math.min(1, Math.max(0, v)));
30+ }
31+ return out;
32+}
33+
34+// Polynomial fit of matplotlib's magma (same source).
35+function magma(t: number): RGB {
36+ const c0 = [-0.002136485053939582, -0.000749655052795221, -0.005386127855323933];
37+ const c1 = [0.2516605407371642, 0.6775232436837668, 2.494026599312351];
38+ const c2 = [8.353717279216625, -3.577719514958484, 0.3144679030132573];
39+ const c3 = [-27.66873308576866, 14.26473078096533, -13.64921318813922];
40+ const c4 = [52.17613981234068, -27.94360607168351, 12.94416944238394];
41+ const c5 = [-50.76852536473588, 29.04658282127291, 4.23415299384598];
42+ const c6 = [18.65570506591883, -11.48977351997711, -5.601961508734096];
43+ const out: RGB = [0, 0, 0];
44+ for (let i = 0; i < 3; i++) {
45+ const v =
46+ c0[i] + t * (c1[i] + t * (c2[i] + t * (c3[i] + t * (c4[i] + t * (c5[i] + t * c6[i])))));
47+ out[i] = Math.round(255 * Math.min(1, Math.max(0, v)));
48+ }
49+ return out;
50+}
51+
52+// Blue-white-red diverging map for signed fields.
53+function blueWhiteRed(t: number): RGB {
54+ const stops: RGB[] = [
55+ [5, 48, 97],
56+ [67, 147, 195],
57+ [209, 229, 240],
58+ [247, 247, 247],
59+ [253, 219, 199],
60+ [214, 96, 77],
61+ [103, 0, 31],
62+ ];
63+ const x = Math.min(1, Math.max(0, t)) * (stops.length - 1);
64+ const i = Math.min(stops.length - 2, Math.floor(x));
65+ const f = x - i;
66+ const a = stops[i];
67+ const b = stops[i + 1];
68+ return [
69+ Math.round(a[0] + f * (b[0] - a[0])),
70+ Math.round(a[1] + f * (b[1] - a[1])),
71+ Math.round(a[2] + f * (b[2] - a[2])),
72+ ];
73+}
74+
75+export const COLORMAPS: Colormap[] = [
76+ { id: "viridis", name: "Viridis", kind: "sequential", at: viridis },
77+ { id: "magma", name: "Magma", kind: "sequential", at: magma },
78+ { id: "bwr", name: "Blue-white-red", kind: "diverging", at: blueWhiteRed },
79+];
80+
81+export function getColormap(id: string): Colormap {
82+ return COLORMAPS.find((c) => c.id === id) ?? COLORMAPS[0];
83+}
84+
85+/** Precompute a 256-entry lookup table for fast pixel fills. */
86+export function colormapTable(cm: Colormap): Uint8ClampedArray {
87+ const table = new Uint8ClampedArray(256 * 3);
88+ for (let k = 0; k < 256; k++) {
89+ const [r, g, b] = cm.at(k / 255);
90+ table[3 * k] = r;
91+ table[3 * k + 1] = g;
92+ table[3 * k + 2] = b;
93+ }
94+ return table;
95+}
packages/core/src/fields/field.tsadded+49−0View file
@@ -0,0 +1,49 @@
1+/**
2+ * Column-major 2-D fields matching numbl's layout: rows index y, columns index x,
3+ * element (i, j) at data[i + j*ny].
4+ */
5+export interface Field2D {
6+ nx: number;
7+ ny: number;
8+ data: Float64Array;
9+}
10+
11+/** A stack of frames: element (i, j, k) at data[i + j*ny + k*ny*nx]. */
12+export interface FieldFrames {
13+ nx: number;
14+ ny: number;
15+ nf: number;
16+ data: Float64Array;
17+}
18+
19+export function frameOf(frames: FieldFrames, k: number): Field2D {
20+ const n = frames.nx * frames.ny;
21+ return { nx: frames.nx, ny: frames.ny, data: frames.data.subarray(k * n, (k + 1) * n) };
22+}
23+
24+export function fieldAt(f: Field2D, i: number, j: number): number {
25+ return f.data[i + j * f.ny];
26+}
27+
28+export interface Range {
29+ min: number;
30+ max: number;
31+}
32+
33+export function rangeOf(data: ArrayLike<number>): Range {
34+ let min = Infinity;
35+ let max = -Infinity;
36+ for (let k = 0; k < data.length; k++) {
37+ const v = data[k];
38+ if (v < min) min = v;
39+ if (v > max) max = v;
40+ }
41+ if (!Number.isFinite(min)) return { min: 0, max: 0 };
42+ return { min, max };
43+}
44+
45+/** Symmetric range about zero, for signed fields drawn with a diverging colormap. */
46+export function symmetricRange(r: Range): Range {
47+ const a = Math.max(Math.abs(r.min), Math.abs(r.max));
48+ return { min: -a, max: a };
49+}
packages/core/src/fields/raster.tsadded+37−0View file
@@ -0,0 +1,37 @@
1+import type { Field2D, Range } from "./field";
2+import { colormapTable, type Colormap } from "./colormap";
3+
4+/**
5+ * Paint a field into RGBA pixels with y increasing upward (row 0 of the image is
6+ * the top of the plate, y = Ly). Non-finite values are drawn transparent.
7+ */
8+export function rasterizeField(
9+ field: Field2D,
10+ range: Range,
11+ cm: Colormap,
12+ out?: Uint8ClampedArray,
13+): Uint8ClampedArray {
14+ const { nx, ny, data } = field;
15+ const table = colormapTable(cm);
16+ const rgba = out && out.length === nx * ny * 4 ? out : new Uint8ClampedArray(nx * ny * 4);
17+ const span = range.max - range.min || 1;
18+ for (let i = 0; i < ny; i++) {
19+ const row = ny - 1 - i;
20+ for (let j = 0; j < nx; j++) {
21+ const v = data[i + j * ny];
22+ const px = (row * nx + j) * 4;
23+ if (!Number.isFinite(v)) {
24+ rgba[px + 3] = 0;
25+ continue;
26+ }
27+ let t = (v - range.min) / span;
28+ t = t < 0 ? 0 : t > 1 ? 1 : t;
29+ const k = Math.round(t * 255) * 3;
30+ rgba[px] = table[k];
31+ rgba[px + 1] = table[k + 1];
32+ rgba[px + 2] = table[k + 2];
33+ rgba[px + 3] = 255;
34+ }
35+ }
36+ return rgba;
37+}
packages/core/src/generate/bundle.tsadded+101−0View file
@@ -0,0 +1,101 @@
1+import type { ProblemSpec } from "../spec/types";
2+import type { MethodDefinition } from "../methods/types";
3+import { generateParamsBlock } from "./params";
4+import { LIBRARY_FILES, type SourceFile } from "./library";
5+
6+/** A complete, self-contained set of `.m` files plus the one to run. */
7+export interface SourceBundle {
8+ files: SourceFile[];
9+ /** File executed as the script */
10+ entry: string;
11+ /** Workspace variable read back after the run */
12+ resultVariable: string;
13+}
14+
15+export const MAIN_FILE = "main.m";
16+
17+/**
18+ * The script a run executes: the params block, the modes stage and the
19+ * strike stage. The host runs the two stages as two scripts (buildModesBundle
20+ * and buildStrikeBundle) so that the modes can be kept between strikes; an
21+ * edited main script runs as written, in one go.
22+ */
23+export function generateMain(spec: ProblemSpec, method: MethodDefinition): string {
24+ return [
25+ generateParamsBlock(spec),
26+ "",
27+ `% Solve. The modes depend only on the plate, its thread and the cutoff: the lab keeps them`,
28+ `% and reruns only the strike when the hammer, the strike point, the probe or the microphone change.`,
29+ `% ${method.modesFunction} and ${method.strikeFunction} and the functions they call are the library files in the other tabs.`,
30+ `modes = ${method.modesFunction}(params);`,
31+ `result = ${method.strikeFunction}(params, modes);`,
32+ "",
33+ ].join("\n");
34+}
35+
36+/** Bundle for running the method on a spec in one script. */
37+export function buildRunBundle(spec: ProblemSpec, method: MethodDefinition): SourceBundle {
38+ return {
39+ files: [{ name: MAIN_FILE, source: generateMain(spec, method) }, ...LIBRARY_FILES],
40+ entry: MAIN_FILE,
41+ resultVariable: "result",
42+ };
43+}
44+
45+/** Stage 1 of a run: the modes, read back by the host and kept. */
46+export function buildModesBundle(spec: ProblemSpec, method: MethodDefinition): SourceBundle {
47+ const main = [
48+ generateParamsBlock(spec),
49+ "",
50+ `% Stage 1 of a run: the modes, which the lab keeps while the plate and its thread stay the same.`,
51+ `modes = ${method.modesFunction}(params);`,
52+ "",
53+ ].join("\n");
54+ return {
55+ files: [{ name: "modes.m", source: main }, ...LIBRARY_FILES],
56+ entry: "modes.m",
57+ resultVariable: "modes",
58+ };
59+}
60+
61+/** Stage 2 of a run: the strike, with the modes passed in as the workspace variable `modes`. */
62+export function buildStrikeBundle(spec: ProblemSpec, method: MethodDefinition): SourceBundle {
63+ const main = [
64+ generateParamsBlock(spec),
65+ "",
66+ `% Stage 2 of a run: the strike. The workspace variable modes is the result of stage 1, passed in by the host.`,
67+ `result = ${method.strikeFunction}(params, modes);`,
68+ "",
69+ ].join("\n");
70+ return {
71+ files: [{ name: "strike.m", source: main }, ...LIBRARY_FILES],
72+ entry: "strike.m",
73+ resultVariable: "result",
74+ };
75+}
76+
77+/** Bundle that evaluates the plan: sizes and estimates before a run. */
78+export function buildPlanBundle(spec: ProblemSpec, method: MethodDefinition): SourceBundle {
79+ const main = [generateParamsBlock(spec), "", `plan = ${method.planFunction}(params);`, ""].join(
80+ "\n",
81+ );
82+ return {
83+ files: [{ name: "plan.m", source: main }, ...LIBRARY_FILES],
84+ entry: "plan.m",
85+ resultVariable: "plan",
86+ };
87+}
88+
89+/**
90+ * Replace files in a bundle with edited versions, keeping everything else. Used
91+ * when the user edits source in the code view: the run then uses exactly the
92+ * text on screen.
93+ */
94+export function withEdits(bundle: SourceBundle, edits: Record<string, string>): SourceBundle {
95+ return {
96+ ...bundle,
97+ files: bundle.files.map((f) =>
98+ edits[f.name] !== undefined ? { ...f, source: edits[f.name] } : f,
99+ ),
100+ };
101+}
packages/core/src/generate/format.tsadded+37−0View file
@@ -0,0 +1,37 @@
1+/** Format a number as a MATLAB literal that round-trips exactly and reads well. */
2+export function formatNumber(x: number): string {
3+ if (!Number.isFinite(x)) return x > 0 ? "Inf" : x < 0 ? "-Inf" : "NaN";
4+ if (x === 0) return "0";
5+ const ax = Math.abs(x);
6+ if (Number.isInteger(x) && ax < 1e6) return String(x);
7+ if (ax >= 1e5 || ax < 1e-4) {
8+ // shortest exponential form that round-trips, written like 200e9 rather than 2e+11
9+ for (let p = 0; p <= 16; p++) {
10+ const s = x.toExponential(p);
11+ if (Number(s) === x) return tidyExponential(s);
12+ }
13+ return tidyExponential(x.toExponential());
14+ }
15+ return String(x);
16+}
17+
18+function tidyExponential(s: string): string {
19+ const [mant, expo] = s.split("e");
20+ const e = Number(expo);
21+ // Prefer engineering-style exponents (multiples of 3) when the mantissa stays short
22+ const shift = ((e % 3) + 3) % 3;
23+ if (shift !== 0) {
24+ const mNum = Number(mant) * Math.pow(10, shift);
25+ const mStr = String(Number(mNum.toPrecision(15)));
26+ if (mStr.length <= 8) return `${mStr}e${e - shift}`;
27+ }
28+ return `${mant}e${e}`;
29+}
30+
31+export function formatString(s: string): string {
32+ return `'${s.replace(/'/g, "''")}'`;
33+}
34+
35+export function formatVector(v: readonly number[]): string {
36+ return `[${v.map(formatNumber).join(" ")}]`;
37+}
packages/core/src/generate/library.tsadded+190−0View file
@@ -0,0 +1,190 @@
1+import plate_setup from "../m/plate_setup.m?raw";
2+import plate_hertz from "../m/plate_hertz.m?raw";
3+import plate_gauss_legendre from "../m/plate_gauss_legendre.m?raw";
4+import plate_damping from "../m/plate_damping.m?raw";
5+import plate_radiation from "../m/plate_radiation.m?raw";
6+import plate_modal_free from "../m/plate_modal_free.m?raw";
7+import plate_synthesize from "../m/plate_synthesize.m?raw";
8+import plate_stress from "../m/plate_stress.m?raw";
9+import plate_spectrum from "../m/plate_spectrum.m?raw";
10+import plate_plan_common from "../m/plate_plan_common.m?raw";
11+import plate_rr_modes from "../m/plate_rr_modes.m?raw";
12+import plate_rr_strike from "../m/plate_rr_strike.m?raw";
13+import plate_rr_contact from "../m/plate_rr_contact.m?raw";
14+import plate_rr_basis from "../m/plate_rr_basis.m?raw";
15+import plate_rr_legendre from "../m/plate_rr_legendre.m?raw";
16+import plate_rr_plan from "../m/plate_rr_plan.m?raw";
17+import plate_fem_modes from "../m/plate_fem_modes.m?raw";
18+import plate_fem_strike from "../m/plate_fem_strike.m?raw";
19+import plate_fem_contact from "../m/plate_fem_contact.m?raw";
20+import plate_fem_rhs from "../m/plate_fem_rhs.m?raw";
21+import plate_fem_force from "../m/plate_fem_force.m?raw";
22+import plate_fem_assemble from "../m/plate_fem_assemble.m?raw";
23+import plate_fem_element from "../m/plate_fem_element.m?raw";
24+import plate_fem_shape from "../m/plate_fem_shape.m?raw";
25+import plate_fem_interp from "../m/plate_fem_interp.m?raw";
26+import plate_fem_eigs from "../m/plate_fem_eigs.m?raw";
27+import plate_fem_nodal from "../m/plate_fem_nodal.m?raw";
28+import plate_fem_plan from "../m/plate_fem_plan.m?raw";
29+import plate_result_schema from "../m/plate_result_schema.m?raw";
30+
31+export interface SourceFile {
32+ name: string;
33+ source: string;
34+}
35+
36+export interface LibraryFile extends SourceFile {
37+ /** One line for the code view's file list */
38+ summary: string;
39+}
40+
41+/** Every `.m` file the solvers can call. The runner ships all of them with each run. */
42+export const LIBRARY_FILES: LibraryFile[] = [
43+ {
44+ name: "plate_rr_modes.m",
45+ source: plate_rr_modes,
46+ summary: "Rayleigh-Ritz: Kirchhoff-Love energy on a tensor-Legendre basis, dense eigensolve",
47+ },
48+ {
49+ name: "plate_rr_strike.m",
50+ source: plate_rr_strike,
51+ summary: "Strike the Rayleigh-Ritz modes: basis at the strike point, damping, pulse, synthesis",
52+ },
53+ {
54+ name: "plate_rr_contact.m",
55+ source: plate_rr_contact,
56+ summary: "The prescribed Hertz pulse and the modal states it leaves, by quadrature",
57+ },
58+ {
59+ name: "plate_rr_basis.m",
60+ source: plate_rr_basis,
61+ summary: "The tensor-Legendre basis and its curvatures at points of the plate",
62+ },
63+ {
64+ name: "plate_rr_legendre.m",
65+ source: plate_rr_legendre,
66+ summary: "Legendre polynomials and their derivatives by recurrence",
67+ },
68+ {
69+ name: "plate_rr_plan.m",
70+ source: plate_rr_plan,
71+ summary: "Sizes and estimates of a Rayleigh-Ritz run, before it runs",
72+ },
73+ {
74+ name: "plate_fem_modes.m",
75+ source: plate_fem_modes,
76+ summary: "Finite elements: assemble the Mindlin-Reissner mesh and solve for its lowest modes",
77+ },
78+ {
79+ name: "plate_fem_strike.m",
80+ source: plate_fem_strike,
81+ summary: "Strike the finite-element modes: interpolation, damping, coupled contact, synthesis",
82+ },
83+ {
84+ name: "plate_fem_contact.m",
85+ source: plate_fem_contact,
86+ summary: "The hammer and the plate as one system: Runge-Kutta integration to separation",
87+ },
88+ {
89+ name: "plate_fem_rhs.m",
90+ source: plate_fem_rhs,
91+ summary: "Time derivative of the coupled hammer/plate state",
92+ },
93+ {
94+ name: "plate_fem_force.m",
95+ source: plate_fem_force,
96+ summary: "The Hunt-Crossley contact force",
97+ },
98+ {
99+ name: "plate_fem_assemble.m",
100+ source: plate_fem_assemble,
101+ summary: "Global sparse stiffness and mass of the mesh, with the thread's spring at the hole",
102+ },
103+ {
104+ name: "plate_fem_element.m",
105+ source: plate_fem_element,
106+ summary: "Stiffness and mass of one Q4 Mindlin-Reissner element, 2 x 2 Gauss points",
107+ },
108+ {
109+ name: "plate_fem_shape.m",
110+ source: plate_fem_shape,
111+ summary: "Bilinear shape functions of the four-node element",
112+ },
113+ {
114+ name: "plate_fem_interp.m",
115+ source: plate_fem_interp,
116+ summary: "Which element holds a point, and its bilinear weights",
117+ },
118+ {
119+ name: "plate_fem_eigs.m",
120+ source: plate_fem_eigs,
121+ summary: "Lowest eigenpairs of the sparse pencil by shift-invert subspace iteration",
122+ },
123+ {
124+ name: "plate_fem_nodal.m",
125+ source: plate_fem_nodal,
126+ summary: "Element-centre values averaged to the nodes",
127+ },
128+ {
129+ name: "plate_fem_plan.m",
130+ source: plate_fem_plan,
131+ summary: "Sizes and estimates of a finite-element run, before it runs",
132+ },
133+ {
134+ name: "plate_setup.m",
135+ source: plate_setup,
136+ summary: "Derived constants: rigidity, the hole, the thread's spring, the cutoff",
137+ },
138+ {
139+ name: "plate_hertz.m",
140+ source: plate_hertz,
141+ summary: "Hertz contact constants: effective modulus, stiffness, duration, impulse",
142+ },
143+ {
144+ name: "plate_damping.m",
145+ source: plate_damping,
146+ summary: "Modal damping from the material, the air and the thread's dashpot",
147+ },
148+ {
149+ name: "plate_radiation.m",
150+ source: plate_radiation,
151+ summary: "Pressure at the microphone per unit modal acceleration",
152+ },
153+ {
154+ name: "plate_modal_free.m",
155+ source: plate_modal_free,
156+ summary: "Free decay of one mode after the contact ends",
157+ },
158+ {
159+ name: "plate_synthesize.m",
160+ source: plate_synthesize,
161+ summary: "Sound, series, frames, stresses and spectra from the modes and the contact",
162+ },
163+ {
164+ name: "plate_stress.m",
165+ source: plate_stress,
166+ summary: "Surface stresses, von Mises and principal stress from curvatures",
167+ },
168+ { name: "plate_spectrum.m", source: plate_spectrum, summary: "Windowed amplitude spectrum" },
169+ {
170+ name: "plate_gauss_legendre.m",
171+ source: plate_gauss_legendre,
172+ summary: "Gauss-Legendre nodes and weights by Golub-Welsch",
173+ },
174+ {
175+ name: "plate_plan_common.m",
176+ source: plate_plan_common,
177+ summary: "The estimates both plans share: mass, pendulum, expected modes, Hertz",
178+ },
179+ {
180+ name: "plate_result_schema.m",
181+ source: plate_result_schema,
182+ summary: "Documentation of the modes and result structs",
183+ },
184+];
185+
186+export function libraryFile(name: string): LibraryFile {
187+ const f = LIBRARY_FILES.find((x) => x.name === name);
188+ if (!f) throw new Error(`unknown library file ${name}`);
189+ return f;
190+}
packages/core/src/generate/params.tsadded+28−0View file
@@ -0,0 +1,28 @@
1+import type { ProblemSpec } from "../spec/types";
2+import { formatNumber as n, formatString as s } from "./format";
3+
4+/**
5+ * Render the specification as the MATLAB `params` struct. The controls edit the
6+ * spec; this block is what the solver actually reads, so the two cannot drift.
7+ */
8+export function generateParamsBlock(spec: ProblemSpec): string {
9+ const { plate, material, hole, thread, hammer, strike, probe, mic, render, rr, fem } = spec;
10+ const lines = [
11+ "% Problem specification, generated from the controls. SI units: m, s, N, Pa, kg/m^3.",
12+ "% Coordinates are measured from the plate's centre, x to the right, y towards the threaded top edge.",
13+ "% Edit anything here or in the library files; the controls regenerate only this block.",
14+ "params = struct();",
15+ `params.plate = struct('width', ${n(plate.width)}, 'height', ${n(plate.height)}, 'thickness', ${n(plate.thickness)}); % sides and thickness [m]`,
16+ `params.material = struct('name', ${s(material.name)}, 'E', ${n(material.E)}, 'nu', ${n(material.nu)}, 'rho', ${n(material.rho)}, 'Q', ${n(material.Q)}); % Young's modulus [Pa], Poisson's ratio, density [kg/m^3], structural quality factor`,
17+ `params.hole = struct('diameter', ${n(hole.diameter)}, 'edge_offset', ${n(hole.edge_offset)}); % the suspension hole on the centreline, this far below the top edge [m]`,
18+ `params.thread = struct('length', ${n(thread.length)}, 'dashpot', ${n(thread.dashpot)}); % thread length [m] (pendulum spring m g / L) and the loss at the hole [N s/m]`,
19+ `params.hammer = struct('mass', ${n(hammer.mass)}, 'tip_radius', ${n(hammer.tip_radius)}, 'speed', ${n(hammer.speed)}, 'E', ${n(hammer.E)}, 'nu', ${n(hammer.nu)}, 'restitution', ${n(hammer.restitution)}); % mass [kg], tip radius [m], approach speed [m/s], tip modulus [Pa] and Poisson's ratio, nominal restitution`,
20+ `params.strike = struct('x', ${n(strike.x)}, 'y', ${n(strike.y)}); % where the hammer lands [m from the centre]`,
21+ `params.probe = struct('x', ${n(probe.x)}, 'y', ${n(probe.y)}); % where the time series are recorded [m from the centre]`,
22+ `params.mic = struct('distance', ${n(mic.distance)}, 'air_density', ${n(mic.air_density)}, 'sound_speed', ${n(mic.sound_speed)}); % microphone on the plate's normal [m], air [kg/m^3], [m/s]`,
23+ `params.render = struct('fs', ${n(render.fs)}, 'duration', ${n(render.duration)}, 'modes', ${n(render.modes)}, 'normalize_dbfs', ${n(render.normalize_dbfs)}, 'nframes', ${n(render.nframes)}, 'frame_span', ${n(render.frame_span)}); % sample rate [Hz] (modes kept below 0.47 fs), duration [s], mode cap, export peak [dBFS], frames over the first frame_span [s]`,
24+ `params.rr = struct('basis_order', ${n(rr.basis_order)}, 'quadrature_order', ${n(rr.quadrature_order)}, 'grid', ${n(rr.grid)}); % Rayleigh-Ritz: Legendre order per side, quadrature points per side, display grid per side`,
25+ `params.fem = struct('mesh_x', ${n(fem.mesh_x)}, 'mesh_y', ${n(fem.mesh_y)}); % finite elements: Q4 elements along x and y`,
26+ ];
27+ return lines.join("\n");
28+}
packages/core/src/index.tsadded+24−0View file
@@ -0,0 +1,24 @@
1+export * from "./spec/types";
2+export * from "./spec/presets";
3+export * from "./spec/defaults";
4+export * from "./spec/validate";
5+export * from "./methods/types";
6+export * from "./methods/registry";
7+export { RR_METHOD } from "./methods/rr";
8+export { FEM_METHOD } from "./methods/fem";
9+export * from "./methods/common";
10+export * from "./generate/format";
11+export * from "./generate/params";
12+export * from "./generate/library";
13+export * from "./generate/bundle";
14+export * from "./runner/plain";
15+export * from "./runner/types";
16+export { WorkerRunner } from "./runner/workerRunner";
17+export * from "./results/types";
18+export * from "./results/parse";
19+export * from "./fields/field";
20+export * from "./fields/colormap";
21+export * from "./fields/raster";
22+export * from "./audio/buffer";
23+export * from "./audio/wav";
24+export * from "./cost";
packages/core/src/m/plate_damping.madded+27−0View file
@@ -0,0 +1,27 @@
1+function [zeta, Q] = plate_damping(freqs, support_part, params, P)
2+% PLATE_DAMPING Modal damping ratios from three losses.
3+%
4+% [zeta, Q] = plate_damping(freqs, support_part, params, P) combines, for
5+% each mode of frequency f,
6+% material loss 1 / Q_material (params.material.Q)
7+% air loading 1 / Q_air, Q_air = 1100 / sqrt(f / 1 kHz), floored at 50 Hz
8+% thread dashpot c phi_hole^2 / omega, the dashpot c at the hole acting on
9+% the mass-normalized mode's displacement phi_hole there
10+% into 1/Q = sum of the three; zeta = min(0.08, 1 / (2 Q)) is the damping
11+% ratio and Q the total quality factor. Rigid-body modes (below 20 Hz, the
12+% finite-element method keeps three) get zeta = 0.01 so that momentum can
13+% flow into them during the collision without their being clamped.
14+%
15+% These are the constants of the simulators this lab follows: order-of-
16+% magnitude figures, not measurements of a particular plate.
17+
18+freqs = freqs(:)';
19+support_part = support_part(:)';
20+omega = 2 * pi * freqs;
21+q_air = 1100 ./ sqrt(max(freqs / 1000, 0.05));
22+inv_q = 1 / params.material.Q + 1 ./ q_air;
23+inv_q = inv_q + params.thread.dashpot * support_part.^2 ./ max(omega, 1);
24+zeta = min(0.08, 0.5 * inv_q);
25+zeta(freqs < 20) = 0.01;
26+Q = 1 ./ inv_q;
27+end
packages/core/src/m/plate_fem_assemble.madded+74−0View file
@@ -0,0 +1,74 @@
1+function A = plate_fem_assemble(params, P)
2+% PLATE_FEM_ASSEMBLE The global stiffness and mass of the meshed plate on its thread.
3+%
4+% A = plate_fem_assemble(params, P) meshes the plate into
5+% params.fem.mesh_x by params.fem.mesh_y rectangular elements, sums the
6+% element matrices of plate_fem_element into sparse K and M (three
7+% unknowns per node, nodes numbered with x fastest: node = ey (nx + 1) + ex),
8+% and adds the thread's pendulum spring m g / L to the transverse
9+% displacement at the hole, interpolated within the element that holds it
10+% (plate_fem_interp). The hole itself is not cut from the mesh: its
11+% 0.8 mm diameter is below the element size at every mesh offered here,
12+% and its mass is a part in a thousand of the plate's.
13+%
14+% Returns K, M, support (the interpolation vector of the hole as a sparse
15+% column), ndof, nn (nodes), nx, ny, dx, dy, grid_x, grid_y (node
16+% coordinates) and wdofs (the indices of the transverse unknowns in node
17+% order).
18+
19+nx = params.fem.mesh_x;
20+ny = params.fem.mesh_y;
21+if nx < 4 || ny < 4
22+ error('plate_fem_assemble: the mesh needs at least 4 elements per side');
23+end
24+W = params.plate.width;
25+H = params.plate.height;
26+dx = W / nx;
27+dy = H / ny;
28+[ke, me] = plate_fem_element(dx, dy, params, P);
29+
30+[EX, EY] = ndgrid(0:nx - 1, 0:ny - 1);
31+ex = EX(:);
32+ey = EY(:);
33+nel = numel(ex);
34+n1 = ey * (nx + 1) + ex;
35+nodes = [n1, n1 + 1, n1 + nx + 2, n1 + nx + 1]; % anticlockwise, zero-based
36+dofs = zeros(nel, 12);
37+for a = 1:4
38+ dofs(:, 3 * a - 2) = 3 * nodes(:, a) + 1;
39+ dofs(:, 3 * a - 1) = 3 * nodes(:, a) + 2;
40+ dofs(:, 3 * a) = 3 * nodes(:, a) + 3;
41+end
42+rows = kron(dofs, ones(1, 12));
43+cols = repmat(dofs, 1, 12);
44+kv = repmat(reshape(ke', 1, 144), nel, 1);
45+mv = repmat(reshape(me', 1, 144), nel, 1);
46+nn = (nx + 1) * (ny + 1);
47+ndof = 3 * nn;
48+K = sparse(rows(:), cols(:), kv(:), ndof, ndof);
49+M = sparse(rows(:), cols(:), mv(:), ndof, ndof);
50+
51+% the thread at the hole
52+[idx, wts] = plate_fem_interp(P.hole_x, P.hole_y, params);
53+nyn = ny + 1;
54+% node grid index (i + (j - 1) nyn, rows y) -> node number ey (nx + 1) + ex -> w unknown
55+i_ = mod(idx - 1, nyn);
56+j_ = floor((idx - 1) / nyn);
57+node = i_ * (nx + 1) + j_;
58+wd = 3 * node + 1;
59+support = sparse(wd(:), ones(4, 1), wts(:), ndof, 1);
60+K = K + P.thread_k * (support * support');
61+
62+A.K = K;
63+A.M = M;
64+A.support = support;
65+A.ndof = ndof;
66+A.nn = nn;
67+A.nx = nx;
68+A.ny = ny;
69+A.dx = dx;
70+A.dy = dy;
71+A.grid_x = linspace(-W / 2, W / 2, nx + 1);
72+A.grid_y = linspace(-H / 2, H / 2, ny + 1);
73+A.wdofs = (1:3:ndof)';
74+end
packages/core/src/m/plate_fem_contact.madded+168−0View file
@@ -0,0 +1,168 @@
1+function C = plate_fem_contact(params, P, freqs, zeta, phi_hit, t)
2+% PLATE_FEM_CONTACT The hammer and the plate as one system during the collision.
3+%
4+% C = plate_fem_contact(params, P, freqs, zeta, phi_hit, t) integrates the
5+% coupled equations of plate_fem_rhs from the hammer touching the plate
6+% at its speed (x_h = 0, v_h = speed, the plate at rest) until the two
7+% separate, delta = x_h - phi' q falling through zero. All the modes take
8+% part, the three rigid-body ones included, so that the very light plate
9+% can move away from the hammer while they are in contact: this is what
10+% makes the force lower and the contact longer than the rigid-target
11+% pulse of plate_rr_contact, and the hammer's momentum transfer smaller.
12+%
13+% The integrator is classical fourth-order Runge-Kutta with a fixed step,
14+% the Hertz duration estimate divided by 2000; at 0.05 us the highest
15+% mode kept (below 0.47 fs) advances by less than a hundredth of a radian
16+% per step. Separation is located by bisection within the step in which
17+% delta changes sign. The stored steps and their derivatives give a cubic
18+% Hermite interpolant, from which the states at the audio samples inside
19+% the contact (C.qs, C.vs, C.fs at C.ts), and the histories on a 600-point
20+% grid (C.tg: the force C.fg, the hammer C.hammer_x and C.hammer_v, the
21+% plate under it C.plate_w and C.plate_v, and the modal states C.qg, C.vg
22+% for the frames) are read. The impulse is the trapezoidal integral of
23+% the force; the effective restitution is the relative separation speed
24+% over the approach speed.
25+
26+hm = params.hammer;
27+H = P.hertz;
28+omega = 2 * pi * freqs(:);
29+zeta = zeta(:);
30+phi = phi_hit(:)';
31+nm = numel(omega);
32+twozw = 2 * zeta .* omega;
33+k = H.contact_k;
34+hc = H.hunt_crossley;
35+mass = hm.mass;
36+est = H.duration;
37+nsub = 2000;
38+h = est / nsub;
39+nmax = 8 * nsub;
40+
41+y = zeros(2 * nm + 2, 1);
42+y(end - 1) = 1e-12;
43+y(end) = hm.speed;
44+Ys = zeros(2 * nm + 2, nmax + 1);
45+Fs = zeros(2 * nm + 2, nmax + 1);
46+Ys(:, 1) = y;
47+Fs(:, 1) = plate_fem_rhs(y, phi, nm, omega, twozw, k, hc, mass);
48+found = false;
49+tc = 0;
50+yc = y;
51+nsteps = 0;
52+for step = 1:nmax
53+ [ynew, fnew] = rk4_step(y, h, phi, nm, omega, twozw, k, hc, mass);
54+ sep = ynew(end - 1) - phi * ynew(1:nm);
55+ if step * h >= 1e-10 && sep < 0
56+ % bisection on the step length from the last stored state
57+ lo = 0;
58+ hi = h;
59+ for b = 1:60
60+ mid = 0.5 * (lo + hi);
61+ ym = rk4_step(y, mid, phi, nm, omega, twozw, k, hc, mass);
62+ if ym(end - 1) - phi * ym(1:nm) < 0
63+ hi = mid;
64+ else
65+ lo = mid;
66+ end
67+ if hi - lo < 1e-18
68+ break
69+ end
70+ end
71+ tc = (step - 1) * h + hi;
72+ yc = rk4_step(y, hi, phi, nm, omega, twozw, k, hc, mass);
73+ found = true;
74+ nsteps = step - 1;
75+ break
76+ end
77+ y = ynew;
78+ Ys(:, step + 1) = y;
79+ Fs(:, step + 1) = fnew;
80+ nsteps = step;
81+end
82+if ~found
83+ error('plate_fem_contact: the hammer did not separate within %.3g us (8 Hertz durations)', 8 * est * 1e6);
84+end
85+Ys = Ys(:, 1:nsteps + 1);
86+Fs = Fs(:, 1:nsteps + 1);
87+fc = plate_fem_rhs(yc, phi, nm, omega, twozw, k, hc, mass);
88+
89+% states at the audio samples inside the contact and on the plotting grid
90+ts = t(t < tc);
91+Yq = hermite(ts, h, tc, Ys, Fs, yc, fc);
92+tg = linspace(0, tc, 600);
93+Yg = hermite(tg, h, tc, Ys, Fs, yc, fc);
94+fs_ = zeros(1, numel(ts));
95+for j = 1:numel(ts)
96+ fs_(j) = plate_fem_force(Yq(:, j), phi, nm, k, hc);
97+end
98+fg = zeros(1, 600);
99+for j = 1:600
100+ fg(j) = plate_fem_force(Yg(:, j), phi, nm, k, hc);
101+end
102+
103+C.model = 'coupled Hunt-Crossley/Hertz contact with the moving plate';
104+C.tc = tc;
105+C.q0 = yc(1:nm)';
106+C.v0 = yc(nm + 1:2 * nm)';
107+C.ts = ts;
108+C.qs = Yq(1:nm, :);
109+C.vs = Yq(nm + 1:2 * nm, :);
110+C.fs = fs_;
111+C.tg = tg;
112+C.qg = Yg(1:nm, :);
113+C.vg = Yg(nm + 1:2 * nm, :);
114+C.fg = fg;
115+C.hammer_x = Yg(end - 1, :);
116+C.hammer_v = Yg(end, :);
117+C.plate_w = phi * C.qg;
118+C.plate_v = phi * C.vg;
119+C.impulse = trapz(tg, fg);
120+C.peak_force = max(fg);
121+C.hammer_exit_v = yc(end);
122+C.plate_exit_v = phi * yc(nm + 1:2 * nm);
123+C.relative_separation_v = C.hammer_exit_v - C.plate_exit_v;
124+C.effective_restitution = -C.relative_separation_v / max(hm.speed, 1e-9);
125+C.steps = nsteps;
126+C.notes = {sprintf('coupled contact: %.4g us long, %.4g N peak, %.4g mN s; the hammer leaves at %.3g m/s into the plate, the plate under it at %.3g m/s (%d Runge-Kutta steps of %.3g ns)', ...
127+ tc * 1e6, C.peak_force, C.impulse * 1e3, C.hammer_exit_v, C.plate_exit_v, nsteps, h * 1e9)};
128+end
129+
130+function [ynew, fnew] = rk4_step(y, h, phi, nm, omega, twozw, k, hc, mass)
131+k1 = plate_fem_rhs(y, phi, nm, omega, twozw, k, hc, mass);
132+k2 = plate_fem_rhs(y + 0.5 * h * k1, phi, nm, omega, twozw, k, hc, mass);
133+k3 = plate_fem_rhs(y + 0.5 * h * k2, phi, nm, omega, twozw, k, hc, mass);
134+k4 = plate_fem_rhs(y + h * k3, phi, nm, omega, twozw, k, hc, mass);
135+ynew = y + h / 6 * (k1 + 2 * k2 + 2 * k3 + k4);
136+fnew = plate_fem_rhs(ynew, phi, nm, omega, twozw, k, hc, mass);
137+end
138+
139+function Y = hermite(times, h, tc, Ys, Fs, yc, fc)
140+% cubic Hermite interpolation of the stored trajectory at the given times in [0, tc]
141+n = size(Ys, 2) - 1; % whole steps stored
142+Y = zeros(size(Ys, 1), numel(times));
143+for j = 1:numel(times)
144+ tj = min(max(times(j), 0), tc);
145+ i = floor(tj / h);
146+ if i < n
147+ t0 = i * h;
148+ hh = h;
149+ y0 = Ys(:, i + 1); f0 = Fs(:, i + 1);
150+ y1 = Ys(:, i + 2); f1 = Fs(:, i + 2);
151+ else
152+ t0 = n * h;
153+ hh = tc - t0;
154+ y0 = Ys(:, n + 1); f0 = Fs(:, n + 1);
155+ y1 = yc; f1 = fc;
156+ end
157+ if hh <= 0
158+ Y(:, j) = y0;
159+ continue
160+ end
161+ s = (tj - t0) / hh;
162+ h00 = 2 * s^3 - 3 * s^2 + 1;
163+ h10 = s^3 - 2 * s^2 + s;
164+ h01 = -2 * s^3 + 3 * s^2;
165+ h11 = s^3 - s^2;
166+ Y(:, j) = h00 * y0 + h10 * hh * f0 + h01 * y1 + h11 * hh * f1;
167+end
168+end
packages/core/src/m/plate_fem_eigs.madded+99−0View file
@@ -0,0 +1,99 @@
1+function [lam, V, info] = plate_fem_eigs(K, M, nwant, nb, maxit, tol)
2+% PLATE_FEM_EIGS The lowest eigenpairs of K v = lambda M v by subspace iteration.
3+%
4+% [lam, V, info] = plate_fem_eigs(K, M, nwant, nb, maxit, tol) finds the
5+% nwant smallest eigenvalues of the sparse symmetric pencil (K, M), K
6+% positive semidefinite and M positive definite, with a block of nb >
7+% nwant vectors. Each iteration maps the block through the shifted
8+% inverse,
9+%
10+% Y = (K - sigma M)^-1 M X, sigma = -(2 pi 1000 Hz)^2 < 0,
11+%
12+% then projects K and M onto Y (Rayleigh-Ritz) and takes the Ritz vectors
13+% as the next block. A negative shift keeps K - sigma M positive definite,
14+% so one sparse Cholesky factorization after an approximate-minimum-degree
15+% ordering serves every iteration, and the eigenvalues nearest the shift,
16+% which are the lowest, converge first. The extra nb - nwant vectors
17+% guard the wanted ones: eigenvalue i converges like
18+% ((lambda_i - sigma) / (lambda_{nb+1} - sigma))^iteration. Degenerate
19+% pairs, which a square plate has many of, are found as reliably as
20+% simple eigenvalues, which is why this is a block method.
21+%
22+% The iteration stops when the wanted eigenvalues change by less than the
23+% relative tolerance tol between iterations, or after maxit iterations;
24+% info reports the count, the largest relative residual
25+% |K v - lambda M v| / |K v| among the wanted pairs, and the shift. The
26+% rigid-body modes sit at lambda = 0, so both measures are floored at the
27+% scale of a 20 Hz mode rather than taken relative to nothing.
28+% Eigenvalues ascend and the eigenvectors are M-normalized.
29+%
30+% numbl's own eigs is far slower on this problem; this routine is written
31+% so that the whole method stays in double precision in numbl.
32+
33+ndof = size(K, 1);
34+nb = min(nb, ndof);
35+nwant = min(nwant, nb);
36+sigma = -(2 * pi * 1000)^2;
37+Ks = K - sigma * M;
38+p = amd(Ks);
39+R = chol(Ks(p, p));
40+rng(12345);
41+X = randn(ndof, nb);
42+lam_prev = Inf * ones(nb, 1);
43+lam_floor = (2 * pi * 20)^2;
44+converged = false;
45+it = 0;
46+lam = zeros(nb, 1);
47+while it < maxit
48+ it = it + 1;
49+ B = M * X;
50+ Yp = R \ (R' \ B(p, :));
51+ Y = zeros(ndof, nb);
52+ Y(p, :) = Yp;
53+ % the columns shrink like 1 / (lambda - sigma), four orders of magnitude across the
54+ % block; scaling them keeps the projected mass matrix well conditioned
55+ Y = Y ./ repmat(sqrt(sum(Y.^2, 1)), ndof, 1);
56+ KY = K * Y;
57+ MY = M * Y;
58+ Kp = Y' * KY;
59+ Mp = Y' * MY;
60+ Kp = (Kp + Kp') / 2;
61+ Mp = (Mp + Mp') / 2;
62+ Rm = chol(Mp);
63+ Cm = Rm' \ Kp / Rm;
64+ Cm = (Cm + Cm') / 2;
65+ [Vc, Dc] = eig(Cm);
66+ [lam, order] = sort(diag(Dc));
67+ Vc = Vc(:, order);
68+ X = Y * (Rm \ Vc);
69+ % The rigid-body modes sit at zero with rounding noise of order eps |K|, so
70+ % they are only asked to be stable in absolute terms; the modes above the
71+ % scale of a 20 Hz mode are judged relatively.
72+ wanted = lam(1:nwant);
73+ prev = lam_prev(1:nwant);
74+ big = abs(wanted) >= lam_floor;
75+ rel = abs(wanted - prev) ./ max(abs(wanted), lam_floor);
76+ change = max([rel(big); 0]);
77+ small_ok = all(abs(wanted(~big) - prev(~big)) < 1e-3 * lam_floor);
78+ lam_prev = lam;
79+ if change < tol && small_ok
80+ converged = true;
81+ break
82+ end
83+end
84+V = X(:, 1:nwant);
85+lam = lam(1:nwant);
86+% residuals of the wanted pairs
87+res = zeros(nwant, 1);
88+KV = K * V;
89+MV = M * V;
90+for k = 1:nwant
91+ r = KV(:, k) - lam(k) * MV(:, k);
92+ res(k) = norm(r) / max(norm(KV(:, k)), lam_floor * norm(MV(:, k)));
93+end
94+info.iterations = it;
95+info.converged = converged;
96+info.residual = max(res);
97+info.sigma = sigma;
98+info.block = nb;
99+end
packages/core/src/m/plate_fem_element.madded+61−0View file
@@ -0,0 +1,61 @@
1+function [ke, me] = plate_fem_element(dx, dy, params, P)
2+% PLATE_FEM_ELEMENT Stiffness and mass of one Q4 Mindlin-Reissner element.
3+%
4+% [ke, me] = plate_fem_element(dx, dy, params, P) integrates, with 2 x 2
5+% Gauss points, the bending energy of the rotations and the transverse-
6+% shear energy of a rectangular element dx x dy with three unknowns per
7+% node (w, theta_x, theta_y):
8+%
9+% U = 1/2 integral ( kappa' Db kappa + gamma' Ds gamma ) dA,
10+% kappa = (d theta_x / dx, d theta_y / dy, d theta_x / dy + d theta_y / dx),
11+% gamma = (w_x + theta_x, w_y + theta_y),
12+% Db = D [1 nu 0; nu 1 0; 0 0 (1 - nu) / 2], Ds = k G h I, k = 5/6,
13+%
14+% and the kinetic energy with the translational mass rho h and the rotary
15+% inertia rho h^3 / 12. Full integration of the shear term is what the
16+% simulator this lab follows does; on a thin plate it locks, which is why
17+% that simulator needs its 80 x 80 mesh to converge (see the README).
18+% Degrees of freedom are ordered (w, theta_x, theta_y) per node, nodes
19+% anticlockwise from the lower-left corner.
20+
21+mat = params.material;
22+h = params.plate.thickness;
23+nu = mat.nu;
24+bend = P.D * [1, nu, 0; nu, 1, 0; 0, 0, 0.5 * (1 - nu)];
25+shear_modulus = mat.E / (2 * (1 + nu));
26+kappa = 5 / 6;
27+shear = kappa * shear_modulus * h * eye(2);
28+rho = mat.rho;
29+ke = zeros(12, 12);
30+me = zeros(12, 12);
31+gp = 1 / sqrt(3);
32+detj = dx * dy / 4;
33+for xi = [-gp, gp]
34+ for eta = [-gp, gp]
35+ [n, dxi, deta] = plate_fem_shape(xi, eta);
36+ dnx = dxi * 2 / dx;
37+ dny = deta * 2 / dy;
38+ bb = zeros(3, 12);
39+ bs = zeros(2, 12);
40+ for a = 1:4
41+ w = 3 * a - 2; rx = 3 * a - 1; ry = 3 * a;
42+ bb(1, rx) = dnx(a);
43+ bb(2, ry) = dny(a);
44+ bb(3, rx) = dny(a);
45+ bb(3, ry) = dnx(a);
46+ bs(1, w) = dnx(a); bs(1, rx) = n(a);
47+ bs(2, w) = dny(a); bs(2, ry) = n(a);
48+ end
49+ ke = ke + (bb' * bend * bb + bs' * shear * bs) * detj;
50+ for a = 1:4
51+ for b = 1:4
52+ nn = n(a) * n(b) * detj;
53+ me(3 * a - 2, 3 * b - 2) = me(3 * a - 2, 3 * b - 2) + rho * h * nn;
54+ rotary = rho * h^3 / 12 * nn;
55+ me(3 * a - 1, 3 * b - 1) = me(3 * a - 1, 3 * b - 1) + rotary;
56+ me(3 * a, 3 * b) = me(3 * a, 3 * b) + rotary;
57+ end
58+ end
59+ end
60+end
61+end
packages/core/src/m/plate_fem_force.madded+23−0View file
@@ -0,0 +1,23 @@
1+function F = plate_fem_force(y, phi, nm, contact_k, hunt_crossley)
2+% PLATE_FEM_FORCE The Hunt-Crossley contact force of the hammer on the plate.
3+%
4+% F = plate_fem_force(y, phi, nm, contact_k, hunt_crossley) evaluates, for
5+% the state y = [q; v; x_h; v_h] (modal displacements and velocities, the
6+% hammer's position and velocity, all positive into the plate),
7+%
8+% delta = x_h - phi' q, delta_dot = v_h - phi' v,
9+% F = k delta^(3/2) (1 + c delta_dot) while delta > 0, else 0,
10+%
11+% floored at zero: a Hertz spring with a velocity-dependent loss that
12+% makes the restitution come out near the nominal coefficient.
13+
14+q = y(1:nm);
15+v = y(nm + 1:2 * nm);
16+delta = y(2 * nm + 1) - phi * q;
17+if delta <= 0
18+ F = 0;
19+ return
20+end
21+delta_v = y(2 * nm + 2) - phi * v;
22+F = max(0, contact_k * delta^1.5 * (1 + hunt_crossley * delta_v));
23+end
packages/core/src/m/plate_fem_interp.madded+28−0View file
@@ -0,0 +1,28 @@
1+function [idx, wts, ex, ey] = plate_fem_interp(x, y, params)
2+% PLATE_FEM_INTERP Where a point of the plate falls in the mesh, and its shape weights.
3+%
4+% [idx, wts, ex, ey] = plate_fem_interp(x, y, params) finds the element
5+% (ex, ey, zero-based) containing the point (x, y) (metres from the
6+% plate's centre; a point on the far edge belongs to the last element) and
7+% returns the bilinear weights wts (1 x 4) of its four nodes, with idx
8+% (1 x 4) the linear index of each node in the node grid stored as an
9+% (ny + 1) x (nx + 1) array with rows indexing y. A field sampled at the
10+% nodes is interpolated as wts * field(idx).
11+
12+nx = params.fem.mesh_x;
13+ny = params.fem.mesh_y;
14+W = params.plate.width;
15+H = params.plate.height;
16+dx = W / nx;
17+dy = H / ny;
18+ux = min(max((x + W / 2) / dx, 0), nx - 1e-12);
19+uy = min(max((y + H / 2) / dy, 0), ny - 1e-12);
20+ex = min(floor(ux), nx - 1);
21+ey = min(floor(uy), ny - 1);
22+xi = 2 * (ux - ex) - 1;
23+eta = 2 * (uy - ey) - 1;
24+wts = plate_fem_shape(xi, eta);
25+nyn = ny + 1;
26+% nodes anticlockwise from (ex, ey): rows are y, so the linear index is (ey + 1) + ex * nyn
27+idx = [(ey + 1) + ex * nyn, (ey + 1) + (ex + 1) * nyn, (ey + 2) + (ex + 1) * nyn, (ey + 2) + ex * nyn];
28+end
packages/core/src/m/plate_fem_modes.madded+126−0View file
@@ -0,0 +1,126 @@
1+function M = plate_fem_modes(params)
2+% PLATE_FEM_MODES Modes of the hung plate by the Q4 Mindlin-Reissner finite-element method.
3+%
4+% M = plate_fem_modes(params) assembles the mesh (plate_fem_assemble) and
5+% solves the generalized eigenproblem K v = omega^2 M v for its lowest
6+% modes (plate_fem_eigs), enough of them to pass 0.47 fs. Three modes lie
7+% below 20 Hz: the two rigid rotations about axes through the hole, which
8+% nothing restrains, and the pendulum mode of the thread's spring. They
9+% are kept, so that the collision can put momentum into the freely hanging
10+% plate, and marked by their frequency; plate_synthesize leaves them out of
11+% the sound. The audible modes are those between 20 Hz and 0.47 fs, at
12+% most params.render.modes of them.
13+%
14+% Returns M with method 'fem', freqs (1 x nm, the low ones first), the
15+% mass-normalized transverse shapes on the node grid, shapes ((ny + 1) x
16+% (nx + 1) x nm, rows index y), the curvatures kxx, kyy, kxy of each shape
17+% (from the rotations: w_xx = -d theta_x / dx and so on, element-centre
18+% values averaged to the nodes), support_part (the shape at the hole),
19+% volume_part (the trapezoidal integral of the shape over the plate),
20+% grid_x, grid_y, the mesh sizes and the eigensolver's report.
21+
22+t_start = tic;
23+P = plate_setup(params);
24+A = plate_fem_assemble(params, P);
25+nx = A.nx;
26+ny = A.ny;
27+nyn = ny + 1;
28+nxn = nx + 1;
29+notes = {};
30+
31+% how many modes to ask for: the modal-density estimate plus a margin, then
32+% enlarge until the wanted set reaches past f_max or the cap
33+plan = plate_plan_common(params, P);
34+n_rigid = 3;
35+lam_max = (2 * pi * P.f_max)^2;
36+nwant = min(params.render.modes, plan.modes_expected + 12) + n_rigid;
37+nwant = max(nwant, n_rigid + 4);
38+tries = 0;
39+while true
40+ tries = tries + 1;
41+ nb = min(A.ndof, nwant + max(nwant, 8));
42+ [lam, V, einfo] = plate_fem_eigs(A.K, A.M, nwant, nb, 60, 1e-10);
43+ n_below = nnz(lam < lam_max);
44+ reached = lam(end) >= lam_max;
45+ capped = n_below - n_rigid >= params.render.modes;
46+ if reached || capped || nb >= A.ndof || tries >= 4
47+ break
48+ end
49+ nwant = ceil(1.5 * nwant) + 4;
50+end
51+freqs = sqrt(max(lam, 0))' / (2 * pi);
52+low = freqs < 20;
53+audible = (lam' > 0) & (freqs >= 20) & (freqs < P.f_max);
54+n_audible = nnz(audible);
55+count = min(params.render.modes, n_audible);
56+sel_a = find(audible);
57+chosen = [find(low), sel_a(1:count)];
58+freqs = freqs(chosen);
59+V = V(:, chosen);
60+nm = numel(freqs);
61+% explicit M-normalization, as the reference simulator does
62+for k = 1:nm
63+ V(:, k) = V(:, k) / sqrt(V(:, k)' * (A.M * V(:, k)));
64+end
65+if ~einfo.converged
66+ notes{end + 1} = sprintf('the eigensolver stopped after %d iterations without meeting its tolerance; largest residual %.1e', einfo.iterations, einfo.residual);
67+end
68+if ~reached && ~capped
69+ notes{end + 1} = sprintf('the eigensolver found %d modes but did not reach 0.47 fs; some audible modes may be missing', n_audible);
70+end
71+if count < n_audible
72+ notes{end + 1} = sprintf('%d modes lie below 0.47 fs; the first %d are kept (params.render.modes)', n_audible, count);
73+end
74+if nnz(low) ~= n_rigid
75+ notes{end + 1} = sprintf('%d modes below 20 Hz where 3 (two free rotations and the pendulum) were expected', nnz(low));
76+end
77+
78+% shapes on the node grid: node ey (nx + 1) + ex -> array (ey + 1, ex + 1)
79+Wn = V(A.wdofs, :);
80+shapes = permute(reshape(Wn, nxn, nyn, nm), [2, 1, 3]);
81+RX = permute(reshape(V(2:3:end, :), nxn, nyn, nm), [2, 1, 3]);
82+RY = permute(reshape(V(3:3:end, :), nxn, nyn, nm), [2, 1, 3]);
83+dx = A.dx;
84+dy = A.dy;
85+% element-centre derivatives of the rotations, then the average of the elements around each node
86+drx_dx = ((RX(1:end - 1, 2:end, :) + RX(2:end, 2:end, :)) - (RX(1:end - 1, 1:end - 1, :) + RX(2:end, 1:end - 1, :))) / (2 * dx);
87+dry_dy = ((RY(2:end, 1:end - 1, :) + RY(2:end, 2:end, :)) - (RY(1:end - 1, 1:end - 1, :) + RY(1:end - 1, 2:end, :))) / (2 * dy);
88+drx_dy = ((RX(2:end, 1:end - 1, :) + RX(2:end, 2:end, :)) - (RX(1:end - 1, 1:end - 1, :) + RX(1:end - 1, 2:end, :))) / (2 * dy);
89+dry_dx = ((RY(1:end - 1, 2:end, :) + RY(2:end, 2:end, :)) - (RY(1:end - 1, 1:end - 1, :) + RY(2:end, 1:end - 1, :))) / (2 * dx);
90+kxx = -plate_fem_nodal(drx_dx, nyn, nxn);
91+kyy = -plate_fem_nodal(dry_dy, nyn, nxn);
92+kxy = -plate_fem_nodal(drx_dy + dry_dx, nyn, nxn) / 2;
93+
94+% participations
95+support_part = (V' * A.support)';
96+area = ones(nyn, nxn) * dx * dy;
97+area([1, end], :) = area([1, end], :) * 0.5;
98+area(:, [1, end]) = area(:, [1, end]) * 0.5;
99+volume_part = (reshape(shapes, nyn * nxn, nm)' * area(:))';
100+
101+M.method = 'fem';
102+M.freqs = freqs;
103+M.support_part = support_part;
104+M.volume_part = volume_part;
105+M.shapes = shapes;
106+M.kxx = kxx;
107+M.kyy = kyy;
108+M.kxy = kxy;
109+M.grid_x = A.grid_x;
110+M.grid_y = A.grid_y;
111+M.ndof = A.ndof;
112+M.nn = A.nn;
113+M.nx = nx;
114+M.ny = ny;
115+M.dx = dx;
116+M.dy = dy;
117+M.n_rigid = nnz(low);
118+M.n_found = numel(lam);
119+M.plate_mass = P.plate_mass;
120+M.eig_iterations = einfo.iterations;
121+M.eig_residual = einfo.residual;
122+M.eig_block = einfo.block;
123+M.size_note = sprintf('%d x %d Q4 elements, %d unknowns; %d Ritz vectors, %d subspace iterations', nx, ny, A.ndof, einfo.block, einfo.iterations);
124+M.wall_s = toc(t_start);
125+M.notes = notes;
126+end
packages/core/src/m/plate_fem_nodal.madded+21−0View file
@@ -0,0 +1,21 @@
1+function N = plate_fem_nodal(E, nyn, nxn)
2+% PLATE_FEM_NODAL Element-centre values averaged to the nodes.
3+%
4+% N = plate_fem_nodal(E, nyn, nxn) takes values on the ny x nx elements
5+% (any number of layers along the third dimension) and returns the mean of
6+% the elements around each node of the (nyn = ny + 1) x (nxn = nx + 1) grid:
7+% four inside, two along an edge, one at a corner.
8+
9+nl = size(E, 3);
10+acc = zeros(nyn, nxn, nl);
11+cnt = zeros(nyn, nxn);
12+acc(1:end - 1, 1:end - 1, :) = acc(1:end - 1, 1:end - 1, :) + E;
13+acc(2:end, 1:end - 1, :) = acc(2:end, 1:end - 1, :) + E;
14+acc(1:end - 1, 2:end, :) = acc(1:end - 1, 2:end, :) + E;
15+acc(2:end, 2:end, :) = acc(2:end, 2:end, :) + E;
16+cnt(1:end - 1, 1:end - 1) = cnt(1:end - 1, 1:end - 1) + 1;
17+cnt(2:end, 1:end - 1) = cnt(2:end, 1:end - 1) + 1;
18+cnt(1:end - 1, 2:end) = cnt(1:end - 1, 2:end) + 1;
19+cnt(2:end, 2:end) = cnt(2:end, 2:end) + 1;
20+N = acc ./ repmat(cnt, [1, 1, nl]);
21+end
packages/core/src/m/plate_fem_plan.madded+30−0View file
@@ -0,0 +1,30 @@
1+function plan = plate_fem_plan(params)
2+% PLATE_FEM_PLAN Sizes and estimates of a finite-element run, before it runs.
3+%
4+% plan = plate_fem_plan(params) reports the mesh and its unknowns, the
5+% number of modes to expect below 0.47 fs, the pendulum frequency of the
6+% thread, the Hertz estimates of the contact and the plate's coincidence
7+% frequency. Cheap: no matrix is assembled. The eigensolve's cost grows
8+% a little faster than the number of unknowns; the strike stage's with the
9+% number of samples times the number of modes.
10+
11+P = plate_setup(params);
12+plan = plate_plan_common(params, P);
13+nx = params.fem.mesh_x;
14+ny = params.fem.mesh_y;
15+ndof = 3 * (nx + 1) * (ny + 1);
16+plan.method = 'fem';
17+plan.ndof = ndof;
18+plan.nx = nx;
19+plan.ny = ny;
20+plan.dx = params.plate.width / nx;
21+plan.dy = params.plate.height / ny;
22+plan.work = ndof^1.3;
23+plan.size_note = sprintf('%d x %d Q4 elements, %d unknowns', nx, ny, ndof);
24+if nx < 4 || ny < 4
25+ plan.notes{end + 1} = 'the mesh needs at least 4 elements per side';
26+end
27+if nx < 48 || ny < 48
28+ plan.notes{end + 1} = 'full-integration Q4 locks in shear on a thin plate: below 48 x 48 the partials are several percent high (the reference used 80 x 80)';
29+end
30+end
packages/core/src/m/plate_fem_rhs.madded+17−0View file
@@ -0,0 +1,17 @@
1+function dy = plate_fem_rhs(y, phi, nm, omega, twozw, contact_k, hunt_crossley, hammer_mass)
2+% PLATE_FEM_RHS Time derivative of the coupled hammer/plate state.
3+%
4+% dy = plate_fem_rhs(y, phi, nm, omega, twozw, contact_k, hunt_crossley, m)
5+% for the state y = [q; v; x_h; v_h]:
6+%
7+% q_tt = phi F - 2 zeta omega q_t - omega^2 q (each mode, mass-normalized)
8+% x_h_tt = -F / m (the hammer)
9+%
10+% with F the contact force of plate_fem_force and twozw = 2 zeta omega.
11+
12+q = y(1:nm);
13+v = y(nm + 1:2 * nm);
14+F = plate_fem_force(y, phi, nm, contact_k, hunt_crossley);
15+qdd = phi' * F - twozw .* v - omega.^2 .* q;
16+dy = [v; qdd; y(2 * nm + 2); -F / hammer_mass];
17+end
packages/core/src/m/plate_fem_shape.madded+11−0View file
@@ -0,0 +1,11 @@
1+function [n, dxi, deta] = plate_fem_shape(xi, eta)
2+% PLATE_FEM_SHAPE Bilinear shape functions of the four-node element.
3+%
4+% [n, dxi, deta] = plate_fem_shape(xi, eta) at a point of the reference
5+% square [-1, 1]^2: the four functions, ordered anticlockwise from the
6+% corner (-1, -1), and their derivatives with respect to xi and eta.
7+
8+n = 0.25 * [(1 - xi) * (1 - eta), (1 + xi) * (1 - eta), (1 + xi) * (1 + eta), (1 - xi) * (1 + eta)];
9+dxi = 0.25 * [-(1 - eta), 1 - eta, 1 + eta, -(1 + eta)];
10+deta = 0.25 * [-(1 - xi), -(1 + xi), 1 + xi, 1 - xi];
11+end
packages/core/src/m/plate_fem_strike.madded+55−0View file
@@ -0,0 +1,55 @@
1+function result = plate_fem_strike(params, M)
2+% PLATE_FEM_STRIKE Strike the finite-element modes and render the result.
3+%
4+% result = plate_fem_strike(params, M) takes the modes of plate_fem_modes,
5+% interpolates each shape at the strike point and at the probe within the
6+% element that holds them (plate_fem_interp), sets the modal damping
7+% (plate_damping; the rigid-body modes get 0.01), integrates the coupled
8+% hammer/plate contact (plate_fem_contact) and synthesizes the sound, the
9+% series and the frames (plate_synthesize). Only this function needs to
10+% run again when the hammer, the strike point, the microphone or the
11+% render settings change.
12+
13+t_start = tic;
14+if ~strcmp(M.method, 'fem')
15+ error('plate_fem_strike: the modes were computed by another method (%s)', M.method);
16+end
17+P = plate_setup(params);
18+pl = params.plate;
19+if abs(params.strike.x) > pl.width / 2 || abs(params.strike.y) > pl.height / 2
20+ error('plate_fem_strike: the strike point is outside the plate');
21+end
22+if abs(params.probe.x) > pl.width / 2 || abs(params.probe.y) > pl.height / 2
23+ error('plate_fem_strike: the probe is outside the plate');
24+end
25+if params.fem.mesh_x ~= M.nx || params.fem.mesh_y ~= M.ny
26+ error('plate_fem_strike: the modes were computed on a %d x %d mesh, not %d x %d', M.nx, M.ny, params.fem.mesh_x, params.fem.mesh_y);
27+end
28+
29+nm = numel(M.freqs);
30+nn = numel(M.shapes) / nm;
31+Sh = reshape(M.shapes, nn, nm);
32+[idx, wts] = plate_fem_interp(params.strike.x, params.strike.y, params);
33+phi_hit = wts * Sh(idx, :);
34+[zeta, Q] = plate_damping(M.freqs, M.support_part, params, P);
35+
36+fs = params.render.fs;
37+na = round(params.render.duration * fs);
38+t = (0:na - 1) / fs;
39+C = plate_fem_contact(params, P, M.freqs, zeta, phi_hit, t);
40+
41+[ip, wp] = plate_fem_interp(params.probe.x, params.probe.y, params);
42+D.zeta = zeta;
43+D.Q = Q;
44+D.phi_hit = phi_hit;
45+D.phi_probe = wp * Sh(ip, :);
46+Kxx = reshape(M.kxx, nn, nm);
47+Kyy = reshape(M.kyy, nn, nm);
48+Kxy = reshape(M.kxy, nn, nm);
49+D.kxx_probe = wp * Kxx(ip, :);
50+D.kyy_probe = wp * Kyy(ip, :);
51+D.kxy_probe = wp * Kxy(ip, :);
52+
53+result = plate_synthesize(params, P, M, C, D);
54+result.info.strike_wall_s = toc(t_start);
55+end
packages/core/src/m/plate_gauss_legendre.madded+19−0View file
@@ -0,0 +1,19 @@
1+function [x, w] = plate_gauss_legendre(n)
2+% PLATE_GAUSS_LEGENDRE Nodes and weights of n-point Gauss-Legendre quadrature on [-1, 1].
3+%
4+% [x, w] = plate_gauss_legendre(n) returns row vectors, nodes ascending,
5+% by the Golub-Welsch method: the nodes are the eigenvalues of the
6+% symmetric tridiagonal Jacobi matrix of the Legendre recurrence and the
7+% weights are twice the squared first components of its eigenvectors.
8+
9+if n == 1
10+ x = 0; w = 2;
11+ return
12+end
13+k = 1:n - 1;
14+b = k ./ sqrt(4 * k.^2 - 1);
15+J = diag(b, 1) + diag(b, -1);
16+[V, D] = eig(J);
17+[x, order] = sort(diag(D)');
18+w = 2 * V(1, order).^2;
19+end
packages/core/src/m/plate_hertz.madded+31−0View file
@@ -0,0 +1,31 @@
1+function H = plate_hertz(params)
2+% PLATE_HERTZ Hertz contact constants of the hammer tip on the plate.
3+%
4+% H = plate_hertz(params) returns
5+% estar effective modulus, 1 / ((1 - nu_h^2) / E_h + (1 - nu^2) / E) [Pa]
6+% contact_k Hertz stiffness 4/3 E* sqrt(R): force = k delta^(3/2) [N/m^1.5]
7+% duration contact time of a sphere on a rigid half-space,
8+% 2.94 (m^2 / (R E*^2 v))^(1/5) [s]
9+% impulse momentum transferred, (1 + e) m v [N s]
10+% shape_integral mean of the sin^(3/2) pulse shape over its duration,
11+% Gamma(5/4) / (sqrt(pi) Gamma(7/4))
12+% peak_force impulse / (duration * shape_integral) [N]
13+% hunt_crossley the damping factor of the coupled model, 3 (1 - e) / (2 v) [s/m]
14+%
15+% The Rayleigh-Ritz method applies the pulse of this duration and impulse
16+% directly; the finite-element method integrates the Hunt-Crossley force
17+% k delta^(3/2) (1 + hunt_crossley delta_dot) against the moving plate and
18+% uses the duration only to size its time step.
19+
20+hm = params.hammer;
21+mat = params.material;
22+inv_estar = (1 - hm.nu^2) / hm.E + (1 - mat.nu^2) / mat.E;
23+H.estar = 1 / inv_estar;
24+H.contact_k = 4 / 3 * H.estar * sqrt(hm.tip_radius);
25+speed = max(hm.speed, 1e-9);
26+H.duration = 2.94 * (hm.mass^2 / (hm.tip_radius * H.estar^2 * speed))^0.2;
27+H.impulse = (1 + hm.restitution) * hm.mass * hm.speed;
28+H.shape_integral = gamma(1.25) / (sqrt(pi) * gamma(1.75));
29+H.peak_force = H.impulse / (H.duration * H.shape_integral);
30+H.hunt_crossley = 1.5 * (1 - hm.restitution) / speed;
31+end
packages/core/src/m/plate_modal_free.madded+33−0View file
@@ -0,0 +1,33 @@
1+function [q, v, acc] = plate_modal_free(tau, omega, zeta, q0, v0)
2+% PLATE_MODAL_FREE Free decay of one mode after the contact ends.
3+%
4+% [q, v, acc] = plate_modal_free(tau, omega, zeta, q0, v0) is the
5+% displacement, velocity and acceleration of a mass-normalized mode of
6+% angular frequency omega and damping ratio zeta at the times tau >= 0
7+% after the hammer left, starting from q0 and v0:
8+%
9+% q(tau) = e^(-alpha tau) (q0 cos(wd tau) + (v0 + alpha q0) / wd sin(wd tau)),
10+% alpha = zeta omega, wd = omega sqrt(1 - zeta^2).
11+%
12+% A rigid-body mode with no restoring force (omega = 0, the two free
13+% rotations the finite-element method keeps) simply drifts: q = q0 + v0 tau.
14+
15+tau = tau(:)';
16+if omega <= 0
17+ q = q0 + v0 * tau;
18+ v = v0 * ones(size(tau));
19+ acc = zeros(size(tau));
20+ return
21+end
22+alpha = zeta * omega;
23+wd = omega * sqrt(max(1 - zeta^2, 1e-12));
24+decay = exp(-alpha * tau);
25+a = q0;
26+b = (v0 + alpha * q0) / wd;
27+ph = wd * tau;
28+cs = cos(ph);
29+sn = sin(ph);
30+q = decay .* (a * cs + b * sn);
31+v = decay .* ((-alpha * a + wd * b) * cs + (-alpha * b - wd * a) * sn);
32+acc = -2 * alpha * v - omega^2 * q;
33+end
packages/core/src/m/plate_plan_common.madded+38−0View file
@@ -0,0 +1,38 @@
1+function plan = plate_plan_common(params, P)
2+% PLATE_PLAN_COMMON The estimates both plans share.
3+%
4+% plan = plate_plan_common(params, P) fills the fields that do not depend
5+% on the method: the plate's mass and the thread's pendulum frequency
6+% sqrt(k / m) / (2 pi), the number of modes to expect below f_max from the
7+% modal density of a thin plate, n(f) = (A / 2) sqrt(rho h / D) (an
8+% asymptotic count; the small plate here has a few more), the Hertz
9+% contact estimates and the coincidence frequency.
10+
11+pl = params.plate;
12+mu = params.material.rho * pl.thickness;
13+area = pl.width * pl.height;
14+density = area / 2 * sqrt(mu / P.D);
15+plan.plate_mass = P.plate_mass;
16+plan.thread_k = P.thread_k;
17+plan.pendulum_hz = sqrt(P.thread_k / P.plate_mass) / (2 * pi);
18+plan.f_max = P.f_max;
19+plan.modes_expected = round(density * P.f_max);
20+plan.modes_cap = params.render.modes;
21+plan.hertz_duration = P.hertz.duration;
22+plan.hertz_impulse = P.hertz.impulse;
23+plan.hertz_peak_force = P.hertz.peak_force;
24+plan.contact_k = P.hertz.contact_k;
25+plan.critical_frequency = P.critical_frequency;
26+plan.nsamples = round(params.render.duration * params.render.fs);
27+plan.nframes = params.render.nframes;
28+plan.notes = {};
29+if P.f_max > 0 && plan.modes_expected == 0
30+ plan.notes{end + 1} = 'no bending mode is expected below 0.47 fs: raise the sample rate or thin the plate';
31+end
32+if plan.modes_expected > params.render.modes
33+ plan.notes{end + 1} = sprintf('about %d modes below 0.47 fs; only %d are kept (render.modes)', plan.modes_expected, params.render.modes);
34+end
35+if params.hammer.speed <= 0
36+ plan.notes{end + 1} = 'the hammer has no speed: nothing will be struck';
37+end
38+end
packages/core/src/m/plate_radiation.madded+23−0View file
@@ -0,0 +1,23 @@
1+function [acoustic, radiation] = plate_radiation(freqs, volume_part, params, P)
2+% PLATE_RADIATION Pressure at the microphone per unit modal acceleration.
3+%
4+% [acoustic, radiation] = plate_radiation(freqs, volume_part, params, P)
5+% gives, for each mode, the coefficient a such that the mode's contribution
6+% to the pressure at the microphone is a * q_tt(t):
7+%
8+% a = rho_air / (2 pi r) * V * sqrt(min(1, f / f_c)),
9+%
10+% where V is the mode's volume participation (the integral of the mass-
11+% normalized shape over the plate: a compact-source estimate for a
12+% microphone on the plate's normal at distance r), and the square-root
13+% factor stands for the front/back cancellation of a small unbaffled plate
14+% below its coincidence frequency f_c (plate_setup). It is a smooth
15+% estimate of the radiation efficiency, not a solution of the diffraction
16+% problem, so the output is meaningful in timbre and decay while its
17+% absolute level is only approximate.
18+
19+freqs = freqs(:)';
20+volume_part = volume_part(:)';
21+radiation = sqrt(min(1, max(freqs / P.critical_frequency, 1e-4)));
22+acoustic = params.mic.air_density / (2 * pi * params.mic.distance) * volume_part .* radiation;
23+end
packages/core/src/m/plate_result_schema.madded+57−0View file
@@ -0,0 +1,57 @@
1+% PLATE_RESULT_SCHEMA Fields of the structs the two methods return.
2+%
3+% A run has two stages. The modes stage (plate_rr_modes or
4+% plate_fem_modes) depends on the plate, its material, the hole, the
5+% thread, the sample rate (through the 0.47 fs cutoff), the mode cap and
6+% the method's numerics; the host keeps its struct and reuses it while
7+% only the hammer, the strike point, the probe, the microphone or the
8+% render settings change. The strike stage (plate_rr_strike or
9+% plate_fem_strike) returns the result.
10+%
11+% Coordinates are metres from the plate's centre, x to the right and y
12+% towards the threaded top edge.
13+%
14+% modes.method 'rr' or 'fem'
15+% modes.freqs 1 x nm, ascending; the finite-element method's three
16+% rigid-body modes (below 20 Hz) come first
17+% modes.shapes ny x nx x nm mass-normalized transverse shapes on the
18+% grid (grid_x 1 x nx, grid_y 1 x ny; rows index y)
19+% modes.kxx, kyy, kxy curvatures w_xx, w_yy, w_xy of each shape on the grid
20+% modes.support_part 1 x nm, the shape at the hole
21+% modes.volume_part 1 x nm, the integral of the shape over the plate
22+% modes.vecs (rr only) the Ritz coefficients, n x nm
23+% modes.ndof, size_note, wall_s, notes, and the mesh or basis sizes
24+%
25+% result.method 'rr' or 'fem'
26+% result.grid x (1 x nx), y (1 x ny): the display grid [m]
27+% result.t time axis of the series [s], one sample per 1/fs
28+% result.probe x, y; w [m] the displacement (all modes); sxx, syy, txy,
29+% vm, sp [Pa] the surface stresses there
30+% result.frames t (1 x nf) over the first render.frame_span seconds;
31+% w the flexural field (audible modes), w_total with the
32+% rigid-body modes, vm and sp the surface stresses,
33+% each ny x nx x nf
34+% result.envelope vm, |sp|: peak over the frames, ny x nx [Pa]
35+% result.spectrum f [Hz]; p the amplitude spectrum of the pressure, w of
36+% the probe displacement (Hann window)
37+% result.audio fs; p the pressure at the microphone [Pa], mean removed;
38+% peak_pa; gain, the factor that puts the peak at
39+% render.normalize_dbfs; model
40+% result.modes n; freq, q, zeta, hit (the shape at the strike point),
41+% support, radiation (volume participation),
42+% radiation_factor, acoustic (Pa per m/s^2 of modal
43+% acceleration), audible (1 or 0), q0 and v0 (the modal
44+% state at separation), shapes (as in modes)
45+% result.contact model; tc the contact time [s]; impulse [N s];
46+% peak_force [N]; t (1 x 600), force, hammer_x, hammer_v,
47+% plate_w, plate_v the histories (the hammer's are empty
48+% for the prescribed pulse); hammer_exit_v, plate_exit_v,
49+% relative_separation_v, effective_restitution; the
50+% Hertz estimates hertz_duration, hertz_impulse,
51+% hertz_peak_force, and contact_k
52+% result.info method, ndof, nmodes, n_audible, n_rigid, fs, nsamples,
53+% nframes, frame_span, plate_mass, thread_k,
54+% critical_frequency, f_max, size_note, modes_wall_s,
55+% wall_s, strike_wall_s, notes (cell array of strings)
56+%
57+% This file documents the convention and has no executable content.
packages/core/src/m/plate_rr_basis.madded+37−0View file
@@ -0,0 +1,37 @@
1+function [b, bxx, byy, bxy] = plate_rr_basis(x, y, order, width, height)
2+% PLATE_RR_BASIS The tensor-Legendre basis and its curvatures at points of the plate.
3+%
4+% [b, bxx, byy, bxy] = plate_rr_basis(x, y, order, width, height) evaluates
5+% the (order + 1)^2 products P_i(2 x / W) P_j(2 y / H), i, j = 0..order, at
6+% the paired points (x, y) (any common shape, in metres from the plate's
7+% centre), as rows of b, together with the physical second derivatives
8+% w_xx, w_yy and w_xy of each basis function. The index k of the function
9+% (i, j) runs with j fastest: k = i (order + 1) + j + 1.
10+%
11+% Legendre products are a complete, well-conditioned basis for a free
12+% rectangle: no boundary condition is built in, which is right for free
13+% edges, and the natural conditions follow from minimizing the energy.
14+
15+xi = 2 * x(:)' / width;
16+eta = 2 * y(:)' / height;
17+[px, dx, dxx] = plate_rr_legendre(order, xi);
18+[py, dy, dyy] = plate_rr_legendre(order, eta);
19+n = (order + 1)^2;
20+pts = numel(xi);
21+b = zeros(n, pts);
22+bxx = zeros(n, pts);
23+byy = zeros(n, pts);
24+bxy = zeros(n, pts);
25+sx = 2 / width;
26+sy = 2 / height;
27+k = 0;
28+for i = 1:order + 1
29+ for j = 1:order + 1
30+ k = k + 1;
31+ b(k, :) = px(i, :) .* py(j, :);
32+ bxx(k, :) = dxx(i, :) .* py(j, :) * sx^2;
33+ byy(k, :) = px(i, :) .* dyy(j, :) * sy^2;
34+ bxy(k, :) = dx(i, :) .* dy(j, :) * sx * sy;
35+ end
36+end
37+end
packages/core/src/m/plate_rr_contact.madded+108−0View file
@@ -0,0 +1,108 @@
1+function C = plate_rr_contact(params, P, freqs, zeta, phi_hit, t)
2+% PLATE_RR_CONTACT The prescribed Hertz pulse and the modal states it leaves.
3+%
4+% C = plate_rr_contact(params, P, freqs, zeta, phi_hit, t) applies the
5+% force history
6+%
7+% F(s) = I sin(pi s / T)^(3/2) / (T Gamma(5/4) / (sqrt(pi) Gamma(7/4))), 0 <= s <= T,
8+%
9+% of duration T and impulse I from plate_hertz (the rounded pulse of a
10+% Hertz contact on a rigid target) to every mode, whose response is the
11+% Duhamel integral with the damped impulse response
12+%
13+% h(u) = e^(-alpha u) sin(wd u) / wd, alpha = zeta omega, wd = omega sqrt(1 - zeta^2).
14+%
15+% The state at separation (C.q0, C.v0) is a 64-point Gauss-Legendre
16+% quadrature of the integral over [0, T]; the states at the audio samples
17+% inside the contact (C.qs, C.vs at C.ts) and on a 600-point plotting grid
18+% (C.qg, C.vg at C.tg) are 32-point quadratures over [0, s]. The plate
19+% does not move the hammer back in this model: the force is what it would
20+% be on a rigid target, which is why the peak force and the impulse differ
21+% from the coupled finite-element contact (see plate_fem_contact).
22+
23+H = P.hertz;
24+T = H.duration;
25+I = H.impulse;
26+nrm = T * H.shape_integral;
27+freqs = freqs(:)';
28+omega = 2 * pi * freqs;
29+zeta = zeta(:)';
30+alpha = zeta .* omega;
31+wd = omega .* sqrt(max(1 - zeta.^2, 1e-12));
32+phi = phi_hit(:)';
33+nm = numel(freqs);
34+
35+% terminal state
36+[z, wz] = plate_gauss_legendre(64);
37+s = 0.5 * T * (z + 1);
38+ws = 0.5 * T * wz;
39+force = I * sin(pi * s / T).^1.5 / nrm;
40+u = T - s;
41+E = exp(-alpha' * u);
42+SW = sin(wd' * u);
43+CW = cos(wd' * u);
44+h = E .* SW ./ wd';
45+hd = E .* (CW - (alpha' ./ wd') .* SW);
46+q0 = phi .* (h * (ws .* force)')';
47+v0 = phi .* (hd * (ws .* force)')';
48+
49+% states inside the contact: at the audio samples and on the plotting grid
50+ts = t(t < T);
51+[qs, vs, fs_] = rr_states(ts, T, I, nrm, alpha, wd, phi);
52+tg = linspace(0, T, 600);
53+[qg, vg, fg] = rr_states(tg, T, I, nrm, alpha, wd, phi);
54+
55+C.model = 'prescribed Hertz pulse (rigid-target contact)';
56+C.tc = T;
57+C.q0 = q0;
58+C.v0 = v0;
59+C.ts = ts;
60+C.qs = qs;
61+C.vs = vs;
62+C.fs = fs_;
63+C.tg = tg;
64+C.qg = qg;
65+C.vg = vg;
66+C.fg = fg;
67+C.plate_v = phi * vg;
68+C.plate_w = phi * qg;
69+C.hammer_v = zeros(1, 0);
70+C.hammer_x = zeros(1, 0);
71+C.impulse = I;
72+C.peak_force = H.peak_force;
73+C.hammer_exit_v = NaN;
74+C.plate_exit_v = phi * v0';
75+C.relative_separation_v = NaN;
76+C.effective_restitution = params.hammer.restitution;
77+C.notes = {sprintf('prescribed Hertz pulse: %.4g us long, %.4g N peak, %.4g mN s', T * 1e6, H.peak_force, I * 1e3)};
78+end
79+
80+function [q, v, f] = rr_states(times, T, I, nrm, alpha, wd, phi)
81+% modal states at the given times within the pulse, by quadrature over [0, time]
82+nm = numel(alpha);
83+nt = numel(times);
84+q = zeros(nm, nt);
85+v = zeros(nm, nt);
86+f = zeros(1, nt);
87+[z, wz] = plate_gauss_legendre(32);
88+for j = 1:nt
89+ tj = times(j);
90+ if tj <= 0
91+ continue
92+ end
93+ s = 0.5 * tj * (z + 1);
94+ ws = 0.5 * tj * wz;
95+ force = I * sin(pi * s / T).^1.5 / nrm;
96+ u = tj - s;
97+ E = exp(-alpha' * u);
98+ SW = sin(wd' * u);
99+ CW = cos(wd' * u);
100+ h = E .* SW ./ wd';
101+ hd = E .* (CW - (alpha' ./ wd') .* SW);
102+ q(:, j) = phi' .* (h * (ws .* force)');
103+ v(:, j) = phi' .* (hd * (ws .* force)');
104+ if tj < T
105+ f(j) = I * sin(pi * tj / T)^1.5 / nrm;
106+ end
107+end
108+end
packages/core/src/m/plate_rr_legendre.madded+26−0View file
@@ -0,0 +1,26 @@
1+function [p, d1, d2] = plate_rr_legendre(order, x)
2+% PLATE_RR_LEGENDRE Legendre polynomials and their first two derivatives.
3+%
4+% [p, d1, d2] = plate_rr_legendre(order, x) tabulates P_n(x), P_n'(x) and
5+% P_n''(x) for n = 0..order at the points of the row vector x in [-1, 1],
6+% as (order + 1) x numel(x) arrays, from the three-term recurrence
7+% (n + 1) P_{n+1} = (2 n + 1) x P_n - n P_{n-1}
8+% and its derivatives
9+% P'_{n+1} = P'_{n-1} + (2 n + 1) P_n, P''_{n+1} = P''_{n-1} + (2 n + 1) P'_n.
10+
11+x = x(:)';
12+m = numel(x);
13+p = zeros(order + 1, m);
14+d1 = zeros(order + 1, m);
15+d2 = zeros(order + 1, m);
16+p(1, :) = 1;
17+if order >= 1
18+ p(2, :) = x;
19+ d1(2, :) = 1;
20+end
21+for n = 1:order - 1
22+ p(n + 2, :) = ((2 * n + 1) * x .* p(n + 1, :) - n * p(n, :)) / (n + 1);
23+ d1(n + 2, :) = d1(n, :) + (2 * n + 1) * p(n + 1, :);
24+ d2(n + 2, :) = d2(n, :) + (2 * n + 1) * d1(n + 1, :);
25+end
26+end
packages/core/src/m/plate_rr_modes.madded+124−0View file
@@ -0,0 +1,124 @@
1+function M = plate_rr_modes(params)
2+% PLATE_RR_MODES Modes of the hung plate by the Rayleigh-Ritz method.
3+%
4+% M = plate_rr_modes(params) minimizes the Kirchhoff-Love bending energy
5+%
6+% U = D/2 integral ( w_xx^2 + w_yy^2 + 2 nu w_xx w_yy + 2 (1 - nu) w_xy^2 ) dA
7+%
8+% over the tensor-Legendre basis of order params.rr.basis_order
9+% (plate_rr_basis), with the kinetic energy rho h / 2 integral w_t^2 dA, both
10+% integrated by Gauss-Legendre quadrature of params.rr.quadrature_order
11+% points per side. The hole subtracts its area's mass at its position
12+% (its effect on the stiffness is negligible at 0.8 mm) and the thread
13+% adds the pendulum spring m g / L there. The generalized eigenproblem
14+% K a = omega^2 Mm a is solved densely after a Cholesky reduction; the
15+% eigenvectors are mass-normalized.
16+%
17+% Kept: modes above 20 Hz and below 0.47 fs, at most params.render.modes
18+% of them. The pendulum mode of the thread and the two rigid rotations
19+% are dropped here (this method has no rigid-body content; the
20+% finite-element method keeps them during the collision).
21+%
22+% Returns M with method 'rr', freqs (1 x nm), vecs (n x nm) the Ritz
23+% coefficients, order, support_part (the shape at the hole) and
24+% volume_part (the integral of the shape over the plate) per mode, and the
25+% shapes and curvatures on a display grid of params.rr.grid points per
26+% side: shapes, kxx, kyy, kxy (ny x nx x nm; rows index y), grid_x, grid_y.
27+
28+t_start = tic;
29+P = plate_setup(params);
30+pl = params.plate;
31+W = pl.width;
32+H = pl.height;
33+order = params.rr.basis_order;
34+nq = params.rr.quadrature_order;
35+mu = params.material.rho * pl.thickness;
36+nu = params.material.nu;
37+notes = {};
38+
39+% quadrature over the plate
40+[qx, wx] = plate_gauss_legendre(nq);
41+[XX, YY] = ndgrid(qx, qx);
42+WW = wx(:) * wx(:)';
43+ww = WW(:)' * W * H / 4;
44+[b, bxx, byy, bxy] = plate_rr_basis(XX(:) * W / 2, YY(:) * H / 2, order, W, H);
45+
46+Mm = mu * ((b .* ww) * b');
47+K = P.D * ((bxx .* ww) * bxx' + (byy .* ww) * byy' ...
48+ + nu * ((bxx .* ww) * byy' + (byy .* ww) * bxx') ...
49+ + 2 * (1 - nu) * ((bxy .* ww) * bxy'));
50+
51+% the hole's mass and the thread's spring at the hole
52+ph = plate_rr_basis(P.hole_x, P.hole_y, order, W, H);
53+Mm = Mm - mu * P.hole_area * (ph * ph');
54+K = K + P.thread_k * (ph * ph');
55+
56+% generalized symmetric eigenproblem by Cholesky reduction
57+Mm = (Mm + Mm') / 2;
58+K = (K + K') / 2;
59+R = chol(Mm);
60+Cm = R' \ K / R;
61+Cm = (Cm + Cm') / 2;
62+[V, Dm] = eig(Cm);
63+[vals, order_] = sort(diag(Dm)');
64+V = V(:, order_);
65+vecs = R \ V;
66+
67+keep = vals > 1e-5;
68+vals = vals(keep);
69+vecs = vecs(:, keep);
70+freqs = sqrt(max(vals, 0)) / (2 * pi);
71+audible = (freqs > 20) & (freqs < P.f_max);
72+n_audible = nnz(audible);
73+count = min(params.render.modes, n_audible);
74+sel = find(audible);
75+sel = sel(1:count);
76+freqs = freqs(sel);
77+vecs = vecs(:, sel);
78+nm = numel(freqs);
79+if count < n_audible
80+ notes{end + 1} = sprintf('%d modes lie below 0.47 fs; the first %d are kept (params.render.modes)', n_audible, count);
81+end
82+
83+% participation of the hole and of the plate's volume (surface quadrature of order max(36, nq))
84+support_part = (vecs' * ph)';
85+ns = max(36, nq);
86+[qs, ws] = plate_gauss_legendre(ns);
87+[XS, YS] = ndgrid(qs, qs);
88+WS = ws(:) * ws(:)';
89+area_w = WS(:) * W * H / 4;
90+bs = plate_rr_basis(XS(:) * W / 2, YS(:) * H / 2, order, W, H);
91+volume_part = ((vecs' * bs) * area_w)';
92+
93+% shapes and curvatures on the display grid
94+ng = params.rr.grid;
95+grid_x = linspace(-W / 2, W / 2, ng);
96+grid_y = linspace(-H / 2, H / 2, ng);
97+[YG, XG] = ndgrid(grid_y, grid_x);
98+[bg, bgxx, bgyy, bgxy] = plate_rr_basis(XG(:), YG(:), order, W, H);
99+shapes = reshape(bg' * vecs, ng, ng, nm);
100+kxx = reshape(bgxx' * vecs, ng, ng, nm);
101+kyy = reshape(bgyy' * vecs, ng, ng, nm);
102+kxy = reshape(bgxy' * vecs, ng, ng, nm);
103+
104+n = (order + 1)^2;
105+M.method = 'rr';
106+M.freqs = freqs;
107+M.vecs = vecs;
108+M.order = order;
109+M.support_part = support_part;
110+M.volume_part = volume_part;
111+M.shapes = shapes;
112+M.kxx = kxx;
113+M.kyy = kyy;
114+M.kxy = kxy;
115+M.grid_x = grid_x;
116+M.grid_y = grid_y;
117+M.ndof = n;
118+M.n_rigid = 0;
119+M.n_found = numel(vals);
120+M.plate_mass = P.plate_mass;
121+M.size_note = sprintf('%d Legendre coefficients (order %d), %d x %d quadrature, dense eigensolve', n, order, nq, nq);
122+M.wall_s = toc(t_start);
123+M.notes = notes;
124+end
packages/core/src/m/plate_rr_plan.madded+21−0View file
@@ -0,0 +1,21 @@
1+function plan = plate_rr_plan(params)
2+% PLATE_RR_PLAN Sizes and estimates of a Rayleigh-Ritz run, before it runs.
3+%
4+% plan = plate_rr_plan(params) reports the problem size (Legendre
5+% coefficients and quadrature points), the number of modes to expect below
6+% 0.47 fs from the modal density of a thin plate, n(f) = (A/2) sqrt(rho h / D),
7+% the pendulum frequency of the thread, the Hertz estimates of the contact
8+% and the plate's coincidence frequency. Cheap: no matrix is assembled.
9+
10+P = plate_setup(params);
11+plan = plate_plan_common(params, P);
12+n = (params.rr.basis_order + 1)^2;
13+plan.method = 'rr';
14+plan.ndof = n;
15+plan.nquad = params.rr.quadrature_order^2;
16+plan.work = n^3;
17+plan.size_note = sprintf('%d Legendre coefficients (order %d), %d quadrature points', n, params.rr.basis_order, plan.nquad);
18+if params.rr.basis_order < 4
19+ plan.notes{end + 1} = 'a basis order below 4 resolves almost no bending mode';
20+end
21+end
packages/core/src/m/plate_rr_strike.madded+45−0View file
@@ -0,0 +1,45 @@
1+function result = plate_rr_strike(params, M)
2+% PLATE_RR_STRIKE Strike the Rayleigh-Ritz modes and render the result.
3+%
4+% result = plate_rr_strike(params, M) takes the modes of plate_rr_modes,
5+% evaluates the basis at the strike point and the probe (the hit
6+% participation of each mode is its mass-normalized shape there), sets the
7+% modal damping (plate_damping), applies the prescribed Hertz pulse
8+% (plate_rr_contact) and synthesizes the sound, the series and the frames
9+% (plate_synthesize). Only this function needs to run again when the
10+% hammer, the strike point, the microphone or the render settings change.
11+
12+t_start = tic;
13+if ~strcmp(M.method, 'rr')
14+ error('plate_rr_strike: the modes were computed by another method (%s)', M.method);
15+end
16+P = plate_setup(params);
17+pl = params.plate;
18+if abs(params.strike.x) > pl.width / 2 || abs(params.strike.y) > pl.height / 2
19+ error('plate_rr_strike: the strike point is outside the plate');
20+end
21+if abs(params.probe.x) > pl.width / 2 || abs(params.probe.y) > pl.height / 2
22+ error('plate_rr_strike: the probe is outside the plate');
23+end
24+
25+hit = plate_rr_basis(params.strike.x, params.strike.y, M.order, pl.width, pl.height);
26+phi_hit = (M.vecs' * hit)';
27+[zeta, Q] = plate_damping(M.freqs, M.support_part, params, P);
28+
29+fs = params.render.fs;
30+na = round(params.render.duration * fs);
31+t = (0:na - 1) / fs;
32+C = plate_rr_contact(params, P, M.freqs, zeta, phi_hit, t);
33+
34+[bp, bxx, byy, bxy] = plate_rr_basis(params.probe.x, params.probe.y, M.order, pl.width, pl.height);
35+D.zeta = zeta;
36+D.Q = Q;
37+D.phi_hit = phi_hit;
38+D.phi_probe = (M.vecs' * bp)';
39+D.kxx_probe = (M.vecs' * bxx)';
40+D.kyy_probe = (M.vecs' * byy)';
41+D.kxy_probe = (M.vecs' * bxy)';
42+
43+result = plate_synthesize(params, P, M, C, D);
44+result.info.strike_wall_s = toc(t_start);
45+end
packages/core/src/m/plate_setup.madded+38−0View file
@@ -0,0 +1,38 @@
1+function P = plate_setup(params)
2+% PLATE_SETUP Constants derived from the parameters, shared by both methods.
3+%
4+% P = plate_setup(params) copies params and adds
5+% D bending rigidity E h^3 / (12 (1 - nu^2)) [N m]
6+% hole_area area of the suspension hole [m^2]
7+% hole_x, hole_y the hole, on the centreline near the top edge [m]
8+% plate_mass rho h (W H - hole area) [kg]
9+% g standard gravity [m/s^2]
10+% thread_k transverse spring of the thread at the hole,
11+% m g / L: the linearized pendulum, not a clamp [N/m]
12+% critical_frequency coincidence frequency c^2 / (2 pi) sqrt(rho h / D),
13+% below which a small unbaffled plate radiates
14+% inefficiently [Hz]
15+% f_max highest mode kept, 0.47 fs [Hz]
16+% Kb, Ks surface-stress factors E h / (2 (1 - nu^2)) and
17+% E h / (2 (1 + nu)) (plate_stress) [Pa m]
18+% hertz the contact constants (plate_hertz)
19+%
20+% Coordinates are measured from the plate's centre, x to the right and
21+% y towards the threaded top edge, as in the simulators this lab follows.
22+
23+P = params;
24+pl = params.plate;
25+mat = params.material;
26+P.D = mat.E * pl.thickness^3 / (12 * (1 - mat.nu^2));
27+P.hole_area = pi * (params.hole.diameter / 2)^2;
28+P.hole_x = 0;
29+P.hole_y = pl.height / 2 - params.hole.edge_offset;
30+P.plate_mass = mat.rho * pl.thickness * (pl.width * pl.height - P.hole_area);
31+P.g = 9.80665;
32+P.thread_k = P.plate_mass * P.g / params.thread.length;
33+P.critical_frequency = params.mic.sound_speed^2 / (2 * pi) * sqrt(mat.rho * pl.thickness / P.D);
34+P.f_max = 0.47 * params.render.fs;
35+P.Kb = mat.E * pl.thickness / (2 * (1 - mat.nu^2));
36+P.Ks = mat.E * pl.thickness / (2 * (1 + mat.nu));
37+P.hertz = plate_hertz(params);
38+end
packages/core/src/m/plate_spectrum.madded+19−0View file
@@ -0,0 +1,19 @@
1+function [f, A] = plate_spectrum(t, x)
2+% PLATE_SPECTRUM One-sided amplitude spectrum of a real, uniformly sampled signal.
3+%
4+% [f, A] = plate_spectrum(t, x) applies a Hann window and returns the
5+% amplitude A at frequencies f [Hz], scaled so that a steady sinusoid of
6+% amplitude a shows a peak of height about a. For a decaying signal the
7+% peak heights depend on the record length, so compare shapes and
8+% frequencies rather than absolute heights.
9+
10+x = x(:).';
11+N = numel(x);
12+dt = t(2) - t(1);
13+win = 0.5 * (1 - cos(2 * pi * (0:N - 1) / (N - 1)));
14+X = fft(x .* win);
15+nh = floor(N / 2) + 1;
16+X = X(1:nh);
17+A = abs(X) * 2 / sum(win);
18+f = (0:nh - 1) / (N * dt);
19+end
packages/core/src/m/plate_stress.madded+41−0View file
@@ -0,0 +1,41 @@
1+function S = plate_stress(P, wxx, wyy, wxy)
2+% PLATE_STRESS Surface stresses of a plate from its curvatures.
3+%
4+% S = plate_stress(P, wxx, wyy, wxy) takes the second derivatives of the
5+% transverse displacement (arrays of any common size) and returns, at the
6+% surface z = h/2,
7+% S.sxx, S.syy, S.txy in-plane stress components [Pa]
8+% S.vm von Mises equivalent stress [Pa]
9+% S.sp principal stress of largest magnitude, signed
10+% (tension positive) [Pa]
11+%
12+% The in-plane strains vary linearly through the thickness,
13+% eps_xx = -z w_xx, eps_yy = -z w_yy, gamma_xy = -2 z w_xy, and plane
14+% stress gives
15+% sigma_xx = -E z / (1 - nu^2) (w_xx + nu w_yy)
16+% sigma_yy = -E z / (1 - nu^2) (w_yy + nu w_xx)
17+% tau_xy = -E z / (1 + nu) w_xy
18+% Stresses are largest at the surfaces z = +-h/2 and vanish at the
19+% mid-plane; the two surfaces carry equal and opposite stress. P.Kb and
20+% P.Ks are the factors E h / (2 (1 - nu^2)) and E h / (2 (1 + nu)).
21+%
22+% For the Mindlin-Reissner method the curvatures are the gradients of the
23+% rotations, -d(theta_x)/dx and so on; the same formula then gives the
24+% bending stress, without the transverse-shear stress, which is small at
25+% these thicknesses.
26+
27+nu = P.material.nu;
28+S.sxx = -P.Kb * (wxx + nu * wyy);
29+S.syy = -P.Kb * (wyy + nu * wxx);
30+S.txy = -P.Ks * wxy;
31+
32+S.vm = sqrt(S.sxx.^2 - S.sxx .* S.syy + S.syy.^2 + 3 * S.txy.^2);
33+
34+c = (S.sxx + S.syy) / 2;
35+r = sqrt(((S.sxx - S.syy) / 2).^2 + S.txy.^2);
36+s1 = c + r;
37+s2 = c - r;
38+S.sp = s1;
39+pick = abs(s2) > abs(s1);
40+S.sp(pick) = s2(pick);
41+end
packages/core/src/m/plate_synthesize.madded+141−0View file
@@ -0,0 +1,141 @@
1+function result = plate_synthesize(params, P, M, C, D)
2+% PLATE_SYNTHESIZE Sound, series, frames and stresses from the modes and the contact.
3+%
4+% result = plate_synthesize(params, P, M, C, D) is the tail of a strike
5+% that both methods share. M is the modes struct (plate_rr_modes or
6+% plate_fem_modes), C the contact record (plate_rr_contact or
7+% plate_fem_contact) and D the per-mode data of this strike: D.zeta and
8+% D.Q (plate_damping), D.phi_hit, and the mode shapes and curvatures at the
9+% probe, D.phi_probe, D.kxx_probe, D.kyy_probe, D.kxy_probe.
10+%
11+% Each mode is a damped oscillator. While the hammer is in contact its
12+% state comes from the contact record, sampled at the audio rate (C.qs,
13+% C.vs, the force C.fs); afterwards it decays freely (plate_modal_free)
14+% from the state C.q0, C.v0 at separation. The pressure at the microphone
15+% is the sum over the audible modes of the acoustic coefficient
16+% (plate_radiation) times the modal acceleration; the probe series and the
17+% frames are the same sums with the mode shapes. The rigid-body modes the
18+% finite-element method keeps (below 20 Hz) are excluded from the sound
19+% and from the flexural frames, and included in w_total and at the probe.
20+%
21+% Frames are taken over the first params.render.frame_span seconds, which
22+% is where the strike transient is: a wave leaving the hammer and filling
23+% the plate. The surface stresses (plate_stress) are evaluated at the
24+% frames and at the probe from the modal curvatures. Nothing is computed
25+% outside this file and the library it calls: the host draws the arrays.
26+
27+t_start = tic;
28+fs = params.render.fs;
29+na = round(params.render.duration * fs);
30+t = (0:na - 1) / fs;
31+freqs = M.freqs(:)';
32+nm = numel(freqs);
33+audible = freqs >= 20;
34+omega = 2 * pi * freqs;
35+zeta = D.zeta(:)';
36+alpha = zeta .* omega;
37+[acoustic, radiation] = plate_radiation(freqs, M.volume_part, params, P);
38+q0 = C.q0(:)';
39+v0 = C.v0(:)';
40+tc = C.tc;
41+phi_hit = D.phi_hit(:)';
42+notes = {};
43+
44+% ---- series at the audio rate: pressure, probe displacement and curvatures
45+in = t < tc;
46+nin = nnz(in);
47+tau = max(t - tc, 0);
48+pressure = zeros(1, na);
49+w_probe = zeros(1, na);
50+kxx_p = zeros(1, na);
51+kyy_p = zeros(1, na);
52+kxy_p = zeros(1, na);
53+for k = 1:nm
54+ [q, v, acc] = plate_modal_free(tau, omega(k), zeta(k), q0(k), v0(k));
55+ if nin > 0
56+ qs = C.qs(k, 1:nin);
57+ vs = C.vs(k, 1:nin);
58+ q(1:nin) = qs;
59+ v(1:nin) = vs;
60+ acc(1:nin) = phi_hit(k) * C.fs(1:nin) - 2 * alpha(k) * vs - omega(k)^2 * qs;
61+ end
62+ if audible(k)
63+ pressure = pressure + acoustic(k) * acc;
64+ end
65+ w_probe = w_probe + D.phi_probe(k) * q;
66+ kxx_p = kxx_p + D.kxx_probe(k) * q;
67+ kyy_p = kyy_p + D.kyy_probe(k) * q;
68+ kxy_p = kxy_p + D.kxy_probe(k) * q;
69+end
70+Sp = plate_stress(P, kxx_p, kyy_p, kxy_p);
71+
72+% ---- modal coordinates at the frame times
73+nf = params.render.nframes;
74+span = params.render.frame_span;
75+tf = linspace(0, span, nf);
76+Qf = zeros(nm, nf);
77+inf_ = tf < tc;
78+if any(inf_)
79+ Qf(:, inf_) = interp1(C.tg(:), C.qg', tf(inf_)')';
80+end
81+after = find(~inf_);
82+if ~isempty(after)
83+ for k = 1:nm
84+ q = plate_modal_free(tf(after) - tc, omega(k), zeta(k), q0(k), v0(k));
85+ Qf(k, after) = q;
86+ end
87+end
88+ny = size(M.shapes, 1);
89+nx = size(M.shapes, 2);
90+nn = ny * nx;
91+Sh = reshape(M.shapes, nn, nm);
92+Wtot = reshape(Sh * Qf, ny, nx, nf);
93+Wflex = reshape(Sh(:, audible) * Qf(audible, :), ny, nx, nf);
94+KXX = reshape(reshape(M.kxx, nn, nm) * Qf, ny, nx, nf);
95+KYY = reshape(reshape(M.kyy, nn, nm) * Qf, ny, nx, nf);
96+KXY = reshape(reshape(M.kxy, nn, nm) * Qf, ny, nx, nf);
97+Sf = plate_stress(P, KXX, KYY, KXY);
98+env_vm = max(Sf.vm, [], 3);
99+env_sp = max(abs(Sf.sp), [], 3);
100+
101+% ---- spectra and the sound
102+[f, Ap] = plate_spectrum(t, pressure);
103+[~, Aw] = plate_spectrum(t, w_probe);
104+pressure = pressure - mean(pressure);
105+peak_pa = max(abs(pressure));
106+gain = 10^(params.render.normalize_dbfs / 20) / max(peak_pa, 1e-15);
107+
108+if nnz(audible) == 0
109+ notes{end + 1} = 'no audible mode below 0.47 fs: the sound is silent';
110+end
111+if span >= tc
112+ notes{end + 1} = sprintf('%d frames over the first %.3g ms; the hammer leaves at %.3g us, in frame %d', nf, span * 1e3, tc * 1e6, nnz(inf_) + 1);
113+else
114+ notes{end + 1} = sprintf('%d frames over the first %.3g ms, all within the contact (%.3g us)', nf, span * 1e3, tc * 1e6);
115+end
116+
117+result.method = M.method;
118+result.grid = struct('x', M.grid_x(:)', 'y', M.grid_y(:)');
119+result.t = t;
120+result.probe = struct('x', params.probe.x, 'y', params.probe.y, 'w', w_probe, ...
121+ 'sxx', Sp.sxx, 'syy', Sp.syy, 'txy', Sp.txy, 'vm', Sp.vm, 'sp', Sp.sp);
122+result.frames = struct('t', tf, 'w', Wflex, 'w_total', Wtot, 'vm', Sf.vm, 'sp', Sf.sp);
123+result.envelope = struct('vm', env_vm, 'sp', env_sp);
124+result.spectrum = struct('f', f, 'p', Ap, 'w', Aw);
125+result.audio = struct('fs', fs, 'p', pressure, 'peak_pa', peak_pa, 'gain', gain, ...
126+ 'normalize_dbfs', params.render.normalize_dbfs, 'model', 'compact source with front/back cancellation');
127+result.modes = struct('n', nm, 'freq', freqs, 'q', D.Q(:)', 'zeta', zeta, 'hit', phi_hit, ...
128+ 'support', M.support_part(:)', 'radiation', M.volume_part(:)', 'radiation_factor', radiation, ...
129+ 'acoustic', acoustic, 'audible', double(audible), 'q0', q0, 'v0', v0, 'shapes', M.shapes);
130+result.contact = struct('model', C.model, 'tc', tc, 'impulse', C.impulse, 'peak_force', C.peak_force, ...
131+ 't', C.tg, 'force', C.fg, 'hammer_v', C.hammer_v, 'hammer_x', C.hammer_x, 'plate_v', C.plate_v, ...
132+ 'plate_w', C.plate_w, 'hammer_exit_v', C.hammer_exit_v, 'plate_exit_v', C.plate_exit_v, ...
133+ 'relative_separation_v', C.relative_separation_v, 'effective_restitution', C.effective_restitution, ...
134+ 'hertz_duration', P.hertz.duration, 'hertz_impulse', P.hertz.impulse, 'hertz_peak_force', P.hertz.peak_force, ...
135+ 'contact_k', P.hertz.contact_k);
136+result.info = struct('method', M.method, 'ndof', M.ndof, 'nmodes', nm, 'n_audible', nnz(audible), ...
137+ 'n_rigid', nnz(~audible), 'fs', fs, 'nsamples', na, 'nframes', nf, 'frame_span', span, ...
138+ 'plate_mass', P.plate_mass, 'thread_k', P.thread_k, 'critical_frequency', P.critical_frequency, ...
139+ 'f_max', P.f_max, 'size_note', M.size_note, 'modes_wall_s', M.wall_s, 'wall_s', toc(t_start), ...
140+ 'notes', {[M.notes, C.notes, notes]});
141+end
packages/core/src/methods/common.tsadded+166−0View file
@@ -0,0 +1,166 @@
1+import type { EquationGroup } from "./types";
2+
3+/**
4+ * The equations behind the lab, grouped as the Equations tab shows them.
5+ * Each group names the methods it applies to; the rest are shared.
6+ */
7+
8+export const OBJECT_GROUP: EquationGroup = {
9+ title: "The object",
10+ equations: [
11+ {
12+ tex: String.raw`D = \frac{E h^3}{12(1-\nu^2)}, \qquad m = \rho h (W H - \pi r_h^2)`,
13+ caption:
14+ "A rectangular plate of sides W and H and thickness h with a small hole of radius r_h near the top edge; D is its bending rigidity and m its mass.",
15+ },
16+ {
17+ tex: String.raw`k_t = \frac{m g}{L}`,
18+ caption:
19+ "The thread of length L holds the hole with the linearized pendulum stiffness against transverse motion. It is a soft point spring, not a clamp; nothing restrains the plate's rotations about the hole.",
20+ },
21+ ],
22+};
23+
24+export const RR_GROUP: EquationGroup = {
25+ title: "Rayleigh-Ritz on the thin-plate energy",
26+ methods: ["rr"],
27+ equations: [
28+ {
29+ tex: String.raw`U = \frac{D}{2}\int_A \left( w_{xx}^2 + w_{yy}^2 + 2\nu\, w_{xx} w_{yy} + 2(1-\nu)\, w_{xy}^2 \right) dA, \qquad T = \frac{\rho h}{2}\int_A w_t^2\, dA`,
30+ caption:
31+ "Kirchhoff-Love bending energy and kinetic energy of the plate: no shear deformation, no rotary inertia. Free edges need no condition, since minimizing U enforces the natural ones.",
32+ },
33+ {
34+ tex: String.raw`w(x,y) = \sum_{i,j=0}^{N} a_{ij}\, P_i\!\left(\tfrac{2x}{W}\right) P_j\!\left(\tfrac{2y}{H}\right)`,
35+ caption:
36+ "The trial field is a tensor product of Legendre polynomials up to order N = 12 per side, 169 coefficients; the integrals are Gauss-Legendre quadratures with 30 points per side.",
37+ },
38+ {
39+ tex: String.raw`\left(\mathbf{K} + k_t\, \mathbf{p}_h \mathbf{p}_h^{\mathsf T}\right)\mathbf{a} = \omega^2 \left(\mathbf{M} - \rho h\, \pi r_h^2\, \mathbf{p}_h \mathbf{p}_h^{\mathsf T}\right)\mathbf{a}`,
40+ caption:
41+ "The generalized eigenproblem, with the thread's spring added and the hole's mass subtracted at the basis vector p_h evaluated at the hole; solved densely after a Cholesky reduction. The rigid rotations and the pendulum mode are dropped.",
42+ },
43+ ],
44+};
45+
46+export const FEM_GROUP: EquationGroup = {
47+ title: "Mindlin-Reissner finite elements",
48+ methods: ["fem"],
49+ equations: [
50+ {
51+ tex: String.raw`U = \frac{1}{2}\int_A \left( \boldsymbol{\kappa}^{\mathsf T} \mathbf{D}_b\, \boldsymbol{\kappa} + \boldsymbol{\gamma}^{\mathsf T} \mathbf{D}_s\, \boldsymbol{\gamma} \right) dA, \quad \boldsymbol{\kappa} = \begin{pmatrix} \theta_{x,x} \\ \theta_{y,y} \\ \theta_{x,y} + \theta_{y,x} \end{pmatrix}, \quad \boldsymbol{\gamma} = \begin{pmatrix} w_x + \theta_x \\ w_y + \theta_y \end{pmatrix}`,
52+ caption:
53+ "Three fields, the deflection w and two rotations, with bending stiffness D_b = D [1, ν, 0; ν, 1, 0; 0, 0, (1-ν)/2] and shear stiffness D_s = κ G h I, κ = 5/6. The kinetic energy carries the mass ρh and the rotary inertia ρh³/12.",
54+ },
55+ {
56+ tex: String.raw`w, \theta_x, \theta_y = \sum_{a=1}^{4} N_a(\xi,\eta)\, (w, \theta_x, \theta_y)_a, \qquad N_a = \tfrac{1}{4}(1 \pm \xi)(1 \pm \eta)`,
57+ caption:
58+ "Bilinear four-node elements on a regular mesh, integrated with 2 x 2 Gauss points for both energies. Full integration of the shear term locks on a thin plate, which is why the partials converge from above as the mesh is refined.",
59+ },
60+ {
61+ tex: String.raw`\left(\mathbf{K} + k_t\, \mathbf{s}\, \mathbf{s}^{\mathsf T}\right)\mathbf{v} = \omega^2 \mathbf{M}\, \mathbf{v}, \qquad \mathbf{Y} = (\mathbf{K} - \sigma \mathbf{M})^{-1} \mathbf{M} \mathbf{X}, \quad \sigma < 0`,
62+ caption:
63+ "The sparse eigenproblem, with the thread's spring on the interpolation vector s of the hole, is solved by subspace iteration: one Cholesky factorization of the negatively shifted matrix, then repeated block solves and Rayleigh-Ritz projections until the lowest modes settle. The three rigid-body modes are kept.",
64+ },
65+ ],
66+};
67+
68+export const CONTACT_GROUP: EquationGroup = {
69+ title: "The hammer",
70+ equations: [
71+ {
72+ tex: String.raw`\frac{1}{E^*} = \frac{1-\nu_h^2}{E_h} + \frac{1-\nu^2}{E}, \qquad k_c = \tfrac{4}{3} E^* \sqrt{R}, \qquad F = k_c\, \delta^{3/2}`,
73+ caption:
74+ "Hertz contact of a sphere of radius R on a flat: the force grows with the three-halves power of the indentation δ.",
75+ },
76+ {
77+ tex: String.raw`T = 2.94 \left( \frac{m_h^2}{R\, E^{*2}\, v_0} \right)^{1/5}, \qquad I = (1 + e)\, m_h v_0`,
78+ caption:
79+ "On a rigid target the contact lasts T and transfers the impulse I, e being the nominal coefficient of restitution.",
80+ },
81+ ],
82+};
83+
84+export const RR_CONTACT_GROUP: EquationGroup = {
85+ title: "The prescribed pulse",
86+ methods: ["rr"],
87+ equations: [
88+ {
89+ tex: String.raw`F(s) = \frac{I \sin^{3/2}(\pi s / T)}{T\, \Gamma(5/4) / (\sqrt{\pi}\, \Gamma(7/4))}, \quad 0 \le s \le T`,
90+ caption:
91+ "The Rayleigh-Ritz method applies the rigid-target pulse as a given force history: the plate does not push back on the hammer, so the peak force is that of a rigid target.",
92+ },
93+ {
94+ tex: String.raw`q_k(t) = \phi_k(\mathbf{x}_s) \int_0^{\min(t,T)} F(s)\, h_k(t - s)\, ds, \qquad h_k(u) = \frac{e^{-\zeta_k \omega_k u} \sin(\omega_{d,k} u)}{\omega_{d,k}}`,
95+ caption:
96+ "Each mode's response is Duhamel's integral of the pulse against its damped impulse response, with φ_k the mass-normalized shape at the strike point; evaluated by Gauss-Legendre quadrature.",
97+ },
98+ ],
99+};
100+
101+export const FEM_CONTACT_GROUP: EquationGroup = {
102+ title: "The coupled contact",
103+ methods: ["fem"],
104+ equations: [
105+ {
106+ tex: String.raw`\delta = x_h - \sum_k \phi_k(\mathbf{x}_s)\, q_k, \qquad F = k_c\, \delta^{3/2} \left( 1 + \frac{3(1-e)}{2 v_0}\, \dot\delta \right) \;\; (\delta > 0)`,
107+ caption:
108+ "The Hunt-Crossley force: a Hertz spring with a loss proportional to the closing speed, chosen so that a collision with a rigid target returns the nominal restitution e.",
109+ },
110+ {
111+ tex: String.raw`\ddot q_k = \phi_k(\mathbf{x}_s)\, F - 2\zeta_k \omega_k \dot q_k - \omega_k^2 q_k, \qquad m_h \ddot x_h = -F`,
112+ caption:
113+ "Hammer and plate as one system, integrated by fourth-order Runge-Kutta until δ falls through zero. All modes take part, the rigid-body ones included, so the light plate can move away from the hammer: the force is lower and the contact longer than the prescribed pulse.",
114+ },
115+ ],
116+};
117+
118+export const DAMPING_GROUP: EquationGroup = {
119+ title: "Losses",
120+ equations: [
121+ {
122+ tex: String.raw`\frac{1}{Q_k} = \frac{1}{Q_{\mathrm{mat}}} + \frac{\sqrt{f_k / 1\,\mathrm{kHz}}}{1100} + \frac{c_t\, \phi_k(\mathbf{x}_h)^2}{\omega_k}, \qquad \zeta_k = \min\!\left(0.08,\ \frac{1}{2 Q_k}\right)`,
123+ caption:
124+ "Three losses per mode: the material's structural loss, air loading that grows with frequency, and the thread's dashpot c_t acting on the mode's displacement at the hole. Order-of-magnitude figures, not measurements.",
125+ },
126+ {
127+ tex: String.raw`q_k(t) = e^{-\zeta_k \omega_k \tau} \left( q_k^0 \cos \omega_{d,k} \tau + \frac{\dot q_k^0 + \zeta_k \omega_k q_k^0}{\omega_{d,k}} \sin \omega_{d,k} \tau \right), \quad \tau = t - t_c`,
128+ caption:
129+ "After the hammer leaves at t_c, every mode decays freely from its state at separation; ω_d is the damped frequency. The sound, the series and the frames are sums of these.",
130+ },
131+ ],
132+};
133+
134+export const RADIATION_GROUP: EquationGroup = {
135+ title: "The sound",
136+ equations: [
137+ {
138+ tex: String.raw`p(t) = \sum_k \frac{\rho_{\mathrm{air}}}{2\pi r}\, V_k\, \sqrt{\min\!\left(1, \frac{f_k}{f_c}\right)}\; \ddot q_k(t), \qquad V_k = \int_A \phi_k\, dA, \qquad f_c = \frac{c^2}{2\pi}\sqrt{\frac{\rho h}{D}}`,
139+ caption:
140+ "A compact-source estimate for a microphone at distance r on the plate's normal: each mode radiates in proportion to its volume velocity, reduced below the coincidence frequency f_c by the front/back cancellation of a small unbaffled plate. Meaningful in timbre and decay; the absolute level is approximate, so the export is peak-normalized.",
141+ },
142+ ],
143+};
144+
145+export const STRESS_GROUP: EquationGroup = {
146+ title: "Surface stress",
147+ equations: [
148+ {
149+ tex: String.raw`\sigma_{xx} = -\frac{E h}{2(1-\nu^2)}\,(w_{xx} + \nu w_{yy}), \quad \sigma_{yy} = -\frac{E h}{2(1-\nu^2)}\,(w_{yy} + \nu w_{xx}), \quad \tau_{xy} = -\frac{E h}{2(1+\nu)}\, w_{xy}`,
150+ caption:
151+ "Bending stress at the surface z = h/2 from the modal curvatures (for the Mindlin-Reissner elements, from the gradients of the rotations); von Mises and the signed principal stress follow. The opposite face carries the opposite sign.",
152+ },
153+ ],
154+};
155+
156+export const ALL_GROUPS: EquationGroup[] = [
157+ OBJECT_GROUP,
158+ RR_GROUP,
159+ FEM_GROUP,
160+ CONTACT_GROUP,
161+ RR_CONTACT_GROUP,
162+ FEM_CONTACT_GROUP,
163+ DAMPING_GROUP,
164+ RADIATION_GROUP,
165+ STRESS_GROUP,
166+];
packages/core/src/methods/fem.tsadded+51−0View file
@@ -0,0 +1,51 @@
1+import type { MethodDefinition } from "./types";
2+import { ALL_GROUPS } from "./common";
3+
4+export const FEM_METHOD: MethodDefinition = {
5+ id: "fem",
6+ name: "Finite elements, Mindlin-Reissner",
7+ shortName: "Finite elements",
8+ summary:
9+ "Q4 Mindlin-Reissner elements with transverse shear, a sparse eigensolve by subspace iteration, and the hammer integrated against the moving plate.",
10+ modesFunction: "plate_fem_modes",
11+ strikeFunction: "plate_fem_strike",
12+ planFunction: "plate_fem_plan",
13+ readingOrder: [
14+ "plate_fem_modes.m",
15+ "plate_fem_strike.m",
16+ "plate_fem_contact.m",
17+ "plate_fem_rhs.m",
18+ "plate_fem_force.m",
19+ "plate_fem_assemble.m",
20+ "plate_fem_element.m",
21+ "plate_fem_shape.m",
22+ "plate_fem_interp.m",
23+ "plate_fem_eigs.m",
24+ "plate_fem_nodal.m",
25+ "plate_fem_plan.m",
26+ "plate_setup.m",
27+ "plate_hertz.m",
28+ "plate_damping.m",
29+ "plate_radiation.m",
30+ "plate_modal_free.m",
31+ "plate_synthesize.m",
32+ "plate_stress.m",
33+ "plate_spectrum.m",
34+ "plate_gauss_legendre.m",
35+ "plate_plan_common.m",
36+ "plate_result_schema.m",
37+ ],
38+ equationGroups: ALL_GROUPS.filter((g) => !g.methods || g.methods.includes("fem")),
39+ validity: [
40+ "Mindlin-Reissner plate theory with bilinear elements and full integration of the shear term, as in the reference simulator. The element locks in shear on a thin plate: the partials converge from above as the mesh is refined, by several percent between 32 x 32 and 80 x 80, and the default 48 x 48 mesh is about 3 percent high on the main partial.",
41+ "The hammer and the plate are integrated together, so the plate moves away under the hammer; the contact is longer and the force and the impulse smaller than the rigid-target pulse. Neither contact model has been checked against a measured hammer.",
42+ "The three rigid-body modes (two free rotations and the pendulum) are kept during the collision so momentum can flow into the freely hanging plate; gravity does not otherwise enter, so the rotations drift after the strike at a constant rate rather than swinging. They are left out of the sound.",
43+ "The hole is not cut from the mesh; the thread is a point spring interpolated within an element. The curvatures for the stresses are element-centre gradients of the rotations averaged to the nodes, first-order accurate.",
44+ "Modal damping is a three-term model (material Q, air loading, the thread's dashpot) with the constants of the reference simulator, chosen, not measured, and capped at a damping ratio of 0.08.",
45+ "Radiated sound is a compact-source estimate with a smooth front/back cancellation below coincidence: meaningful in timbre and decay, approximate in level.",
46+ "The eigensolver is a subspace iteration in double precision; its convergence tolerance and largest residual are reported under Run details.",
47+ "The model is linear: no denting, no chatter of the hammer, no thread buzz, no room.",
48+ ],
49+ numericsOf: (spec) => ({ ...spec.fem }),
50+ cost: { msPerWork: 0.25, msPerSampleMode: 4e-4 },
51+};
packages/core/src/methods/registry.tsadded+12−0View file
@@ -0,0 +1,12 @@
1+import type { MethodId } from "../spec/types";
2+import type { MethodDefinition } from "./types";
3+import { RR_METHOD } from "./rr";
4+import { FEM_METHOD } from "./fem";
5+
6+export const METHODS: MethodDefinition[] = [RR_METHOD, FEM_METHOD];
7+
8+export function methodById(id: MethodId): MethodDefinition {
9+ const m = METHODS.find((x) => x.id === id);
10+ if (!m) throw new Error(`unknown method ${id}`);
11+ return m;
12+}
packages/core/src/methods/rr.tsadded+44−0View file
@@ -0,0 +1,44 @@
1+import type { MethodDefinition } from "./types";
2+import { ALL_GROUPS } from "./common";
3+
4+export const RR_METHOD: MethodDefinition = {
5+ id: "rr",
6+ name: "Rayleigh-Ritz, Kirchhoff-Love",
7+ shortName: "Rayleigh-Ritz",
8+ summary:
9+ "Thin-plate bending energy on a tensor-Legendre basis, a dense eigensolve, and a prescribed Hertz pulse as the strike.",
10+ modesFunction: "plate_rr_modes",
11+ strikeFunction: "plate_rr_strike",
12+ planFunction: "plate_rr_plan",
13+ readingOrder: [
14+ "plate_rr_modes.m",
15+ "plate_rr_strike.m",
16+ "plate_rr_contact.m",
17+ "plate_rr_basis.m",
18+ "plate_rr_legendre.m",
19+ "plate_rr_plan.m",
20+ "plate_setup.m",
21+ "plate_hertz.m",
22+ "plate_damping.m",
23+ "plate_radiation.m",
24+ "plate_modal_free.m",
25+ "plate_synthesize.m",
26+ "plate_stress.m",
27+ "plate_spectrum.m",
28+ "plate_gauss_legendre.m",
29+ "plate_plan_common.m",
30+ "plate_result_schema.m",
31+ ],
32+ equationGroups: ALL_GROUPS.filter((g) => !g.methods || g.methods.includes("rr")),
33+ validity: [
34+ "Kirchhoff-Love thin-plate theory: no shear deformation or rotary inertia, so the highest modes kept (tens of kHz on a 1 mm plate) are a little high, and a thick plate is wrong.",
35+ "The strike is a prescribed force pulse, that of a Hertz contact on a rigid target. The plate does not push back on the hammer, so the peak force and the impulse are those of a rigid target: much larger than the coupled contact of the finite-element method predicts for this very light plate.",
36+ "The hole enters as a point mass correction and the thread as a point spring; neither perturbs the stiffness. The rigid rotations and the pendulum mode are dropped, so nothing of the plate's whole-body motion after the strike is in this result.",
37+ "Modal damping is a three-term model (material Q, air loading, the thread's dashpot) with the constants of the reference simulator, chosen, not measured, and capped at a damping ratio of 0.08.",
38+ "Radiated sound is a compact-source estimate with a smooth front/back cancellation below coincidence: meaningful in timbre and decay, approximate in level.",
39+ "The Legendre basis is complete on the rectangle: the first ten modes were stable to better than 0.01 Hz between orders 12 and 14. Stresses are second derivatives of the field and converge more slowly than the frequencies.",
40+ "The model is linear: no denting, no chatter of the hammer, no thread buzz, no room.",
41+ ],
42+ numericsOf: (spec) => ({ ...spec.rr }),
43+ cost: { msPerWork: 2e-4, msPerSampleMode: 4e-4 },
44+};
packages/core/src/methods/types.tsadded+47−0View file
@@ -0,0 +1,47 @@
1+import type { MethodId, ProblemSpec } from "../spec/types";
2+
3+export interface Equation {
4+ /** LaTeX, display mode */
5+ tex: string;
6+ caption: string;
7+}
8+
9+/** A titled group of equations, for the equations tab. */
10+export interface EquationGroup {
11+ title: string;
12+ /** Which methods the group applies to; absent means both */
13+ methods?: MethodId[];
14+ equations: Equation[];
15+}
16+
17+/**
18+ * A solution method is data: which `.m` functions to call, the equations it
19+ * discretizes, and what it approximates.
20+ */
21+export interface MethodDefinition {
22+ id: MethodId;
23+ name: string;
24+ /** Two or three words, for tabs and other tight places */
25+ shortName: string;
26+ /** One sentence for tabs and tooltips */
27+ summary: string;
28+ /** `.m` function taking `params` and returning the modes struct */
29+ modesFunction: string;
30+ /** `.m` function taking `params` and the modes and returning `result` */
31+ strikeFunction: string;
32+ /** `.m` function taking `params` and returning the plan */
33+ planFunction: string;
34+ /** Library files in reading order for this method (main.m is prepended by the code view) */
35+ readingOrder: string[];
36+ equationGroups: EquationGroup[];
37+ /** Plain statements of what is idealized or approximate, shown next to results */
38+ validity: string[];
39+ /** The numerics settings of the spec this method reads */
40+ numericsOf: (spec: ProblemSpec) => Record<string, number>;
41+ /**
42+ * Starting guesses for the cost model, refined by measurements: milliseconds
43+ * per unit of the plan's `work` for the modes stage, and per sample-mode for
44+ * the strike stage.
45+ */
46+ cost: { msPerWork: number; msPerSampleMode: number };
47+}
packages/core/src/node.tsadded+3−0View file
@@ -0,0 +1,3 @@
1+/** Node-only entry: the in-process runner used by tests and scripts. */
2+export { NodeRunner } from "./runner/nodeRunner";
3+export { executeBundle, numblVersion } from "./runner/execute";
packages/core/src/results/parse.tsadded+253−0View file
@@ -0,0 +1,253 @@
1+import {
2+ isPlainCell,
3+ isPlainStruct,
4+ isPlainTensor,
5+ type PlainStruct,
6+ type PlainValue,
7+} from "../runner/plain";
8+import type { Field2D, FieldFrames } from "../fields/field";
9+import type { MethodId } from "../spec/types";
10+import type { ContactResult, MethodPlan, ModesResult, PlateResult, ResultInfo } from "./types";
11+
12+export class ResultShapeError extends Error {}
13+
14+function field(s: PlainStruct, name: string): PlainValue {
15+ const v = s.fields[name];
16+ if (v === undefined) throw new ResultShapeError(`result is missing field "${name}"`);
17+ return v;
18+}
19+
20+function struct(s: PlainStruct, name: string): PlainStruct {
21+ const v = field(s, name);
22+ if (!isPlainStruct(v)) throw new ResultShapeError(`field "${name}" should be a struct`);
23+ return v;
24+}
25+
26+function num(s: PlainStruct, name: string): number {
27+ const v = field(s, name);
28+ if (typeof v === "number") return v;
29+ if (typeof v === "boolean") return v ? 1 : 0;
30+ if (isPlainTensor(v) && v.data.length === 1) return v.data[0];
31+ throw new ResultShapeError(`field "${name}" should be a scalar`);
32+}
33+
34+function numOpt(s: PlainStruct, name: string, fallback: number): number {
35+ return s.fields[name] === undefined ? fallback : num(s, name);
36+}
37+
38+function str(s: PlainStruct, name: string): string {
39+ const v = field(s, name);
40+ if (typeof v === "string") return v;
41+ throw new ResultShapeError(`field "${name}" should be a string`);
42+}
43+
44+function vec(s: PlainStruct, name: string): Float64Array {
45+ const v = field(s, name);
46+ if (typeof v === "number") return Float64Array.of(v);
47+ if (isPlainTensor(v)) return v.data;
48+ throw new ResultShapeError(`field "${name}" should be a numeric vector`);
49+}
50+
51+function field2d(s: PlainStruct, name: string): Field2D {
52+ const v = field(s, name);
53+ if (!isPlainTensor(v) || v.shape.length !== 2)
54+ throw new ResultShapeError(`field "${name}" should be a 2-D array`);
55+ return { ny: v.shape[0], nx: v.shape[1], data: v.data };
56+}
57+
58+function frames3d(s: PlainStruct, name: string): FieldFrames {
59+ const v = field(s, name);
60+ if (!isPlainTensor(v)) throw new ResultShapeError(`field "${name}" should be a 3-D array`);
61+ const [ny, nx, nf] = [v.shape[0] ?? 0, v.shape[1] ?? 0, v.shape[2] ?? 1];
62+ if (v.shape.length < 2 || v.shape.length > 3)
63+ throw new ResultShapeError(`field "${name}" should be a 3-D array`);
64+ return { ny, nx, nf, data: v.data };
65+}
66+
67+function strings(s: PlainStruct, name: string): string[] {
68+ const v = s.fields[name];
69+ if (v === undefined) return [];
70+ if (typeof v === "string") return [v];
71+ if (isPlainCell(v)) return v.items.map((x) => (typeof x === "string" ? x : JSON.stringify(x)));
72+ return [];
73+}
74+
75+function methodId(s: string): MethodId {
76+ if (s === "rr" || s === "fem") return s;
77+ throw new ResultShapeError(`unknown method "${s}"`);
78+}
79+
80+const INFO_STANDARD = new Set([
81+ "method",
82+ "ndof",
83+ "nmodes",
84+ "n_audible",
85+ "n_rigid",
86+ "fs",
87+ "nsamples",
88+ "nframes",
89+ "frame_span",
90+ "plate_mass",
91+ "thread_k",
92+ "critical_frequency",
93+ "f_max",
94+ "size_note",
95+ "modes_wall_s",
96+ "wall_s",
97+ "strike_wall_s",
98+ "notes",
99+]);
100+
101+function parseInfo(s: PlainStruct): ResultInfo {
102+ const extra: Record<string, number | string | boolean> = {};
103+ for (const [k, v] of Object.entries(s.fields)) {
104+ if (INFO_STANDARD.has(k)) continue;
105+ if (typeof v === "number" || typeof v === "string" || typeof v === "boolean") extra[k] = v;
106+ else if (isPlainTensor(v) && v.data.length === 1) extra[k] = v.data[0];
107+ }
108+ return {
109+ method: methodId(str(s, "method")),
110+ ndof: num(s, "ndof"),
111+ nmodes: num(s, "nmodes"),
112+ n_audible: num(s, "n_audible"),
113+ n_rigid: num(s, "n_rigid"),
114+ fs: num(s, "fs"),
115+ nsamples: num(s, "nsamples"),
116+ nframes: num(s, "nframes"),
117+ frame_span: num(s, "frame_span"),
118+ plate_mass: num(s, "plate_mass"),
119+ thread_k: num(s, "thread_k"),
120+ critical_frequency: num(s, "critical_frequency"),
121+ f_max: num(s, "f_max"),
122+ size_note: str(s, "size_note"),
123+ modes_wall_s: num(s, "modes_wall_s"),
124+ wall_s: num(s, "wall_s"),
125+ strike_wall_s: numOpt(s, "strike_wall_s", 0),
126+ notes: strings(s, "notes"),
127+ extra,
128+ };
129+}
130+
131+function parseModes(s: PlainStruct): ModesResult {
132+ return {
133+ n: num(s, "n"),
134+ freq: vec(s, "freq"),
135+ q: vec(s, "q"),
136+ zeta: vec(s, "zeta"),
137+ hit: vec(s, "hit"),
138+ support: vec(s, "support"),
139+ radiation: vec(s, "radiation"),
140+ radiation_factor: vec(s, "radiation_factor"),
141+ acoustic: vec(s, "acoustic"),
142+ audible: Array.from(vec(s, "audible"), (x) => x !== 0),
143+ q0: vec(s, "q0"),
144+ v0: vec(s, "v0"),
145+ shapes: frames3d(s, "shapes"),
146+ };
147+}
148+
149+function parseContact(s: PlainStruct): ContactResult {
150+ return {
151+ model: str(s, "model"),
152+ tc: num(s, "tc"),
153+ impulse: num(s, "impulse"),
154+ peak_force: num(s, "peak_force"),
155+ t: vec(s, "t"),
156+ force: vec(s, "force"),
157+ hammer_x: vec(s, "hammer_x"),
158+ hammer_v: vec(s, "hammer_v"),
159+ plate_w: vec(s, "plate_w"),
160+ plate_v: vec(s, "plate_v"),
161+ hammer_exit_v: num(s, "hammer_exit_v"),
162+ plate_exit_v: num(s, "plate_exit_v"),
163+ relative_separation_v: num(s, "relative_separation_v"),
164+ effective_restitution: num(s, "effective_restitution"),
165+ hertz_duration: num(s, "hertz_duration"),
166+ hertz_impulse: num(s, "hertz_impulse"),
167+ hertz_peak_force: num(s, "hertz_peak_force"),
168+ contact_k: num(s, "contact_k"),
169+ };
170+}
171+
172+/** Validate and type the strike's result struct. Throws ResultShapeError with a specific message. */
173+export function parsePlateResult(v: PlainValue): PlateResult {
174+ if (!isPlainStruct(v)) throw new ResultShapeError("the result variable is not a struct");
175+ const grid = struct(v, "grid");
176+ const probe = struct(v, "probe");
177+ const frames = struct(v, "frames");
178+ const env = struct(v, "envelope");
179+ const spec = struct(v, "spectrum");
180+ const audio = struct(v, "audio");
181+ return {
182+ method: methodId(str(v, "method")),
183+ grid: { x: vec(grid, "x"), y: vec(grid, "y") },
184+ t: vec(v, "t"),
185+ probe: {
186+ x: num(probe, "x"),
187+ y: num(probe, "y"),
188+ w: vec(probe, "w"),
189+ sxx: vec(probe, "sxx"),
190+ syy: vec(probe, "syy"),
191+ txy: vec(probe, "txy"),
192+ vm: vec(probe, "vm"),
193+ sp: vec(probe, "sp"),
194+ },
195+ frames: {
196+ t: vec(frames, "t"),
197+ w: frames3d(frames, "w"),
198+ w_total: frames3d(frames, "w_total"),
199+ vm: frames3d(frames, "vm"),
200+ sp: frames3d(frames, "sp"),
201+ },
202+ envelope: { vm: field2d(env, "vm"), sp: field2d(env, "sp") },
203+ spectrum: { f: vec(spec, "f"), p: vec(spec, "p"), w: vec(spec, "w") },
204+ audio: {
205+ fs: num(audio, "fs"),
206+ p: vec(audio, "p"),
207+ peak_pa: num(audio, "peak_pa"),
208+ gain: num(audio, "gain"),
209+ normalize_dbfs: num(audio, "normalize_dbfs"),
210+ model: str(audio, "model"),
211+ },
212+ modes: parseModes(struct(v, "modes")),
213+ contact: parseContact(struct(v, "contact")),
214+ info: parseInfo(struct(v, "info")),
215+ };
216+}
217+
218+export function parseMethodPlan(v: PlainValue): MethodPlan {
219+ if (!isPlainStruct(v)) throw new ResultShapeError("the plan variable is not a struct");
220+ const opt = (name: string) => (v.fields[name] === undefined ? undefined : num(v, name));
221+ return {
222+ method: methodId(str(v, "method")),
223+ ndof: num(v, "ndof"),
224+ work: num(v, "work"),
225+ size_note: str(v, "size_note"),
226+ plate_mass: num(v, "plate_mass"),
227+ thread_k: num(v, "thread_k"),
228+ pendulum_hz: num(v, "pendulum_hz"),
229+ f_max: num(v, "f_max"),
230+ modes_expected: num(v, "modes_expected"),
231+ modes_cap: num(v, "modes_cap"),
232+ hertz_duration: num(v, "hertz_duration"),
233+ hertz_impulse: num(v, "hertz_impulse"),
234+ hertz_peak_force: num(v, "hertz_peak_force"),
235+ contact_k: num(v, "contact_k"),
236+ critical_frequency: num(v, "critical_frequency"),
237+ nsamples: num(v, "nsamples"),
238+ nframes: num(v, "nframes"),
239+ notes: strings(v, "notes"),
240+ nx: opt("nx"),
241+ ny: opt("ny"),
242+ dx: opt("dx"),
243+ dy: opt("dy"),
244+ };
245+}
246+
247+/** Sanity check of a modes struct read back from stage 1, before it is kept. */
248+export function checkModesValue(v: PlainValue): { method: MethodId; nmodes: number } {
249+ if (!isPlainStruct(v)) throw new ResultShapeError("the modes variable is not a struct");
250+ const method = methodId(str(v, "method"));
251+ const freqs = vec(v, "freqs");
252+ return { method, nmodes: freqs.length };
253+}
packages/core/src/results/types.tsadded+149−0View file
@@ -0,0 +1,149 @@
1+import type { Field2D, FieldFrames } from "../fields/field";
2+import type { MethodId } from "../spec/types";
3+
4+export interface ProbeSeries {
5+ x: number;
6+ y: number;
7+ /** Displacement at the probe, all modes [m] */
8+ w: Float64Array;
9+ sxx: Float64Array;
10+ syy: Float64Array;
11+ txy: Float64Array;
12+ vm: Float64Array;
13+ sp: Float64Array;
14+}
15+
16+/** Per-mode table of a result. */
17+export interface ModesResult {
18+ n: number;
19+ /** Frequencies [Hz], ascending; the finite-element method's rigid-body modes first */
20+ freq: Float64Array;
21+ /** Total quality factor */
22+ q: Float64Array;
23+ zeta: Float64Array;
24+ /** Mass-normalized shape at the strike point */
25+ hit: Float64Array;
26+ /** Shape at the hole */
27+ support: Float64Array;
28+ /** Volume participation, the integral of the shape over the plate [m^2 / sqrt(kg)] */
29+ radiation: Float64Array;
30+ radiation_factor: Float64Array;
31+ /** Pressure at the microphone per unit modal acceleration */
32+ acoustic: Float64Array;
33+ audible: boolean[];
34+ /** Modal state at separation */
35+ q0: Float64Array;
36+ v0: Float64Array;
37+ /** The shapes on the grid, one frame per mode */
38+ shapes: FieldFrames;
39+}
40+
41+export interface ContactResult {
42+ model: string;
43+ /** Contact time [s] */
44+ tc: number;
45+ impulse: number;
46+ peak_force: number;
47+ /** Histories over the contact */
48+ t: Float64Array;
49+ force: Float64Array;
50+ /** Empty for the prescribed pulse */
51+ hammer_x: Float64Array;
52+ hammer_v: Float64Array;
53+ /** The plate under the hammer */
54+ plate_w: Float64Array;
55+ plate_v: Float64Array;
56+ hammer_exit_v: number;
57+ plate_exit_v: number;
58+ relative_separation_v: number;
59+ effective_restitution: number;
60+ hertz_duration: number;
61+ hertz_impulse: number;
62+ hertz_peak_force: number;
63+ contact_k: number;
64+}
65+
66+export interface ResultInfo {
67+ method: MethodId;
68+ ndof: number;
69+ nmodes: number;
70+ n_audible: number;
71+ n_rigid: number;
72+ fs: number;
73+ nsamples: number;
74+ nframes: number;
75+ frame_span: number;
76+ plate_mass: number;
77+ thread_k: number;
78+ critical_frequency: number;
79+ f_max: number;
80+ size_note: string;
81+ /** Wall time of the modes stage inside the .m [s] */
82+ modes_wall_s: number;
83+ /** Wall time of the synthesis inside the .m [s] */
84+ wall_s: number;
85+ strike_wall_s: number;
86+ notes: string[];
87+ /** Anything else the .m put in info */
88+ extra: Record<string, number | string | boolean>;
89+}
90+
91+/** Typed view of the `result` struct a strike returns (see plate_result_schema.m). */
92+export interface PlateResult {
93+ method: MethodId;
94+ grid: { x: Float64Array; y: Float64Array };
95+ t: Float64Array;
96+ probe: ProbeSeries;
97+ frames: {
98+ t: Float64Array;
99+ /** Audible modes only */
100+ w: FieldFrames;
101+ /** All modes, rigid-body ones included */
102+ w_total: FieldFrames;
103+ vm: FieldFrames;
104+ sp: FieldFrames;
105+ };
106+ envelope: { vm: Field2D; sp: Field2D };
107+ spectrum: { f: Float64Array; p: Float64Array; w: Float64Array };
108+ audio: {
109+ fs: number;
110+ /** Pressure at the microphone [Pa], mean removed */
111+ p: Float64Array;
112+ peak_pa: number;
113+ /** Gain that puts the peak at normalize_dbfs */
114+ gain: number;
115+ normalize_dbfs: number;
116+ model: string;
117+ };
118+ modes: ModesResult;
119+ contact: ContactResult;
120+ info: ResultInfo;
121+}
122+
123+/** Sizes and estimates before a run (plate_rr_plan, plate_fem_plan). */
124+export interface MethodPlan {
125+ method: MethodId;
126+ ndof: number;
127+ /** Cost proxy of the modes stage */
128+ work: number;
129+ size_note: string;
130+ plate_mass: number;
131+ thread_k: number;
132+ pendulum_hz: number;
133+ f_max: number;
134+ modes_expected: number;
135+ modes_cap: number;
136+ hertz_duration: number;
137+ hertz_impulse: number;
138+ hertz_peak_force: number;
139+ contact_k: number;
140+ critical_frequency: number;
141+ nsamples: number;
142+ nframes: number;
143+ notes: string[];
144+ /** Mesh sizes, for the finite-element method */
145+ nx?: number;
146+ ny?: number;
147+ dx?: number;
148+ dy?: number;
149+}
packages/core/src/runner/convert.tsadded+69−0View file
@@ -0,0 +1,69 @@
1+import { RTV } from "numbl";
2+import type { RuntimeValue } from "numbl";
3+import type { PlainValue } from "./plain";
4+
5+type Kinded = { kind?: string };
6+
7+/** numbl RuntimeValue -> PlainValue. Tensor data is copied so the result owns its memory. */
8+export function toPlain(v: RuntimeValue | undefined): PlainValue {
9+ if (v === undefined) return { kind: "unsupported", description: "undefined" };
10+ if (typeof v === "number" || typeof v === "boolean" || typeof v === "string") return v;
11+ const k = (v as Kinded).kind;
12+ switch (k) {
13+ case "tensor": {
14+ const t = v as { data: Float64Array; imag?: Float64Array; shape: number[] };
15+ const out: PlainValue = {
16+ kind: "tensor",
17+ shape: [...t.shape],
18+ data: Float64Array.from(t.data),
19+ };
20+ if (t.imag) out.imag = Float64Array.from(t.imag);
21+ return out;
22+ }
23+ case "char":
24+ return (v as { value: string }).value;
25+ case "struct": {
26+ const fields: Record<string, PlainValue> = {};
27+ for (const [name, val] of (v as { fields: Map<string, RuntimeValue> }).fields)
28+ fields[name] = toPlain(val);
29+ return { kind: "struct", fields };
30+ }
31+ case "cell": {
32+ const c = v as { data: RuntimeValue[]; shape: number[] };
33+ return { kind: "cell", shape: [...c.shape], items: c.data.map(toPlain) };
34+ }
35+ case "struct_array": {
36+ // struct(...) with a cell-valued field builds a struct array in MATLAB; a single
37+ // element is the common case (e.g. 'notes', {notes}) and reads as a plain struct.
38+ const sa = v as { elements: RuntimeValue[] };
39+ if (sa.elements.length === 1) return toPlain(sa.elements[0]);
40+ return { kind: "cell", shape: [1, sa.elements.length], items: sa.elements.map(toPlain) };
41+ }
42+ case "complex":
43+ return { kind: "unsupported", description: "complex scalar" };
44+ default:
45+ return { kind: "unsupported", description: k ?? typeof v };
46+ }
47+}
48+
49+/** PlainValue -> numbl RuntimeValue, for passing the modes of stage 1 into stage 2. */
50+export function toRuntime(v: PlainValue): RuntimeValue {
51+ if (typeof v === "number" || typeof v === "boolean" || typeof v === "string") return v;
52+ switch (v.kind) {
53+ case "tensor":
54+ return RTV.tensor(
55+ Float64Array.from(v.data),
56+ [...v.shape],
57+ v.imag ? Float64Array.from(v.imag) : undefined,
58+ );
59+ case "struct": {
60+ const fields: Record<string, RuntimeValue> = {};
61+ for (const [name, val] of Object.entries(v.fields)) fields[name] = toRuntime(val);
62+ return RTV.struct(fields);
63+ }
64+ case "cell":
65+ return RTV.cell(v.items.map(toRuntime), [...v.shape]);
66+ case "unsupported":
67+ throw new Error(`cannot convert unsupported value (${v.description}) to numbl`);
68+ }
69+}
packages/core/src/runner/execute.tsadded+89−0View file
@@ -0,0 +1,89 @@
1+import { executeCode, RuntimeError } from "numbl";
2+import type { RuntimeValue } from "numbl";
3+import type { RunRequest, RunResult, BackendInfo } from "./types";
4+import { toPlain, toRuntime } from "./convert";
5+
6+const OPTIMIZATION = "1" as const;
7+
8+declare const __NUMBL_VERSION__: string | undefined;
9+
10+/**
11+ * numbl's package version, injected at build time by the host's Vite config
12+ * (see vite.shared.ts); numbl's own `version` builtin reports the MATLAB
13+ * release it emulates, which is not what "which backend ran it" means.
14+ */
15+export function numblVersion(): string {
16+ return typeof __NUMBL_VERSION__ === "string" ? __NUMBL_VERSION__ : "unknown";
17+}
18+
19+/**
20+ * Run a bundle synchronously in the current thread. Both the Node runner and
21+ * the worker call this, so the two hosts execute identically.
22+ */
23+export function executeBundle(
24+ req: RunRequest,
25+ backend: BackendInfo,
26+ onOutput?: (text: string) => void,
27+): RunResult {
28+ const { bundle } = req;
29+ const entry = bundle.files.find((f) => f.name === bundle.entry);
30+ const output: string[] = [];
31+ const t0 = performance.now();
32+ if (!entry) {
33+ return {
34+ ok: false,
35+ error: `entry file ${bundle.entry} is not in the bundle`,
36+ output,
37+ wallMs: 0,
38+ backend,
39+ };
40+ }
41+ const workspaceFiles = bundle.files
42+ .filter((f) => f.name !== bundle.entry)
43+ .map((f) => ({ name: f.name, source: f.source }));
44+ const initialVariableValues: Record<string, RuntimeValue> = {};
45+ for (const [name, v] of Object.entries(req.inputs ?? {}))
46+ initialVariableValues[name] = toRuntime(v);
47+ try {
48+ const result = executeCode(
49+ entry.source,
50+ {
51+ optimization: OPTIMIZATION,
52+ displayResults: false,
53+ initialVariableValues,
54+ onOutput: (text: string) => {
55+ output.push(text);
56+ onOutput?.(text);
57+ },
58+ },
59+ workspaceFiles,
60+ bundle.entry,
61+ );
62+ const wallMs = performance.now() - t0;
63+ const value = result.variableValues[bundle.resultVariable];
64+ if (value === undefined) {
65+ return {
66+ ok: false,
67+ error: `the script finished but did not define the variable "${bundle.resultVariable}"`,
68+ output,
69+ wallMs,
70+ backend,
71+ };
72+ }
73+ return { ok: true, value: toPlain(value), output, wallMs, backend };
74+ } catch (e) {
75+ const wallMs = performance.now() - t0;
76+ return { ok: false, error: formatError(e), output, wallMs, backend };
77+ }
78+}
79+
80+export function formatError(e: unknown): string {
81+ if (e instanceof RuntimeError) {
82+ const where = [e.file, e.line]
83+ .filter((x) => x !== undefined && x !== null && x !== "")
84+ .join(":");
85+ return where ? `${where}: ${e.message}` : e.message;
86+ }
87+ if (e instanceof Error) return e.message;
88+ return String(e);
89+}
packages/core/src/runner/nodeRunner.tsadded+26−0View file
@@ -0,0 +1,26 @@
1+import type { BackendInfo, RunRequest, RunResult, Runner } from "./types";
2+import { executeBundle, numblVersion } from "./execute";
3+
4+/**
5+ * Runs numbl in the current Node process. Used by tests and scripts; cannot
6+ * cancel.
7+ */
8+export class NodeRunner implements Runner {
9+ private readonly info: BackendInfo = {
10+ engine: "numbl",
11+ version: numblVersion(),
12+ optimization: "1",
13+ host: "node",
14+ };
15+ readonly backend = Promise.resolve(this.info);
16+
17+ run(req: RunRequest, onOutput?: (text: string) => void): Promise<RunResult> {
18+ return Promise.resolve(executeBundle(req, this.info, onOutput));
19+ }
20+ cancel(): void {
21+ /* synchronous host: nothing in flight between awaits */
22+ }
23+ dispose(): void {
24+ /* nothing to release */
25+ }
26+}
packages/core/src/runner/plain.tsadded+49−0View file
@@ -0,0 +1,49 @@
1+/**
2+ * Plain, structured-clone-friendly mirror of the numbl values the lab uses.
3+ * Tensors keep numbl's column-major layout: element (i, j) of an [m, n] array is
4+ * data[i + j*m], and (i, j, k) of an [m, n, p] array is data[i + j*m + k*m*n].
5+ */
6+export interface PlainTensor {
7+ kind: "tensor";
8+ shape: number[];
9+ data: Float64Array;
10+ imag?: Float64Array;
11+}
12+export interface PlainStruct {
13+ kind: "struct";
14+ fields: Record<string, PlainValue>;
15+}
16+export interface PlainCell {
17+ kind: "cell";
18+ shape: number[];
19+ items: PlainValue[];
20+}
21+export interface PlainUnsupported {
22+ kind: "unsupported";
23+ description: string;
24+}
25+export type PlainValue =
26+ number | boolean | string | PlainTensor | PlainStruct | PlainCell | PlainUnsupported;
27+
28+export function isPlainTensor(v: PlainValue | undefined): v is PlainTensor {
29+ return typeof v === "object" && v !== null && v.kind === "tensor";
30+}
31+export function isPlainStruct(v: PlainValue | undefined): v is PlainStruct {
32+ return typeof v === "object" && v !== null && v.kind === "struct";
33+}
34+export function isPlainCell(v: PlainValue | undefined): v is PlainCell {
35+ return typeof v === "object" && v !== null && v.kind === "cell";
36+}
37+
38+/** All ArrayBuffers inside a value, for zero-copy transfer out of a worker. */
39+export function collectBuffers(v: PlainValue, out: ArrayBuffer[] = []): ArrayBuffer[] {
40+ if (isPlainTensor(v)) {
41+ out.push(v.data.buffer as ArrayBuffer);
42+ if (v.imag) out.push(v.imag.buffer as ArrayBuffer);
43+ } else if (isPlainStruct(v)) {
44+ for (const f of Object.values(v.fields)) collectBuffers(f, out);
45+ } else if (isPlainCell(v)) {
46+ for (const item of v.items) collectBuffers(item, out);
47+ }
48+ return out;
49+}
packages/core/src/runner/protocol.tsadded+17−0View file
@@ -0,0 +1,17 @@
1+import type { SourceBundle } from "../generate/bundle";
2+import type { PlainValue } from "./plain";
3+import type { BackendInfo, RunResult } from "./types";
4+
5+export type ToWorker =
6+ | {
7+ type: "run";
8+ id: number;
9+ bundle: SourceBundle;
10+ inputs?: Record<string, PlainValue>;
11+ }
12+ | { type: "info"; id: number };
13+
14+export type FromWorker =
15+ | { type: "output"; id: number; text: string }
16+ | { type: "done"; id: number; result: RunResult }
17+ | { type: "info"; id: number; backend: BackendInfo };
packages/core/src/runner/types.tsadded+48−0View file
@@ -0,0 +1,48 @@
1+import type { SourceBundle } from "../generate/bundle";
2+import type { PlainValue } from "./plain";
3+
4+export interface BackendInfo {
5+ engine: "numbl";
6+ version: string;
7+ /** numbl optimization level: "1" is the JS JIT, "0" the interpreter */
8+ optimization: "0" | "1";
9+ /** Where the code ran */
10+ host: "web-worker" | "node";
11+}
12+
13+export interface RunRequest {
14+ bundle: SourceBundle;
15+ /** Workspace variables defined before the script runs (the modes of stage 1, for stage 2) */
16+ inputs?: Record<string, PlainValue>;
17+}
18+
19+export interface RunSuccess {
20+ ok: true;
21+ value: PlainValue;
22+ /** Everything the script printed */
23+ output: string[];
24+ /** Wall time of executeCode, including JIT compilation [ms] */
25+ wallMs: number;
26+ backend: BackendInfo;
27+}
28+
29+export interface RunFailure {
30+ ok: false;
31+ /** Formatted numbl error, with file and line when known */
32+ error: string;
33+ output: string[];
34+ wallMs: number;
35+ backend: BackendInfo;
36+ /** True when the run was cancelled by the host rather than failing */
37+ cancelled?: boolean;
38+}
39+
40+export type RunResult = RunSuccess | RunFailure;
41+
42+export interface Runner {
43+ run(req: RunRequest, onOutput?: (text: string) => void): Promise<RunResult>;
44+ /** Abandon the run in flight, if any. Its promise resolves with cancelled: true. */
45+ cancel(): void;
46+ dispose(): void;
47+ readonly backend: Promise<BackendInfo>;
48+}
packages/core/src/runner/worker.tsadded+33−0View file
@@ -0,0 +1,33 @@
1+/// <reference lib="webworker" />
2+/**
3+ * Web Worker that owns a numbl instance. One run at a time; the host cancels
4+ * a run by terminating the worker, which is why nothing here needs to be
5+ * interruptible. The modes of stage 1 travel back to the host and return as
6+ * an input of stage 2, so a cancelled or crashed worker loses nothing kept.
7+ */
8+import { executeBundle, numblVersion } from "./execute";
9+import { collectBuffers } from "./plain";
10+import type { FromWorker, ToWorker } from "./protocol";
11+import type { BackendInfo } from "./types";
12+
13+const backend: BackendInfo = {
14+ engine: "numbl",
15+ version: numblVersion(),
16+ optimization: "1",
17+ host: "web-worker",
18+};
19+
20+const post = (msg: FromWorker, transfer: ArrayBuffer[] = []) => self.postMessage(msg, transfer);
21+
22+self.onmessage = (e: MessageEvent<ToWorker>) => {
23+ const msg = e.data;
24+ if (msg.type === "info") {
25+ post({ type: "info", id: msg.id, backend });
26+ return;
27+ }
28+ const result = executeBundle({ bundle: msg.bundle, inputs: msg.inputs }, backend, (text) =>
29+ post({ type: "output", id: msg.id, text }),
30+ );
31+ const transfer = result.ok ? collectBuffers(result.value) : [];
32+ post({ type: "done", id: msg.id, result }, transfer);
33+};
packages/core/src/runner/workerRunner.tsadded+111−0View file
@@ -0,0 +1,111 @@
1+import type { BackendInfo, RunRequest, RunResult, Runner } from "./types";
2+import type { FromWorker, ToWorker } from "./protocol";
3+
4+interface Pending {
5+ resolve: (r: RunResult) => void;
6+ onOutput?: (text: string) => void;
7+ output: string[];
8+ t0: number;
9+}
10+
11+/**
12+ * Runs numbl in a Web Worker the runner owns. Cancelling terminates the worker
13+ * and a fresh one is created for the next run, so a runaway eigensolve can
14+ * always be stopped, with or without cross-origin isolation.
15+ */
16+export class WorkerRunner implements Runner {
17+ private worker: Worker | null = null;
18+ private nextId = 1;
19+ private pending = new Map<number, Pending>();
20+ private infoResolvers = new Map<number, (b: BackendInfo) => void>();
21+ private cachedBackend: BackendInfo | null = null;
22+ private readonly createWorker: () => Worker;
23+
24+ constructor(createWorker?: () => Worker) {
25+ this.createWorker =
26+ createWorker ??
27+ (() => new Worker(new URL("./worker.ts", import.meta.url), { type: "module" }));
28+ }
29+
30+ get backend(): Promise<BackendInfo> {
31+ if (this.cachedBackend) return Promise.resolve(this.cachedBackend);
32+ return new Promise((resolve) => {
33+ const id = this.nextId++;
34+ this.infoResolvers.set(id, (b) => {
35+ this.cachedBackend = b;
36+ resolve(b);
37+ });
38+ this.ensureWorker().postMessage({ type: "info", id } satisfies ToWorker);
39+ });
40+ }
41+
42+ private ensureWorker(): Worker {
43+ if (this.worker) return this.worker;
44+ const w = this.createWorker();
45+ w.onmessage = (e: MessageEvent<FromWorker>) => this.onMessage(e.data);
46+ w.onerror = (e) => this.failAll(`numbl worker crashed: ${e.message || "unknown error"}`);
47+ this.worker = w;
48+ return w;
49+ }
50+
51+ private onMessage(msg: FromWorker) {
52+ if (msg.type === "info") {
53+ this.infoResolvers.get(msg.id)?.(msg.backend);
54+ this.infoResolvers.delete(msg.id);
55+ return;
56+ }
57+ const p = this.pending.get(msg.id);
58+ if (!p) return;
59+ if (msg.type === "output") {
60+ p.output.push(msg.text);
61+ p.onOutput?.(msg.text);
62+ } else if (msg.type === "done") {
63+ this.pending.delete(msg.id);
64+ p.resolve(msg.result);
65+ }
66+ }
67+
68+ private failAll(error: string, cancelled = false) {
69+ const backend = this.cachedBackend ?? {
70+ engine: "numbl",
71+ version: "unknown",
72+ optimization: "1",
73+ host: "web-worker",
74+ };
75+ for (const p of this.pending.values()) {
76+ p.resolve({
77+ ok: false,
78+ error,
79+ output: p.output,
80+ wallMs: performance.now() - p.t0,
81+ backend,
82+ cancelled,
83+ });
84+ }
85+ this.pending.clear();
86+ }
87+
88+ run(req: RunRequest, onOutput?: (text: string) => void): Promise<RunResult> {
89+ return new Promise((resolve) => {
90+ const id = this.nextId++;
91+ this.pending.set(id, { resolve, onOutput, output: [], t0: performance.now() });
92+ this.ensureWorker().postMessage({
93+ type: "run",
94+ id,
95+ bundle: req.bundle,
96+ inputs: req.inputs,
97+ } satisfies ToWorker);
98+ });
99+ }
100+
101+ cancel(): void {
102+ if (!this.worker) return;
103+ this.worker.terminate();
104+ this.worker = null;
105+ this.failAll("run cancelled", true);
106+ }
107+
108+ dispose(): void {
109+ this.cancel();
110+ }
111+}
packages/core/src/spec/defaults.tsadded+37−0View file
@@ -0,0 +1,37 @@
1+import { SCENARIO_PRESETS } from "./presets";
2+import type { MethodId, ProblemSpec } from "./types";
3+
4+/** The default problem: the reference centre hit. */
5+export function defaultSpec(): ProblemSpec {
6+ return cloneSpec(SCENARIO_PRESETS[0].spec);
7+}
8+
9+/** Deep-copy a spec (specs are plain data). */
10+export function cloneSpec(spec: ProblemSpec): ProblemSpec {
11+ return JSON.parse(JSON.stringify(spec)) as ProblemSpec;
12+}
13+
14+/**
15+ * The part of the spec the modes stage depends on: the plate, the elastic
16+ * constants, the hole, the thread's length (its spring), the sample rate
17+ * (the 0.47 fs cutoff), the mode cap and the method's numerics. Two specs
18+ * with the same key have the same modes, so the host reuses them and runs
19+ * only the strike stage. The material's Q, the dashpot, the hammer, the
20+ * strike point, the probe, the microphone and the rest of the render
21+ * settings belong to the strike stage.
22+ */
23+export function modesKey(spec: ProblemSpec, method: MethodId): string {
24+ const { plate, material, hole, thread, render } = spec;
25+ return JSON.stringify({
26+ method,
27+ plate,
28+ E: material.E,
29+ nu: material.nu,
30+ rho: material.rho,
31+ hole,
32+ length: thread.length,
33+ fs: render.fs,
34+ modes: render.modes,
35+ numerics: method === "rr" ? spec.rr : spec.fem,
36+ });
37+}
packages/core/src/spec/presets.tsadded+231−0View file
@@ -0,0 +1,231 @@
1+import type { Hammer, Material, PlateGeometry, ProblemSpec } from "./types";
2+
3+/**
4+ * Material presets. E, nu and rho are room-temperature handbook figures for
5+ * the named alloy (ASM Handbook, MatWeb) and typical rather than certified.
6+ * The quality factor Q is the internal, structural loss alone; it is an
7+ * order-of-magnitude figure, and the air loading and the thread's dashpot are
8+ * added to it per mode (plate_damping.m). The default 6061-T6 values are
9+ * those of the simulators this lab follows.
10+ */
11+export interface MaterialPreset {
12+ id: string;
13+ material: Material;
14+ source: string;
15+}
16+
17+export const MATERIAL_PRESETS: MaterialPreset[] = [
18+ {
19+ id: "6061-t6",
20+ material: { name: "6061-T6 aluminium", E: 68.9e9, nu: 0.33, rho: 2700, Q: 1400 },
21+ source: "6061-T6 sheet, typical values; Q = 1400 is the reference simulators' figure.",
22+ },
23+ {
24+ id: "a36-steel",
25+ material: { name: "A36 structural steel", E: 200e9, nu: 0.26, rho: 7850, Q: 2500 },
26+ source: "Hot-rolled low-carbon steel; steel rings longer than aluminium.",
27+ },
28+ {
29+ id: "304-stainless",
30+ material: { name: "304 stainless steel", E: 193e9, nu: 0.29, rho: 8000, Q: 2000 },
31+ source: "Annealed 304 sheet, typical values.",
32+ },
33+ {
34+ id: "c260-brass",
35+ material: { name: "C260 cartridge brass", E: 110e9, nu: 0.375, rho: 8530, Q: 1500 },
36+ source: "70/30 brass, half-hard sheet, typical values.",
37+ },
38+ {
39+ id: "b20-bronze",
40+ material: { name: "B20 bell bronze (CuSn20)", E: 105e9, nu: 0.34, rho: 8800, Q: 4000 },
41+ source: "Bell and cymbal bronze; its low internal loss is what makes it ring.",
42+ },
43+ {
44+ id: "ti-6al-4v",
45+ material: { name: "Ti-6Al-4V titanium", E: 113.8e9, nu: 0.342, rho: 4430, Q: 3000 },
46+ source: "Annealed Grade 5 titanium sheet, typical values.",
47+ },
48+];
49+
50+export interface PlatePreset {
51+ id: string;
52+ name: string;
53+ plate: PlateGeometry;
54+ note: string;
55+}
56+
57+export const PLATE_PRESETS: PlatePreset[] = [
58+ {
59+ id: "coupon-30x30x1",
60+ name: "30 x 30 x 1 mm coupon",
61+ plate: { width: 0.03, height: 0.03, thickness: 0.001 },
62+ note: "The reference plate: a square coupon whose lowest partial is near 3.6 kHz and whose highest kept mode is ultrasonic.",
63+ },
64+ {
65+ id: "coupon-40x30x1",
66+ name: "40 x 30 x 1 mm coupon",
67+ plate: { width: 0.04, height: 0.03, thickness: 0.001 },
68+ note: "Not square: the degenerate pairs of the square split into distinct partials.",
69+ },
70+ {
71+ id: "coupon-30x30x0p5",
72+ name: "30 x 30 x 0.5 mm shim",
73+ plate: { width: 0.03, height: 0.03, thickness: 0.0005 },
74+ note: "Half the thickness: every partial at half the frequency, and twice as many below the cutoff.",
75+ },
76+ {
77+ id: "plate-60x60x1",
78+ name: "60 x 60 x 1 mm plate",
79+ plate: { width: 0.06, height: 0.06, thickness: 0.001 },
80+ note: "Four times the area: partials a quarter as high, four times as many modes, and a longer eigensolve.",
81+ },
82+ {
83+ id: "plate-50x30x2",
84+ name: "50 x 30 x 2 mm bar",
85+ plate: { width: 0.05, height: 0.03, thickness: 0.002 },
86+ note: "Thicker and oblong; the thin-plate assumption of the Rayleigh-Ritz method is at its edge here.",
87+ },
88+];
89+
90+export interface HammerPreset {
91+ id: string;
92+ name: string;
93+ hammer: Hammer;
94+ note: string;
95+}
96+
97+export const HAMMER_PRESETS: HammerPreset[] = [
98+ {
99+ id: "steel-10g",
100+ name: "10 g steel hammer, 2 mm tip",
101+ hammer: { mass: 0.01, tip_radius: 0.002, speed: 0.45, E: 200e9, nu: 0.29, restitution: 0.62 },
102+ note: "The reference strike: a small steel hammer at 0.45 m/s.",
103+ },
104+ {
105+ id: "steel-5g-sharp",
106+ name: "5 g steel hammer, 1 mm tip",
107+ hammer: { mass: 0.005, tip_radius: 0.001, speed: 0.6, E: 200e9, nu: 0.29, restitution: 0.62 },
108+ note: "Lighter and sharper: a shorter contact excites the high partials more.",
109+ },
110+ {
111+ id: "brass-20g",
112+ name: "20 g brass hammer, 4 mm tip",
113+ hammer: { mass: 0.02, tip_radius: 0.004, speed: 0.3, E: 110e9, nu: 0.35, restitution: 0.55 },
114+ note: "Heavier, softer and rounder: a longer contact that favours the lowest partials.",
115+ },
116+];
117+
118+export interface ScenarioPreset {
119+ id: string;
120+ name: string;
121+ note: string;
122+ spec: ProblemSpec;
123+}
124+
125+const mat = (id: string) => ({ ...MATERIAL_PRESETS.find((m) => m.id === id)!.material });
126+const plate = (id: string) => ({ ...PLATE_PRESETS.find((p) => p.id === id)!.plate });
127+const hammer = (id: string) => ({ ...HAMMER_PRESETS.find((h) => h.id === id)!.hammer });
128+
129+const COMMON = {
130+ hole: { diameter: 0.8e-3, edge_offset: 1.5e-3 },
131+ thread: { length: 0.2, dashpot: 7.5e-4 },
132+ probe: { x: 0.008, y: -0.006 },
133+ mic: { distance: 0.35, air_density: 1.204, sound_speed: 343 },
134+ render: {
135+ fs: 96000,
136+ duration: 4,
137+ modes: 72,
138+ normalize_dbfs: -1,
139+ nframes: 120,
140+ frame_span: 0.002,
141+ },
142+ rr: { basis_order: 12, quadrature_order: 30, grid: 49 },
143+ fem: { mesh_x: 48, mesh_y: 48 },
144+};
145+
146+/**
147+ * Whole problems. The first is the reference centre hit of the simulators
148+ * this lab follows; the others move the strike, change the hammer or the
149+ * plate. Each note says what to look for.
150+ */
151+export const SCENARIO_PRESETS: ScenarioPreset[] = [
152+ {
153+ id: "center-hit",
154+ name: "Centre hit (the reference)",
155+ note: "The 30 mm 6061-T6 coupon struck at its centre. The centre lies on both symmetry axes, so most asymmetric modes are silent; the strong partials are near 6.7, 17.2 and 33.5 kHz (Rayleigh-Ritz) and a percent or two away from those in the finite-element model.",
156+ spec: {
157+ plate: plate("coupon-30x30x1"),
158+ material: mat("6061-t6"),
159+ hammer: hammer("steel-10g"),
160+ strike: { x: 0, y: 0 },
161+ ...COMMON,
162+ },
163+ },
164+ {
165+ id: "off-center-hit",
166+ name: "Off-centre hit, (6, -4) mm",
167+ note: "The same coupon struck 6 mm right of and 4 mm below the centre. The modes the centre hit could not reach now sound, including the lowest at 3.6 kHz; the finite-element contact is shorter and lighter than at the centre because the plate under the hammer is more compliant there.",
168+ spec: {
169+ plate: plate("coupon-30x30x1"),
170+ material: mat("6061-t6"),
171+ hammer: hammer("steel-10g"),
172+ strike: { x: 0.006, y: -0.004 },
173+ ...COMMON,
174+ },
175+ },
176+ {
177+ id: "sharp-tap",
178+ name: "Sharp tap near a corner",
179+ note: "A light, sharp hammer near the lower-right corner: a short contact that excites the high partials, on the part of the plate that moves most.",
180+ spec: {
181+ plate: plate("coupon-30x30x1"),
182+ material: mat("6061-t6"),
183+ hammer: hammer("steel-5g-sharp"),
184+ strike: { x: 0.011, y: -0.011 },
185+ ...COMMON,
186+ },
187+ },
188+ {
189+ id: "brass-plate",
190+ name: "Brass coupon, oblong",
191+ note: "A 40 x 30 mm brass coupon struck off-centre with the heavier brass hammer. Denser and softer than aluminium, brass rings lower; the oblong outline splits the square's degenerate pairs into distinct partials.",
192+ spec: {
193+ plate: plate("coupon-40x30x1"),
194+ material: mat("c260-brass"),
195+ hammer: hammer("brass-20g"),
196+ strike: { x: 0.008, y: -0.005 },
197+ ...COMMON,
198+ },
199+ },
200+ {
201+ id: "thin-shim",
202+ name: "Thin shim",
203+ note: "A 0.5 mm shim of the same outline: every partial at half the frequency, twice as many below the cutoff, and a contact the plate yields to much more, so the two contact models differ most here.",
204+ spec: {
205+ plate: plate("coupon-30x30x0p5"),
206+ material: mat("6061-t6"),
207+ hammer: hammer("steel-10g"),
208+ strike: { x: 0.005, y: -0.003 },
209+ ...COMMON,
210+ },
211+ },
212+ {
213+ id: "bronze-bell-plate",
214+ name: "Bell-bronze plate, 60 mm",
215+ note: "A 60 mm bronze square: four times the area, partials a quarter as high, many more modes below the cutoff, and bronze's low loss keeps it ringing for the whole four seconds. The eigensolve takes longer.",
216+ spec: {
217+ plate: plate("plate-60x60x1"),
218+ material: mat("b20-bronze"),
219+ hammer: hammer("steel-10g"),
220+ strike: { x: 0.012, y: -0.008 },
221+ ...COMMON,
222+ probe: { x: 0.016, y: -0.012 },
223+ },
224+ },
225+];
226+
227+export function scenarioPreset(id: string): ScenarioPreset {
228+ const s = SCENARIO_PRESETS.find((x) => x.id === id);
229+ if (!s) throw new Error(`unknown scenario ${id}`);
230+ return s;
231+}
packages/core/src/spec/types.tsadded+123−0View file
@@ -0,0 +1,123 @@
1+/**
2+ * The problem specification. This mirrors, field for field, the `params` struct
3+ * that the generated MATLAB source builds (see generate/params.ts), so that the
4+ * controls, the source on screen and the numbers numbl computes all describe
5+ * the same object.
6+ *
7+ * Units are SI throughout: metres, seconds, newtons, pascals, kilograms.
8+ * Coordinates on the plate are measured from its centre, x to the right and
9+ * y towards the threaded top edge, as in the simulators this lab follows.
10+ */
11+
12+export interface PlateGeometry {
13+ /** Side along x [m] */
14+ width: number;
15+ /** Side along y [m]; the hole is near the top edge, y = +height/2 */
16+ height: number;
17+ thickness: number;
18+}
19+
20+export interface Material {
21+ name: string;
22+ /** Young's modulus [Pa] */
23+ E: number;
24+ /** Poisson's ratio */
25+ nu: number;
26+ /** Density [kg/m^3] */
27+ rho: number;
28+ /** Structural quality factor: internal loss, 1/Q per mode */
29+ Q: number;
30+}
31+
32+/** The suspension hole on the centreline, `edge_offset` below the top edge. */
33+export interface Hole {
34+ diameter: number;
35+ edge_offset: number;
36+}
37+
38+/** The thread through the hole: a pendulum spring m g / L and a dashpot at the hole. */
39+export interface Thread {
40+ length: number;
41+ /** Localized loss at the hole [N s/m] */
42+ dashpot: number;
43+}
44+
45+/** A small steel hammer with a rounded tip. */
46+export interface Hammer {
47+ mass: number;
48+ tip_radius: number;
49+ /** Approach speed [m/s] */
50+ speed: number;
51+ E: number;
52+ nu: number;
53+ /** Nominal coefficient of restitution */
54+ restitution: number;
55+}
56+
57+export interface Point {
58+ x: number;
59+ y: number;
60+}
61+
62+/** A microphone on the plate's normal. */
63+export interface Microphone {
64+ distance: number;
65+ air_density: number;
66+ sound_speed: number;
67+}
68+
69+export interface RenderSettings {
70+ /** Sample rate of the series and the audio [Hz]; modes are kept below 0.47 fs */
71+ fs: number;
72+ /** Rendered duration [s] */
73+ duration: number;
74+ /** At most this many audible modes */
75+ modes: number;
76+ /** Peak level of the exported sound [dBFS] */
77+ normalize_dbfs: number;
78+ /** Animation frames, over the first `frame_span` seconds */
79+ nframes: number;
80+ frame_span: number;
81+}
82+
83+/** Numerics of the Rayleigh-Ritz method. */
84+export interface RrSettings {
85+ /** Legendre order per side: (order + 1)^2 coefficients */
86+ basis_order: number;
87+ /** Gauss-Legendre points per side */
88+ quadrature_order: number;
89+ /** Display grid, nodes per side */
90+ grid: number;
91+}
92+
93+/** Numerics of the finite-element method. */
94+export interface FemSettings {
95+ mesh_x: number;
96+ mesh_y: number;
97+}
98+
99+export interface ProblemSpec {
100+ plate: PlateGeometry;
101+ material: Material;
102+ hole: Hole;
103+ thread: Thread;
104+ hammer: Hammer;
105+ strike: Point;
106+ probe: Point;
107+ mic: Microphone;
108+ render: RenderSettings;
109+ rr: RrSettings;
110+ fem: FemSettings;
111+}
112+
113+export type MethodId = "rr" | "fem";
114+
115+/** Whether a point (from the centre) lies on the plate. */
116+export function onPlate(plate: PlateGeometry, x: number, y: number): boolean {
117+ return Math.abs(x) <= plate.width / 2 && Math.abs(y) <= plate.height / 2;
118+}
119+
120+/** The hole's position, on the centreline below the top edge. */
121+export function holePosition(spec: ProblemSpec): Point {
122+ return { x: 0, y: spec.plate.height / 2 - spec.hole.edge_offset };
123+}
packages/core/src/spec/validate.tsadded+85−0View file
@@ -0,0 +1,85 @@
1+import { onPlate, type ProblemSpec } from "./types";
2+
3+export interface ValidationIssue {
4+ /** Dotted path into the spec, e.g. "plate.thickness" */
5+ path: string;
6+ message: string;
7+ severity: "error" | "warning";
8+}
9+
10+/**
11+ * Structural checks a control panel can show before running anything. The
12+ * `.m` code repeats the checks that matter for the numerics, so a spec that
13+ * passes here can still be rejected by the solver with a legible message.
14+ */
15+export function validateSpec(spec: ProblemSpec): ValidationIssue[] {
16+ const out: ValidationIssue[] = [];
17+ const err = (path: string, message: string) => out.push({ path, message, severity: "error" });
18+ const warn = (path: string, message: string) => out.push({ path, message, severity: "warning" });
19+
20+ const { plate, material, hole, thread, hammer, strike, probe, mic, render, rr, fem } = spec;
21+ if (!(plate.width > 0)) err("plate.width", "must be positive");
22+ if (!(plate.height > 0)) err("plate.height", "must be positive");
23+ if (!(plate.thickness > 0)) err("plate.thickness", "must be positive");
24+ if (plate.thickness > 0.1 * Math.min(plate.width, plate.height))
25+ warn(
26+ "plate.thickness",
27+ "thickness above a tenth of the shorter side: the Kirchhoff-Love method is unreliable; the Mindlin-Reissner method carries the shear",
28+ );
29+ if (!(material.E > 0)) err("material.E", "must be positive");
30+ if (!(material.nu >= 0 && material.nu < 0.5)) err("material.nu", "must lie in [0, 0.5)");
31+ if (!(material.rho > 0)) err("material.rho", "must be positive");
32+ if (!(material.Q > 0)) err("material.Q", "must be positive");
33+
34+ if (!(hole.diameter > 0)) err("hole.diameter", "must be positive");
35+ if (!(hole.edge_offset > hole.diameter / 2))
36+ err("hole.edge_offset", "the hole must lie inside the plate: offset above the hole's radius");
37+ if (!(hole.edge_offset < plate.height / 2))
38+ err("hole.edge_offset", "the hole must lie above the plate's centre");
39+ if (!(thread.length > 0)) err("thread.length", "must be positive");
40+ if (thread.dashpot < 0) err("thread.dashpot", "cannot be negative");
41+
42+ if (!(hammer.mass > 0)) err("hammer.mass", "must be positive");
43+ if (!(hammer.tip_radius > 0)) err("hammer.tip_radius", "must be positive");
44+ if (!(hammer.speed > 0)) err("hammer.speed", "must be positive");
45+ if (!(hammer.E > 0)) err("hammer.E", "must be positive");
46+ if (!(hammer.nu >= 0 && hammer.nu < 0.5)) err("hammer.nu", "must lie in [0, 0.5)");
47+ if (!(hammer.restitution >= 0 && hammer.restitution <= 1))
48+ err("hammer.restitution", "must lie in [0, 1]");
49+ if (hammer.speed > 2)
50+ warn(
51+ "hammer.speed",
52+ "a hard strike would dent aluminium; the model is linear and does not know",
53+ );
54+
55+ if (!onPlate(plate, strike.x, strike.y)) err("strike", "strike point lies outside the plate");
56+ if (!onPlate(plate, probe.x, probe.y)) err("probe", "probe lies outside the plate");
57+
58+ if (!(mic.distance > 0)) err("mic.distance", "must be positive");
59+ if (!(mic.air_density > 0)) err("mic.air_density", "must be positive");
60+ if (!(mic.sound_speed > 0)) err("mic.sound_speed", "must be positive");
61+
62+ if (!(render.fs > 0)) err("render.fs", "sample rate must be positive");
63+ if (!(render.duration > 0)) err("render.duration", "duration must be positive");
64+ if (render.duration > 10) warn("render.duration", "a long render is slow to synthesize");
65+ if (!(render.modes >= 1)) err("render.modes", "keep at least one mode");
66+ if (render.nframes < 2) err("render.nframes", "need at least 2 frames");
67+ if (!(render.frame_span > 0)) err("render.frame_span", "must be positive");
68+ if (render.frame_span > render.duration)
69+ err("render.frame_span", "the frames cannot outlast the render");
70+ if (!(render.normalize_dbfs <= 0)) err("render.normalize_dbfs", "a peak above 0 dBFS clips");
71+
72+ if (!Number.isInteger(rr.basis_order) || rr.basis_order < 2)
73+ err("rr.basis_order", "the basis needs order 2 or more");
74+ if (rr.basis_order > 24) warn("rr.basis_order", "a high order costs time and conditioning");
75+ if (!Number.isInteger(rr.quadrature_order) || rr.quadrature_order < rr.basis_order + 2)
76+ err("rr.quadrature_order", "use at least basis order + 2 quadrature points per side");
77+ if (!Number.isInteger(rr.grid) || rr.grid < 5)
78+ err("rr.grid", "the display grid needs 5 nodes or more");
79+
80+ if (!Number.isInteger(fem.mesh_x) || fem.mesh_x < 4) err("fem.mesh_x", "at least 4 elements");
81+ if (!Number.isInteger(fem.mesh_y) || fem.mesh_y < 4) err("fem.mesh_y", "at least 4 elements");
82+ if (fem.mesh_x * fem.mesh_y > 100 * 100)
83+ warn("fem", "more than 10,000 elements: the eigensolve will take minutes");
84+ return out;
85+}
packages/core/test/fem.test.tsadded+149−0View file
@@ -0,0 +1,149 @@
1+import { describe, expect, it } from "vitest";
2+import {
3+ audibleFreqs,
4+ isolatedAudible,
5+ maxAbs,
6+ reference,
7+ referenceWav,
8+ runScript,
9+ scalar,
10+ solve,
11+ testSpec,
12+} from "./helpers";
13+
14+/**
15+ * The finite-element method against the diagnostics of
16+ * aluminum_plate_fem_sim.py (sim-docs) at the meshes the reference was run
17+ * at: the same element, assembly, thread spring, damping, coupled contact
18+ * and radiation model. The eigensolver differs (subspace iteration here,
19+ * ARPACK there) and the contact integrator differs (fixed-step Runge-Kutta
20+ * here, DOP853 there), so the agreement is to the accuracy of both, which
21+ * is far tighter than the physics.
22+ */
23+function checkAgainst(
24+ name: string,
25+ result: Awaited<ReturnType<typeof solve>>["result"],
26+ wavTol: number,
27+) {
28+ const ref = reference(name);
29+ const f = audibleFreqs(result);
30+ expect(f.length).toBe(ref.modes.length);
31+ ref.modes.forEach((m, k) => {
32+ expect(Math.abs(f[k] - m.frequency_hz) / m.frequency_hz).toBeLessThan(2e-7);
33+ });
34+ expect(result.info.n_rigid).toBe(ref.derived.low_frequency_momentum_modes);
35+ expect(result.info.ndof).toBe(ref.derived.fem_degrees_of_freedom);
36+ for (const k of isolatedAudible(result)) {
37+ const m = ref.modes[k - result.info.n_rigid];
38+ expect(result.modes.q[k]).toBeCloseTo(m.q_total, 2);
39+ expect(Math.abs(result.modes.hit[k])).toBeCloseTo(Math.abs(m.hit_participation), 3);
40+ }
41+ const c = result.contact;
42+ const d = ref.derived;
43+ const rel = (x: number, y: number) => Math.abs(x - y) / Math.abs(y);
44+ expect(rel(c.tc, d.contact_duration_s)).toBeLessThan(1e-5);
45+ expect(rel(c.impulse, d.impact_impulse_Ns)).toBeLessThan(1e-4);
46+ expect(rel(c.peak_force, d.peak_contact_force_N)).toBeLessThan(1e-5);
47+ expect(rel(c.hammer_exit_v, d.hammer_exit_velocity_m_s_positive_into_plate)).toBeLessThan(1e-5);
48+ expect(
49+ rel(c.plate_exit_v, d.plate_contact_point_exit_velocity_m_s_positive_into_plate),
50+ ).toBeLessThan(1e-5);
51+ expect(c.effective_restitution).toBeCloseTo(d.effective_relative_restitution, 5);
52+ const wav = referenceWav(name);
53+ const a = result.audio.p;
54+ const g = result.audio.gain;
55+ let worst = 0;
56+ for (let i = 0; i < wav.samples.length; i++)
57+ worst = Math.max(worst, Math.abs(a[i] * g - wav.samples[i]));
58+ expect(worst).toBeLessThan(wavTol);
59+}
60+
61+describe("finite elements against the reference simulator", () => {
62+ it("reproduces the centre hit on a 16 x 16 mesh", async () => {
63+ const { result } = await solve(testSpec("center-hit"), "fem");
64+ // degenerate pairs mix differently between the two eigensolvers, which moves the sound by a little
65+ checkAgainst("fem_center_16", result, 5e-4);
66+ expect(result.contact.hammer_v.length).toBe(result.contact.t.length);
67+ expect(result.contact.tc).toBeGreaterThan(result.contact.hertz_duration);
68+ expect(result.contact.peak_force).toBeLessThan(result.contact.hertz_peak_force / 4);
69+ });
70+
71+ it("reproduces the centre hit on a 24 x 24 mesh", async () => {
72+ const spec = testSpec("center-hit");
73+ spec.fem.mesh_x = 24;
74+ spec.fem.mesh_y = 24;
75+ const { result } = await solve(spec, "fem");
76+ checkAgainst("fem_center_24", result, 5e-4);
77+ });
78+
79+ it("reproduces the off-centre hit on a 32 x 32 mesh", async () => {
80+ const spec = testSpec("off-center-hit");
81+ spec.fem.mesh_x = 32;
82+ spec.fem.mesh_y = 32;
83+ const { result } = await solve(spec, "fem");
84+ checkAgainst("fem_off_32", result, 5e-4);
85+ expect(Math.abs(result.modes.hit[result.info.n_rigid])).toBeGreaterThan(1);
86+ });
87+
88+ it("converges the main partial from above as the mesh is refined", async () => {
89+ const at = async (n: number) => {
90+ const spec = testSpec();
91+ spec.fem.mesh_x = n;
92+ spec.fem.mesh_y = n;
93+ spec.render.duration = 0.01;
94+ const { result } = await solve(spec, "fem");
95+ return audibleFreqs(result)[2];
96+ };
97+ const f16 = await at(16);
98+ const f24 = await at(24);
99+ expect(f16).toBeGreaterThan(f24);
100+ expect(f24).toBeGreaterThan(reference("rr_center").modes[2].frequency_hz);
101+ });
102+
103+ it("finds the rigid-body modes and mass-normalizes every mode", async () => {
104+ const spec = testSpec();
105+ const out = await runScript(
106+ spec,
107+ [
108+ "P = plate_setup(params);",
109+ "A = plate_fem_assemble(params, P);",
110+ "[lam, V, info] = plate_fem_eigs(A.K, A.M, 12, 30, 60, 1e-10);",
111+ "out.converged = double(info.converged);",
112+ "out.residual = info.residual;",
113+ "out.iterations = info.iterations;",
114+ "out.lam1 = lam(1); out.lam3 = lam(3); out.lam4 = lam(4);",
115+ "out.norm4 = V(:, 4)' * (A.M * V(:, 4));",
116+ "out.orth = abs(V(:, 4)' * (A.M * V(:, 5)));",
117+ "out.pendulum_hz = sqrt(max(lam(3), 0)) / (2 * pi);",
118+ ].join("\n"),
119+ );
120+ expect(scalar(out, "converged")).toBe(1);
121+ // eigenvalues converge quadratically in the residual: a 1e-10 eigenvalue tolerance goes with residuals near 1e-5
122+ expect(scalar(out, "residual")).toBeLessThan(1e-4);
123+ expect(scalar(out, "iterations")).toBeLessThan(40);
124+ // two free rotations at zero, then the pendulum, then the first bending mode
125+ expect(Math.abs(scalar(out, "lam1"))).toBeLessThan(1);
126+ expect(scalar(out, "pendulum_hz")).toBeGreaterThan(1);
127+ expect(scalar(out, "pendulum_hz")).toBeLessThan(5);
128+ expect(Math.sqrt(scalar(out, "lam4")) / (2 * Math.PI)).toBeCloseTo(3636.156, 2);
129+ expect(scalar(out, "norm4")).toBeCloseTo(1, 8);
130+ expect(scalar(out, "orth")).toBeLessThan(1e-8);
131+ });
132+
133+ it("carries the mode shapes, curvatures and frames the views draw", async () => {
134+ const spec = testSpec();
135+ spec.render.duration = 0.02;
136+ const { result } = await solve(spec, "fem");
137+ expect(result.grid.x.length).toBe(spec.fem.mesh_x + 1);
138+ expect(result.modes.shapes.ny).toBe(spec.fem.mesh_y + 1);
139+ expect(result.modes.shapes.nf).toBe(result.modes.n);
140+ expect(result.frames.w_total.nf).toBe(spec.render.nframes);
141+ expect(maxAbs(result.frames.vm.data)).toBeGreaterThan(0);
142+ // the rigid modes carry no curvature
143+ const n = result.modes.shapes.nx * result.modes.shapes.ny;
144+ let kmax = 0;
145+ for (let i = 0; i < n; i++) kmax = Math.max(kmax, Math.abs(result.frames.w_total.data[i]));
146+ expect(result.info.n_rigid).toBe(3);
147+ expect(kmax).toBeGreaterThanOrEqual(0);
148+ });
149+});
packages/core/test/fixtures/fem_center_16.jsonadded+147−0View file
@@ -0,0 +1,147 @@
1+{
2+ "model": "Q4 Mindlin-Reissner FEM; sparse eigensolve; coupled Hunt-Crossley/Hertz impact",
3+ "config": {
4+ "width": 0.03,
5+ "height": 0.03,
6+ "thickness": 0.001,
7+ "density": 2700.0,
8+ "young": 68900000000.0,
9+ "poisson": 0.33,
10+ "material_q": 1400.0,
11+ "shear_correction": 0.8333333333333334,
12+ "mesh_x": 16,
13+ "mesh_y": 16,
14+ "hole_diameter": 0.0008,
15+ "hole_edge_offset": 0.0015,
16+ "thread_length": 0.2,
17+ "thread_dashpot": 0.00075,
18+ "hit_x": 0.0,
19+ "hit_y": 0.0,
20+ "hammer_mass": 0.01,
21+ "hammer_tip_radius": 0.002,
22+ "hammer_speed": 0.45,
23+ "hammer_young": 200000000000.0,
24+ "hammer_poisson": 0.29,
25+ "restitution": 0.62,
26+ "mic_distance": 0.35,
27+ "air_density": 1.204,
28+ "sound_speed": 343.0,
29+ "sample_rate": 96000,
30+ "duration": 4.0,
31+ "modes": 72,
32+ "normalize_dbfs": -1.0
33+ },
34+ "derived": {
35+ "plate_mass_kg": 0.002428642831973649,
36+ "fem_degrees_of_freedom": 867,
37+ "retained_mode_count": 12,
38+ "low_frequency_momentum_modes": 3,
39+ "contact_duration_s": 0.00011718534618607626,
40+ "impact_impulse_Ns": 0.0016584850018954377,
41+ "peak_contact_force_N": 23.970981572218452,
42+ "hammer_exit_velocity_m_s_positive_into_plate": 0.2841514204940169,
43+ "plate_contact_point_exit_velocity_m_s_positive_into_plate": 0.48099945672947186,
44+ "relative_separation_velocity_m_s": -0.19684803623545494,
45+ "effective_relative_restitution": 0.4374400805232332,
46+ "critical_frequency_hz": 12120.88938013311,
47+ "unnormalized_peak_pressure_pa_approx": 4.7216855423089564e-08
48+ },
49+ "modes": [
50+ {
51+ "mode": 1,
52+ "frequency_hz": 3636.1559922758443,
53+ "q_total": 408.52934721279394,
54+ "hit_participation": 2.65408506282041e-07,
55+ "support_participation": 3.9601586076596717e-07,
56+ "radiation_participation": 1.2000437536047182e-14
57+ },
58+ {
59+ "mode": 2,
60+ "frequency_hz": 8051.305581345652,
61+ "q_total": 301.4657641622347,
62+ "hit_participation": -2.9405292327351075e-06,
63+ "support_participation": 39.652890438803574,
64+ "radiation_participation": 6.833854201230942e-10
65+ },
66+ {
67+ "mode": 3,
68+ "frequency_hz": 9039.966872283216,
69+ "q_total": 290.045426979398,
70+ "hit_participation": 34.731473750403524,
71+ "support_participation": -3.119227344042696,
72+ "radiation_participation": -4.263546029355421e-11
73+ },
74+ {
75+ "mode": 4,
76+ "frequency_hz": 11215.971597875456,
77+ "q_total": 265.96971557867914,
78+ "hit_participation": -5.76698784577264e-07,
79+ "support_participation": -9.557913690008505,
80+ "radiation_participation": -8.487482880414638e-11
81+ },
82+ {
83+ "mode": 5,
84+ "frequency_hz": 11215.972189384156,
85+ "q_total": 264.9631028602091,
86+ "hit_participation": -6.503673939709411e-06,
87+ "support_participation": -37.86151081853688,
88+ "radiation_participation": -3.362485268790672e-10
89+ },
90+ {
91+ "mode": 6,
92+ "frequency_hz": 19188.80218623442,
93+ "q_total": 212.48176215610172,
94+ "hit_participation": -33.25757491594329,
95+ "support_participation": 39.540338683284176,
96+ "radiation_participation": 1.1996806505024452e-10
97+ },
98+ {
99+ "mode": 7,
100+ "frequency_hz": 23684.168413632236,
101+ "q_total": 194.58706595905363,
102+ "hit_participation": -6.696686471891198e-07,
103+ "support_participation": 10.777021294339091,
104+ "radiation_participation": 2.1457799319807075e-11
105+ },
106+ {
107+ "mode": 8,
108+ "frequency_hz": 23684.16887004039,
109+ "q_total": 194.54860976090694,
110+ "hit_participation": 6.795936473910993e-07,
111+ "support_participation": -17.82397758956648,
112+ "radiation_participation": -3.54982819398067e-11
113+ },
114+ {
115+ "mode": 9,
116+ "frequency_hz": 24995.07722130597,
117+ "q_total": 190.1396356296486,
118+ "hit_participation": -2.926971533901175e-07,
119+ "support_participation": 8.37354246425821e-07,
120+ "radiation_participation": -2.2663244544918025e-15
121+ },
122+ {
123+ "mode": 10,
124+ "frequency_hz": 26965.169996081924,
125+ "q_total": 183.99220610636925,
126+ "hit_participation": 1.8974319919597864e-07,
127+ "support_participation": -1.59078243470191e-06,
128+ "radiation_participation": 2.417011903121935e-15
129+ },
130+ {
131+ "mode": 11,
132+ "frequency_hz": 33477.73621021154,
133+ "q_total": 167.38380491323127,
134+ "hit_participation": 4.600119166074213e-09,
135+ "support_participation": -1.8810414541936487,
136+ "radiation_participation": -1.872514933365897e-12
137+ },
138+ {
139+ "mode": 12,
140+ "frequency_hz": 33477.73627769843,
141+ "q_total": 167.24987805941555,
142+ "hit_participation": 2.80533172219444e-07,
143+ "support_participation": -36.677794201479905,
144+ "radiation_participation": -3.6560501737062205e-11
145+ }
146+ ]
147+}
packages/core/test/fixtures/fem_center_16_wav.jsonadded+1−0View file
@@ -0,0 +1 @@
1+{"fs": 96000, "samples": [-2e-07, -0.891251, 0.7403112, 0.654105, -0.5192794, -0.1930555, 0.3148936, 0.0705885, 0.2102868, 0.3470793, -0.0302959, -0.369342, -0.4402394, 0.0463879, 0.4037874, 0.1005187, -0.4473388, -0.452787, 0.1458525, 0.5847734, 0.3092544, -0.2804439, -0.3913064, 0.0755943, 0.4103473, 0.0978627, -0.4555655, -0.4776171, 0.1016314, 0.5430251, 0.299544, -0.2521437, -0.3482926, 0.1075543, 0.4270763, 0.1088834, -0.4512289, -0.4948055, 0.0577954, 0.4939906, 0.2769327, -0.2379365, -0.3153266, 0.1371644, 0.4496427, 0.131502, -0.4334574, -0.500847, 0.0192831, 0.442373, 0.2443033, -0.2377404, -0.2951627, 0.1597706, 0.4730889, 0.1621524, -0.4032218, -0.4938061, -0.0097311, 0.3931745, 0.2057953, -0.2496854, -0.2888449, 0.1717712, 0.4924937, 0.1962797, -0.3631682, -0.4735385, -0.0263718, 0.3510294, 0.1662367, -0.270395, -0.2956046, 0.1710663, 0.5036189, 0.2289611, -0.3172336, -0.4416737, -0.0294164, 0.3196021, 0.1304935, -0.2954593, -0.3129982, 0.1573036, 0.503451, 0.2555622, -0.2701006, -0.4013638, -0.0194238, 0.3011209, 0.1028298, -0.3200348, -0.3372609, 0.131888, 0.4905731, 0.2723406, -0.2265637, -0.3568377, 0.0013739, 0.2961141, 0.0863588, -0.3394887, -0.3638288, 0.0977608, 0.4653167, 0.2769214, -0.1908957, -0.3128225, 0.0293968, 0.3033717, 0.0826566, -0.3500026, -0.3879544, 0.05898, 0.4296828, 0.2685825, -0.1662938, -0.2739115, 0.0602148, 0.3201398, 0.0915871, -0.3490569, -0.4053348, 0.0201626, 0.3870424, 0.2483161, -0.1544795, -0.2439608, 0.0891472, 0.3425126, 0.1113539, -0.3357357, -0.412669, -0.0141358, 0.3416646, 0.2186644, -0.1554996, -0.2255954, 0.1118828, 0.3659693, 0.138771, -0.3108132, -0.4080725, -0.0400209, 0.2981344, 0.1833543, -0.1677524, -0.2198888, 0.1250394, 0.3859793, 0.1697099, -0.2766168, -0.3912951, -0.0547595, 0.2607427, 0.1467823, -0.1882308, -0.2262527, 0.1265875, 0.3985985, 0.1996632, -0.2366877, -0.3637205, -0.0571162, 0.2329266, 0.113424, -0.2129456, -0.2425472, 0.1160905, 0.4009741, 0.224345, -0.1952881, -0.3281498, -0.0474865, 0.2168327, 0.0872423, -0.2374689, -0.2653944, 0.0947279, 0.3916982, 0.2402515, -0.1568254, -0.288405, -0.0278138, 0.2130595, 0.0711768, -0.257525, -0.2906462, 0.065107, 0.3709623, 0.2451062, -0.1252679, -0.2488036, -0.0013022, 0.2206089, 0.0667784, -0.2695477, -0.3139457, 0.0308912, 0.3404994, 0.2381352, -0.1036304, -0.2135822, 0.0280283, 0.2370485, 0.0740331, -0.2711348, -0.3313034, -0.0037055, 0.3033234, 0.2201382, -0.0935958, -0.1863379, 0.0558875, 0.2588605, 0.0913957, -0.2613391, -0.3396156, -0.0344845, 0.2633053, 0.1933525, -0.0953186, -0.1695683, 0.0782846, 0.281925, 0.1160243, -0.2407638, -0.3370545, -0.0578164, 0.2246458, 0.1611293, -0.1074349, -0.1643635, 0.0920469, 0.302074, 0.1441803, -0.2114504, -0.3232825, -0.0711081, 0.1913141, 0.1274705, -0.1272705, -0.170291, 0.0952193, 0.3156395, 0.171739, -0.1765799, -0.2994664, -0.0731227, 0.166531, 0.0964894, -0.1512194, -0.1854832, 0.0872969, 0.3199256, 0.1947406, -0.1400296, -0.2680916, -0.0641166, 0.1523574, 0.0718665, -0.1752366, -0.2069123, 0.0692602, 0.3135425, 0.209908, -0.105848, -0.2326087, -0.0457749, 0.1494469, 0.0563738, -0.195378, -0.2308113, 0.0434172, 0.2965626, 0.2150649, -0.0777173, -0.1969594, -0.0209633, 0.1569865, 0.0515292, -0.2083162, -0.2531841, 0.0130707, 0.2704803, 0.2094, -0.058476, -0.1650478, 0.0066737, 0.1728326, 0.0574215, -0.2117654, -0.2703352, -0.0179355, 0.2379865, 0.1935451, -0.0497615, -0.1402269, 0.0332065, 0.1938195, 0.0727286, -0.2047597, -0.27935, -0.0457351, 0.2025885, 0.1694645, -0.051818, -0.1248676, 0.0549432, 0.216196, 0.0949195, -0.1877535, -0.2784623, -0.0669493, 0.1681315, 0.1401687, -0.0634909, -0.1200663, 0.068911, 0.2361296, 0.1206117, -0.162532, -0.2672638, -0.0791217, 0.1382853, 0.109298, -0.0824063, -0.125525, 0.0732315, 0.2502125, 0.1460337, -0.1319497, -0.2467295, -0.0810244, 0.1160637, 0.0806301, -0.1053076, -0.1396192, 0.0673459, 0.2559002, 0.1675295, -0.0995305, -0.2190609, -0.0727941, 0.1034408, 0.0575805, -0.1285014, -0.1596382, 0.0520604, 0.2518288, 0.1820375, -0.0689907, -0.187371, -0.0558875, 0.1011111, 0.0427598, -0.1483519, -0.1821633, 0.0294127, 0.2379698, 0.187482, -0.0437445, -0.1552556, -0.0328631, 0.1084231, 0.0376467, -0.1617576, -0.2035321, 0.0023788, 0.215606, 0.1830262, -0.0264604, -0.1263102, -0.0070237, 0.1234925, 0.0424163, -0.1665473, -0.2203241, -0.0255387, 0.1871362, 0.1691569, -0.0187253, -0.1036571, 0.0180339, 0.143473, 0.055944, -0.1617465, -0.2298055, -0.0507821, 0.1557355, 0.1475939, -0.0208561, -0.0895432, 0.038897, 0.1649513, 0.0759791, -0.147681, -0.2302758, -0.0702083, 0.1249216, 0.1210388, -0.0318834, -0.0850616, 0.052783, 0.1844063, 0.0994641, -0.1259074, -0.2212698, -0.0814955, 0.0980842, 0.0928002, -0.0497031, -0.0900297, 0.0578921, 0.1986741, 0.1229531, -0.0989833, -0.2035942, -0.0834318, 0.0780408, 0.0663457, -0.0713769, -0.1030398, 0.0536237, 0.205357, 0.1430731, -0.0701093, -0.1791952, -0.0760529, 0.0666773, 0.0448425, -0.0935348, -0.1216681, 0.0406313, 0.2031226, 0.1569669, -0.0426939, -0.1508781, -0.060612, 0.0647177, 0.0307459, -0.1128276, -0.1428151, 0.0207106, 0.1918577, 0.1626589, -0.0199003, -0.1219212, -0.0393913, 0.0716512, 0.0254908, -0.1263659, -0.1631258, -0.0034609, 0.1726576, 0.1592974, -0.0042347, -0.095634, -0.0153807, 0.085825, 0.0293236, -0.1320917, -0.1794363, -0.0286928, 0.1476573, 0.1472433, 0.0027714, -0.0749211, 0.008129, 0.1046832, 0.0412941, -0.1290304, -0.1891864, -0.0517141, 0.1197291, 0.1279982, 0.000742, -0.0619083, 0.0279859, 0.125122, 0.0594044, -0.1173971, -0.1907426, -0.069604, 0.0920882, 0.1039844, -0.0095156, -0.0576772, 0.0415878, 0.1439083, 0.0808932, -0.0985444, -0.1835964, -0.0801712, 0.0678637, 0.0782063, -0.0261406, -0.062144, 0.0472144, 0.1581073, 0.1026145, -0.0747612, -0.1684065, -0.0822265, 0.0496869, 0.0538449, -0.046484, -0.0740941, 0.0442348, 0.1654626, 0.1214588, -0.0489514, -0.1468893, -0.0757203, 0.0393549, 0.0338347, -0.0674732, -0.0913633, 0.0331684, 0.1646784, 0.1347622, -0.0242395, -0.1215712, -0.0617259, 0.0376111, 0.0204821, -0.0860244, -0.1111431, 0.0155938, 0.1555705, 0.1406483, -0.0035539, -0.0954383, -0.0422754, 0.044067, 0.0151746, -0.0994492, -0.1303628, -0.0060821, 0.1390687, 0.1382588, 0.0107554, -0.0715348, -0.0200741, 0.0572767, 0.018217, -0.1057989, -0.1460979, -0.0289561, 0.1170703, 0.1278465, 0.0172139, -0.0525611, 0.0018699, 0.0749477, 0.0288128, -0.1041064, -0.1559526, -0.0500223, 0.0921706, 0.1107187, 0.0153997, -0.0405242, 0.0206467, 0.0942595, 0.0451915, -0.0944926, -0.1583634, -0.0665693, 0.0673074, 0.089044, 0.0059774, -0.036489, 0.0338258, 0.1122455, 0.064862, -0.078128, -0.1527879, -0.0765318, 0.0453662, 0.0655491, -0.0094101, -0.040455, 0.0397663, 0.1261856, 0.0849537, -0.0570581, -0.1397565, -0.0787491, 0.0288019, 0.0431501, -0.0283773, -0.0513784, 0.0378166, 0.1339587, 0.1026024, -0.0339158, -0.1207792, -0.0731003, 0.0193241, 0.0245636, -0.0481244, -0.0673301, 0.0283798, 0.1343098, 0.1153268, -0.0115627, -0.0981281, -0.0604991, 0.0176861, 0.0119535, -0.0658146, -0.0857679, 0.0128392, 0.1269969, 0.1213479, 0.0072923, -0.074522, -0.0427525, 0.0236055, 0.0066575, -0.0789476, -0.1038823, -0.0066435, 0.1128026, 0.1198089, 0.0204477, -0.0527593, -0.022306, 0.0358212, 0.0090266, -0.0856818, -0.1189727, -0.0274287, 0.0934101, 0.1108693, 0.0264887, -0.0353477, -0.0019077, 0.0522792, 0.0184003, -0.0850632, -0.1287992, -0.0467562, 0.0711631, 0.0956631, 0.0249573, -0.0241804, 0.0157578, 0.0704185, 0.0332127, -0.0771324, -0.1318685, -0.0621107, 0.048743, 0.0761296, 0.0163935, -0.0202991, 0.0284226, 0.0875186, 0.0512178, -0.0628982, -0.1276149, -0.0715485, 0.0288097, 0.0547419, 0.0022452, -0.0237734, 0.0345249, 0.1010603, 0.0697987, -0.0441861, -0.1164558, -0.0739418, 0.0136509, 0.0341711, -0.0153416, -0.0337116, 0.0334004, 0.1090524, 0.086319, -0.0233812, -0.0997172, -0.0691109, 0.0048894, 0.0169303, -0.0338179, -0.0483965, 0.0253511, 0.1102835, 0.0984695, -0.0031027, -0.07944, -0.0578282, 0.0032817, 0.0050477, -0.0505753, -0.0655285, 0.0115861, 0.1044645, 0.1045669, 0.0141462, -0.0580977, -0.0416982, 0.0086362, -0.0001917, -0.0632908, -0.0825398, -0.0059559, 0.0922488, 0.1037618, 0.0263051, -0.0382606, -0.0229292, 0.0198569, 0.0016004, -0.0702268, -0.0969374, -0.0248765, 0.0751267, 0.0961338, 0.0320196, -0.0222561, -0.0040307, 0.0351057, 0.009882, -0.070447, -0.1066299, -0.0426426, 0.0552121, 0.0826631, 0.0308054, -0.0118674, 0.0125227, 0.0520591, 0.0232713, -0.0639237, -0.1101939, -0.0569233, 0.0349516, 0.0650816, 0.0230937, -0.008109, 0.0246166, 0.0682236, 0.0397457, -0.0515229, -0.107048, -0.0658915, 0.0167947, 0.0456306, 0.0101571, -0.011109, 0.0307673, 0.081268, 0.0569209, -0.034871, -0.0975124, -0.0684558, 0.0028719, 0.0267552, -0.0060751, -0.0201098, 0.0303048, 0.0893251, 0.0723715, -0.016127, -0.0827475, -0.0643882, -0.0052798, 0.0107789, -0.0232841, -0.0335853, 0.0234439, 0.0912292, 0.0839511, 0.0023139, -0.0645853, -0.0543369, -0.0069066, -0.0004003, -0.0390732, -0.0494576, 0.0112381, 0.0866541, 0.0900692, 0.0181396, -0.0452738, -0.0397238, -0.0021225, -0.0055505, -0.0512862, -0.0653807, -0.0045758, 0.0761393, 0.0898889, 0.0294246, -0.0271724, -0.0225413, 0.0081235, -0.0042557, -0.0582851, -0.0790558, -0.0218207, 0.0610018, 0.0834229, 0.0348754, -0.0124398, -0.0050795, 0.0221887, 0.0030466, -0.0591562, -0.0885313, -0.0381738, 0.0431465, 0.0715135, 0.033989, -0.0027525, 0.0103822, 0.0379665, 0.0151397, -0.0538145, -0.0924537, -0.051476, 0.0248035, 0.0557058, 0.0271019, 0.0009048, 0.0218741, 0.0531735, 0.0302038, -0.0429987, -0.0902327, -0.0600128, 0.0082277, 0.0380293, 0.0153301, -0.0016439, 0.0279894, 0.0656535, 0.046068, -0.0281562, -0.082104, -0.0627309, -0.0045991, 0.0207211, 0.0004057, -0.0097595, 0.0280582, 0.0736578, 0.0605028, -0.0112393, -0.0690805, -0.0593637, -0.0122228, 0.0059273, -0.0155627, -0.0220882, 0.0222204, 0.0760677, 0.0715146, 0.0055628, -0.0528045, -0.0504503, -0.0138985, -0.004577, -0.0303748, -0.0367537, 0.0113908, 0.0725283, 0.0776029, 0.0201166, -0.0353177, -0.0372484, -0.0096738, -0.0096148, -0.0420303, -0.0516148, -0.0028771, 0.0634794, 0.0779481, 0.030624, -0.0187845, -0.0215542, -0.000367, -0.0087492, -0.0489882, -0.0645523, -0.018608, 0.0500823, 0.0725058, 0.0358532, -0.0052049, -0.0054555, 0.0125556, -0.0023271, -0.05036, -0.0737478, -0.0336735, 0.0340517, 0.0619971, 0.0352886, 0.003846, 0.008949, 0.0271862, 0.0085831, -0.046013, -0.0779162, -0.0460759, 0.017419, 0.0477974, 0.0291849, 0.0074167, 0.0198263, 0.0414347, 0.0223459, -0.0365734, -0.0764639, -0.0542075, 0.0022582, 0.0317433, 0.0185179, 0.0052919, 0.0258447, 0.0533081, 0.0369864, -0.0233272, -0.0695541, -0.0570552, -0.0095884, 0.01588, 0.0048411, -0.0019938, 0.0263393, 0.0611708, 0.0504561, -0.0080384, -0.0580683, -0.0543222, -0.0167476, 0.0021889, -0.0099295, -0.0132414, 0.021386, 0.0639517, 0.0609046, 0.0072938, -0.0434765, -0.0464516, -0.0184875, -0.0076721, -0.0237751, -0.0267583, 0.011776, 0.0612744, 0.0669191, 0.0207016, -0.0276318, -0.0345538, -0.0147994, -0.0125805, -0.034842, -0.0405915, -0.0011027, 0.0534918, 0.0677003, 0.0305085, -0.0125184, -0.0202464, -0.006386, -0.0120857, -0.0416817, -0.0527891, -0.0154595, 0.0416255, 0.0631564, 0.035543, 1.22e-05, -0.0054318, 0.0054461, -0.0064553, -0.0434336, -0.0616577, -0.029346, 0.0272173, 0.0539006, 0.0352832, 0.0084822, 0.0079594, 0.0189701, 0.0033747, -0.0399263, -0.0659796, -0.0409143, 0.0121162, 0.0411562, 0.0299132, 0.0119748, 0.0182227, 0.0322746, 0.0159363, -0.0316863, -0.0651636, -0.0486593, -0.0017716, 0.0265828, 0.0202831, 0.0102435, 0.0240996, 0.0435189, 0.0294338, -0.0198542, -0.0593097, -0.0516094, -0.0127348, 0.0120498, 0.0077852, 0.0037309, 0.0249343, 0.0511742, 0.0419871, -0.0060222, -0.0491825, -0.0494441, -0.0194787, -0.0006154, -0.0058411, -0.0065031, 0.0207483, 0.0542176, 0.0518798, 0.0079848, -0.0360963, -0.0425228, -0.0212873, -0.0098655, -0.0187447, -0.0189331, 0.0122228, 0.0522575, 0.0577827, 0.0203534, -0.0217319, -0.0318234, -0.0181055, -0.0146323, -0.0292089, -0.0317787, 0.0005964, 0.0455712, 0.0589202, 0.0295216, -0.0079068, -0.0188018, -0.010533, -0.0144572, -0.0358738, -0.0432435, -0.0125095, 0.0350548, 0.0551615, 0.0343777, 0.0036663, -0.0051899, 0.0002675, -0.0095391, -0.0379092, -0.0517521, -0.0253115, 0.0220935, 0.0470245, 0.034399, 0.0116024, 0.0072374, 0.0127344, -0.0006956, -0.035112, -0.0561527, -0.0361025, 0.0083691, 0.0355951, 0.029708, 0.0150211, 0.0168966, 0.0251207, 0.0107571, -0.0279211, -0.05586, -0.0434747, -0.0043679, 0.022372, 0.0210435, 0.0136511, 0.0226002, 0.0357279, 0.023188, -0.0173461, -0.0509209, -0.0464987, -0.0145293, 0.0090621, 0.0096512, 0.0078546, 0.023705, 0.0431281, 0.0348718, -0.0048229, -0.0419951, -0.0448366, -0.0208963, -0.0026499, -0.0028909, -0.0014335, 0.0201861, 0.0463473, 0.0442182, 0.0079839, -0.0302568, -0.0387745, -0.0227714, -0.0113215, -0.0148866, -0.01284, 0.0126276, 0.0449833, 0.0499785, 0.0194049, -0.0172286, -0.0291721, -0.0200604, -0.0159382, -0.0247468, -0.0247428, 0.0021329, 0.0392476, 0.0514026, 0.0279853, -0.0045747, -0.0173376, -0.0132725, -0.0160376, -0.031197, -0.0354898, -0.0098307, 0.0299248, 0.0483276, 0.0326726, 0.0061216, -0.0048471, -0.0034407, -0.0117599, -0.0334381, -0.0436167, -0.0216324, 0.0182576, 0.0411879, 0.0329469, 0.0135645, 0.0066688, 0.008024, -0.003817, -0.0312423, -0.0480354, -0.0316963, 0.0057743, 0.0309453, 0.0288785, 0.0169098, 0.0157403, 0.0195261, 0.0066128, -0.0249714, -0.0481703, -0.0387067, -0.0059181, 0.0189524, 0.0211068, 0.0158683, 0.0212483, 0.0294993, 0.0180488, -0.0155172, -0.044023, -0.0417769, -0.0153475, 0.0067661, 0.0107449, 0.0107316, 0.0225654, 0.0366118, 0.0289092, -0.0041735, -0.0361608, -0.0405569, -0.0213673, -0.004061, -0.0007766, 0.0023222, 0.0196277, 0.0399369, 0.0377216, 0.0075427, -0.0256304, -0.0352689, -0.0233014, -0.0121856, -0.0119044, -0.0081246, 0.012933, 0.0390674, 0.0433141, 0.018095, -0.0138104, -0.0266666, -0.0210235, -0.0166458, -0.0211689, -0.0191323, 0.003463, 0.0341576, 0.0449638, 0.0261304, -0.0022234, -0.0159247, -0.0149624, -0.0169798, -0.0273767, -0.0291822, -0.0074557, 0.0258924, 0.0424824, 0.0306536, 0.0076681, -0.0044761, -0.0060349, -0.0132772, -0.029761, -0.0369145, -0.018333, 0.0153853, 0.0362301, 0.0311512, 0.0146524, 0.006182, 0.0044858, -0.0061556, -0.0280749, -0.0413032, -0.0277145, 0.0040243, 0.027058, 0.027649, 0.017923, 0.0146863, 0.0151432, 0.0033312, -0.0226126, -0.0417848, -0.0343727, -0.0067167, 0.016185, 0.0206983, 0.0171773, 0.019984, 0.0244939, 0.0138401, -0.0141603, -0.0383229, -0.037463, -0.0154738, 0.0050309, 0.0112921, 0.0126452, 0.0214653, 0.0312969, 0.0239222, -0.0038823, -0.0314027, -0.036628, -0.0211704, -0.0049754, 0.0007263, 0.0050492, 0.0190345, 0.0346726, 0.0322151, 0.0068398, -0.021956, -0.0320348, -0.0231531, -0.0125835, -0.0095774, -0.0045012, 0.0131127, 0.0342122, 0.0376203, 0.0165929, -0.0112299, -0.0243417, -0.0212694, -0.0168829, -0.0182613, -0.014663, 0.0045717, 0.0300211, 0.039443, 0.0241197, -0.0006163, -0.0146027, -0.0158777, -0.017416, -0.0242087, -0.0240411, -0.00539, 0.0226945, 0.0374749, 0.0284808, 0.0085346, -0.0041194, -0.0077896, -0.0142289, -0.026686, -0.0313734, -0.0154115, 0.0132295, 0.0320116, 0.0291718, 0.0150907, 0.0057342, 0.0018463, -0.0078555, -0.0254318, -0.0356938, -0.0241522, 0.0028858, 0.0238042, 0.0261811, 0.0182837, 0.0136948, 0.0117018, 0.0007628, -0.0206815, -0.0364535, -0.0304668, -0.0069858, 0.0139505, 0.0199815, 0.0178018, 0.018773, 0.0204476, 0.0104092, -0.0131265, -0.0335846, -0.0335535, -0.0151229, 0.0037438, 0.0114582, 0.0138215, 0.0203785, 0.0269282, 0.0197572, -0.0038134, -0.0274992, -0.0330513, -0.0205156, -0.005501, 0.0017835, 0.0069754, 0.0183895, 0.0303108, 0.0275468, 0.006, -0.0190257, -0.0290791, -0.0225346, -0.0126218, -0.0077422, -0.0017407, 0.0131598, 0.0301876, 0.03275, 0.0150157, -0.0092913, -0.0222107, -0.0210068, -0.0167577, -0.0158651, -0.0111063, 0.0054619, 0.0266221, 0.0347006, 0.022066, 0.0004325, -0.0133902, -0.0162283, -0.0174581, -0.0215415, -0.0198406, -0.0036224, 0.0201302, 0.033175, 0.0262651, 0.0089, -0.0037993, -0.0089163, -0.0147324, -0.0240713, -0.0267733, -0.0128511, 0.0116034, 0.0284123, 0.0271206, 0.0150553, 0.005302, -0.0001057, -0.0090398, -0.0231829, -0.030996, -0.0209889, 0.0021836, 0.0210739, 0.0245891, 0.0181669, 0.0127437, 0.0089926, -0.0012199, -0.0190603, -0.0319761, -0.0269693, -0.0068918, 0.0121474, 0.0190741, 0.017918, 0.0175976, 0.0171554, 0.0076252, -0.01231, -0.0296196, -0.0300313, -0.014455, 0.0028104, 0.0113632, 0.014439, 0.0192938, 0.0233079, 0.0162818, -0.0038723, -0.0242745, -0.0298139, -0.0195599, -0.0057285, 0.0025165, 0.0082823, 0.0176899, 0.0266624, 0.0235863, 0.0051094, -0.0166757, -0.0263952, -0.0216024, -0.0123893, -0.0062776, 0.0003402, 0.01308, 0.0268158, 0.0285769, 0.013443, -0.0078412, -0.0202732, -0.0203931, -0.0163604, -0.0138621, -0.0082784, 0.0061476, 0.0237956, 0.0306169, 0.020045, 0.0010682, -0.0122925, -0.0161736, -0.0171996, -0.0192628, -0.016399, -0.0021318, 0.0180469, 0.0294714, 0.0240818, 0.0089045, -0.0035251, -0.0095763, -0.0148857, -0.0218126, -0.0229373, -0.0106257, 0.0103655, 0.0253302, 0.0250741, 0.0146829, 0.0048743, -0.001533, -0.0098115, -0.0212333, -0.0270397, -0.0181967, 0.0017862, 0.0187742, 0.0229527, 0.017709, 0.0118231, 0.0068533, -0.0027252, -0.0176648, -0.0281915, -0.0238518, -0.0065582, 0.010691, 0.0180594, 0.017664, 0.0164508, 0.0144579, 0.0053765, -0.0116373, -0.0262773, -0.0268708, -0.0135885, 0.0021522, 0.011094, 0.0146387, 0.0182098, 0.0202819, 0.0133836, -0.0039943, -0.0215888, -0.0268954, -0.0184195, -0.0057321, 0.0030143, 0.0091137, 0.016941, 0.0235813, 0.020222, 0.0042249, -0.0147768, -0.0239683, -0.0204725, -0.0119593, -0.0050943, 0.0018879, 0.0128862, 0.02396, 0.0249933, 0.0119263, -0.0067598, -0.0185206, -0.0195462, -0.015765, -0.0121652, -0.0060325, 0.006649, 0.0214151, 0.0270901, 0.0181059, 0.0014036, -0.0113065, -0.0158342, -0.0167178, -0.0172894, -0.0135707, -0.000892, 0.0163287, 0.0262699, 0.0219797, 0.0086553, -0.003298, -0.0098931, -0.0147704, -0.0198331, -0.0197228, -0.0087049, 0.0094104, 0.0226795, 0.0230833, 0.0140787, 0.0044477, -0.0025609, -0.0102574, -0.0195147, -0.0236881, -0.0157428, 0.001597, 0.0168276, 0.0213262, 0.0170155, 0.0109293, 0.0051588, -0.0038438, -0.0164353, -0.0249705, -0.0210824, -0.0060755, 0.0095115, 0.0169958, 0.0171472, 0.015332, 0.0122318, 0.0035691, -0.0110573, -0.023438, -0.024043, -0.0126101, 0.0017051, 0.0107129, 0.0145309, 0.0171303, 0.0177307, 0.0109673, -0.0041368, -0.0193318, -0.0242709, -0.0171793, -0.0055724, 0.003342, 0.009583, 0.0161523, 0.0209541, 0.0173597, 0.0033827, -0.0132279, -0.0217794, -0.0192305, -0.011391, -0.0041264, 0.0030184, 0.0125945, 0.0215148, 0.021908, 0.0104978, -0.0059539, -0.0169398, -0.0185534, -0.0150315, -0.0107093, -0.0042509, 0.0069885, 0.019384, 0.0240343, 0.0162785, 0.001525, -0.0104247, -0.0153005, -0.0160753, -0.0155598, -0.0112383, 0.0001249, 0.0148884, 0.023491, 0.0199889, 0.0082352, -0.0031146, -0.0099602, -0.0144533, -0.0180765, -0.0170153, -0.0070566, 0.0086596, 0.0203881, 0.0211806, 0.0133235, 0.0040233, -0.0032851, -0.0104485, -0.0179771, -0.0208316, -0.0135934, 0.0015446, 0.01517, 0.0197455, 0.016167, 0.0100635, 0.0038118, -0.0046513, -0.0153296, -0.0222104, -0.0186278, -0.0055093, 0.0085512, 0.0159235, 0.0164504, 0.0142438, 0.010381, 0.0021244, -0.010536, -0.0210062, -0.021517, -0.0115818, 0.0014168, 0.0102639, 0.0142013, 0.0160617, 0.0155615, 0.0089527, -0.0042725, -0.017417, -0.0219142, -0.0159003, -0.005298, 0.0035465, 0.0097797, 0.0153356, 0.0186931, 0.0149198, 0.0026045, -0.0119505, -0.0198077, -0.0179381, -0.0107316, -0.0033253, 0.0038238, 0.0122224, 0.0193989, 0.0192442, 0.0091753, -0.0053521, -0.0155154, -0.0174793, -0.0142082, -0.0094463, -0.0028399, 0.0071893, 0.017629, 0.021377, 0.0145792, 0.0014985, -0.0096369, -0.0146402, -0.0153227, -0.0140282, -0.0093085, 0.000947, 0.0136607, 0.0210686, 0.0181263, 0.0077063, -0.0029688, -0.0098485, -0.0139886, -0.0165013, -0.0147232, -0.0056494, 0.008055, 0.0183967, 0.0193849, 0.0124779, 0.0036044, -0.0037792, -0.0104435, -0.0165851, -0.0183821, -0.0117158, 0.0015777, 0.0137484, 0.0182332, 0.0152248, 0.0092283, 0.0027378, -0.00521, -0.0143188, -0.0198284, -0.0164554, -0.0049061, 0.0077639, 0.0148691, 0.0156367, 0.0131904, 0.0088308, 0.0009769, -0.0100507, -0.0189065, -0.0192628, -0.0105475, 0.0012461, 0.0097778, 0.0137165, 0.0150113, 0.0137019, 0.0072727, -0.0043851, -0.0157757, -0.0197994, -0.0146257, -0.0049473, 0.003662, 0.0097735, 0.0145028, 0.0167302, 0.0128355, 0.0019015, -0.0108833, -0.018032, -0.0166397, -0.0100183, -0.002655, 0.0043769, 0.0117869, 0.01755, 0.0169373, 0.0079669, -0.0049, -0.0142318, -0.0163704, -0.0133328, -0.0083401, -0.0017247, 0.0072737, 0.0160941, 0.0190575, 0.0130148, 0.0013739, -0.0089325, -0.0139031, -0.0145003, -0.0126604, -0.0077056, 0.0016009, 0.0125963, 0.0189471, 0.0163993, 0.0071151, -0.002854, -0.009611, -0.0134201, -0.015077, -0.0127726, -0.0044539, 0.0075542, 0.0166559, 0.017706, 0.0115871, 0.0031954, -0.0040993, -0.0102897, -0.0153127, -0.016269, -0.0100787, 0.0016594, 0.0125201, 0.0168026, 0.0142349, 0.0084271, 0.0018787, -0.0055715, -0.0133829, -0.0177587, -0.0145346, -0.0042986, 0.0071118, 0.01385, 0.0147542, 0.0121764, 0.0075235, 7.22e-05, -0.0095874, -0.0170785, -0.0172519, -0.0095372, 0.0011599, 0.009276, 0.0131272, 0.0139865, 0.0120957, 0.0058714, -0.0044661, -0.0143545, -0.0179009, -0.0133848, -0.0045501, 0.003713, 0.0096186, 0.0136651, 0.0150121, 0.0110508, 0.001278, -0.0099795, -0.0164318, -0.0153658, -0.0092798, -0.002089, 0.0047352, 0.0113041, 0.0159197, 0.0149331, 0.006874, -0.0045557, -0.0130738, -0.01526, -0.0124348, -0.007364, -0.0008459, 0.0072621, 0.0147368, 0.0170254, 0.0115857, 0.0011882, -0.0083008, -0.0131259, -0.0136395, -0.0114307, -0.0063696, 0.0021111, 0.0116588, 0.0170807, 0.014809, 0.0064962, -0.0027634, -0.009288, -0.0127825, -0.0137807, -0.011104, -0.0034424, 0.0071267, 0.0151253, 0.0161473, 0.0106847, 0.0028005, -0.0042881, -0.0100257, -0.0141416, -0.0144357, -0.0086533, 0.0017642, 0.0114498, 0.0154601, 0.013231, 0.0076636, 0.0011901, -0.0057776, -0.0125082, -0.0159487, -0.0128368, -0.0037086, 0.0065655, 0.0128766, 0.0138388, 0.011206, 0.0064137, -0.0006343, -0.009138, -0.0154749, -0.0154575, -0.008571, 0.0011326, 0.0087729, 0.0124725, 0.0129937, 0.0106986, 0.0047022, -0.0045118, -0.0131116, -0.0161961, -0.0121975, -0.0041294, 0.0037173, 0.0093573, 0.0128324, 0.0134975, 0.0095191, 0.0007337, -0.0092025, -0.0149883, -0.0141375, -0.008538, -0.0016075, 0.0049438, 0.010788, 0.0144705, 0.0131862, 0.0058936, -0.0042884, -0.0120269, -0.0141714, -0.0115364, -0.0064974, -0.000156, 0.0071728, 0.0135244, 0.015238, 0.0102881, 0.0009685, -0.007732, -0.0123348, -0.0127647, -0.0103198, -0.0052522, 0.0024997, 0.0108212, 0.015431, 0.0133523, 0.0058742, -0.0026907, -0.008909, -0.012103, -0.0125954, -0.0096697, -0.0025903, 0.0067506, 0.0137714, 0.0147076, 0.0097945, 0.0024234, -0.0043784, -0.0096823, -0.0130581, -0.0128362, -0.0074136, 0.0018744, 0.0105095, 0.0142081, 0.0122379, 0.0069404, 0.0006371, -0.0058624, -0.0116856, -0.0143559, -0.0113363, -0.0031506, 0.0061011, 0.0119549, 0.0129168, 0.0102826, 0.0054661, -0.0011798, -0.0086976, -0.0140579, -0.0138556, -0.0076613, 0.0011444, 0.0082778, 0.0117815, 0.0120386, 0.0094758, 0.0037265, -0.0045222, -0.012014, -0.0146637, -0.0110757, -0.0037026, 0.0036874, 0.009022, 0.0120134, 0.0121537, 0.0082012, 0.0002652, -0.0085247, -0.0136844, -0.0129678, -0.007809, -0.0011956, 0.0050381, 0.0102509, 0.0131732, 0.0116589, 0.0050201, -0.0040747, -0.0110784, -0.0131201, -0.0106543, -0.0057244, 0.0003827, 0.0070223, 0.0124325, 0.0136603, 0.0091153, 0.0007346, -0.0072173, -0.0115486, -0.0118943, -0.0093123, -0.0043147, 0.0027863, 0.0100636, 0.0139666, 0.0120232, 0.0052663, -0.0026306, -0.0084962, -0.0114027, -0.0115079, -0.0084312, -0.0018753, 0.0064105, 0.0125672, 0.0133832, 0.0089329, 0.0020673, -0.0043952, -0.0092838, -0.0120523, -0.0114337, -0.0063363, 0.001979, 0.0096763, 0.0130456, 0.0112727, 0.0062598, 0.0001925, -0.0058537, -0.0109091, -0.0129465, -0.0100099, -0.0026329, 0.0057004, 0.0110872, 0.0120075, 0.009409, 0.0046526, -0.0015945, -0.0082647, -0.0127972, -0.0124247, -0.006815, 0.0011803, 0.0077969, 0.0110761, 0.0111252, 0.0084, 0.0029123, -0.0044991, -0.0110362, -0.0132847, -0.0100265, -0.0032822, 0.0036324, 0.0086375, 0.0112151, 0.0109551, 0.0070647, -0.0001326, -0.007925, -0.0125045, -0.0118647, -0.0071046, -0.000842, 0.005046, 0.0097033, 0.0120052, 0.0103195, 0.0042465, -0.0038975, -0.0102165, -0.0121162, -0.0098008, -0.005033, 0.0008001, 0.0068243, 0.0114418, 0.0122626, 0.0080595, 0.0005002, -0.0067488, -0.0107803, -0.011042, -0.0083964, -0.0035254, 0.0029879, 0.0093712, 0.012661, 0.0108143, 0.0046844, -0.0025786, -0.0080656, -0.0106981, -0.0105079, -0.0073571, -0.0012782, 0.0060958, 0.01149, 0.0121682, 0.0081112, 0.0017345, -0.0043573, -0.0088496, -0.0111167, -0.0101984, -0.0054005, 0.0020705, 0.0089322, 0.0119696, 0.0103472, 0.0056229, -0.0001645, -0.0057738, -0.0101746, -0.0116929, -0.0088367, -0.00216, 0.0053488, 0.0102742, 0.0111245, 0.0085865, 0.0039512, -0.0019033, -0.0078386, -0.0116689, -0.0111452, -0.006035, 0.001229, 0.0073339, 0.0103724, 0.0102569, 0.0074492, 0.002233, -0.0044459, -0.0101581, -0.0120419, -0.0090523, -0.0028772, 0.0035591, 0.0082228, 0.0104431, 0.0098811, 0.0060825, -0.0004658, -0.0073873, -0.0114348, -0.0108323, -0.0064329, -0.000538, 0.0049889, 0.0091536, 0.0109485, 0.0091414, 0.0035645, -0.0037446, -0.009431, -0.011166, -0.008984, -0.0044132, 0.0011202, 0.0065905, 0.0105379, 0.0110202, 0.0071117, 0.0002749, -0.00632, -0.0100389, -0.0102178, -0.0075625, -0.0028595, 0.0031195, 0.0087328, 0.0114924, 0.0097175, 0.0041363, -0.0025308, -0.0076288, -0.0100012, -0.0095874, -0.0064222, -0.0007817, 0.0057991, 0.0105218, 0.0110562, 0.0073365, 0.0014266, -0.0042794, -0.0083947, -0.0102457, -0.0091057, -0.0045882, 0.0021452, 0.0082625, 0.010976, 0.0094693, 0.0050304, -0.0004505, -0.0056404, -0.0094796, -0.0105729, -0.0077987, -0.0017333, 0.0050355, 0.0095146, 0.010277, 0.007816, 0.0033442, -0.0021266, -0.0074203, -0.0106537, -0.0100001, -0.0053216, 0.0012826, 0.0068908, 0.0096819, 0.0094354, 0.0066056, 0.0016667, -0.0043663, -0.009364, -0.0109208, -0.0081536, -0.002494, 0.0034721, 0.0077921, 0.0097016, 0.0089154, 0.005232, -0.0007409, -0.0068996, -0.0104632, -0.0098716, -0.0057992, -0.0002769, 0.0048839, 0.0086091, 0.0099888, 0.0081027, 0.0029658, -0.003607, -0.0087132, -0.0102723, -0.0082097, -0.0038574, 0.001362, 0.0063311, 0.0097091, 0.0099126, 0.0062631, 6.48e-05, -0.0059252, -0.0093296, -0.0094285, -0.0068028, -0.0022963, 0.0031936, 0.0081406, 0.0104427, 0.008724, 0.0036265, -0.0024847, -0.0071944, -0.0093212, -0.0087395, -0.0056053, -0.0003711, 0.0055157, 0.0096473, 0.0100402, 0.0066125, 0.0011442, -0.0041722, -0.0079308, -0.0094343, -0.0081356, -0.0038832, 0.0022012, 0.0076555, 0.0100602, 0.008643, 0.004482, -0.0006784, -0.005468, -0.008822, -0.0095685, -0.0068794, -0.0013528, 0.0047519, 0.0088065, 0.0094709, 0.0070972, 0.0028174, -0.002281, -0.0070108, -0.0097363, -0.0089741, -0.0046734, 0.0013353, 0.0064687, 0.0090129, 0.0086621, 0.0058552, 0.0011948, -0.0042645, -0.0086415, -0.0099077, -0.0073285, -0.0021363, 0.0033751, 0.0073562, 0.0089937, 0.0080445, 0.0044941, -0.0009644, -0.0064527, -0.0095793, -0.0089818, -0.0052066, -5.29e-05, 0.0047441, 0.0080751, 0.0091144, 0.0071844, 0.0024426, -0.0034789, -0.0080556, -0.0094363, -0.0074812, -0.0033586, 0.0015408, 0.0060543, 0.0089467, 0.0089223, 0.005505, -0.0001264, -0.0055597, -0.0086558, -0.0086786, -0.0061104, -0.0018194, 0.003221, 0.0075885, 0.0094969, 0.0078255, 0.0031569, -0.0024379, -0.0067682, -0.0086644, -0.0079585, -0.00489, -3.39e-05, 0.0052427, 0.0088543, 0.0091127, 0.005941, 0.0008876, -0.0040441, -0.0074666, -0.0086788, -0.0072718, -0.0032716, 0.0022382, 0.007102, 0.009217, 0.0078706, 0.0039769, -0.0008588, -0.005268, -0.0082003, -0.0086645, -0.0060651, -0.0010164, 0.0044918, 0.0081475, 0.0087097, 0.0064296, 0.0023593, -0.0023798, -0.0066112, -0.0089037, -0.008054, -0.0040873, 0.0013829, 0.0060676, 0.0083708, 0.007937, 0.005186, 0.0008025, -0.0041442, -0.0079808, -0.0089914, -0.006574, -0.0018064, 0.0032707, 0.0069228, 0.0083213, 0.0072573, 0.0038527, -0.0011426, -0.0060397, -0.0087736, -0.0081609, -0.0046564, 0.0001383, 0.0045798, 0.0075562, 0.008316, 0.0063709, 0.0019869, -0.0033559, -0.0074513, -0.0086572, -0.0068002, -0.0029116, 0.0016687, 0.0057669, 0.0082434, 0.0080351, 0.0048287, -0.0002967, -0.0052196, -0.0080194, -0.0079708, -0.0054799, -0.0014151, 0.0032108, 0.0070721, 0.0086422, 0.0070138, 0.0027281, -0.0023892, -0.0063543, -0.0080354, -0.0072395, -0.0042617, 0.0002413, 0.0049784, 0.0081325, 0.008267, 0.0053219, 0.0006566, -0.0039016, -0.0070087, -0.0079757, -0.0065006, -0.0027415, 0.0022568, 0.0065944, 0.0084414, 0.0071524, 0.0035139, -0.0009997, -0.0050491, -0.0076134, -0.0078486, -0.0053432, -0.0007218, 0.0042502, 0.0075347, 0.007995, 0.0058115, 0.0019604, -0.0024344, -0.0062231, -0.008146, -0.007228, -0.0035598, 0.0014234, 0.0056875, 0.0077591, 0.0072595, 0.0045882, 0.000477, -0.0040095, -0.0073742, -0.0081613, -0.0058866, -0.0015051, 0.003161, 0.006498, 0.0076855, 0.0065446, 0.0032947, -0.0012813, -0.0056556, -0.008038, -0.0074059, -0.004149, 0.0003005, 0.0043989, 0.0070554, 0.0075858, 0.005649, 0.0015913, -0.0032355, -0.0068949, -0.0079339, -0.0061667, -0.0025111, 0.0017557, 0.0054743, 0.0075935, 0.0072382, 0.0042264, -0.0004456, -0.004902, -0.007421, -0.0073065, -0.0049061, -0.0010724, 0.0031708, 0.0065879, 0.0078678, 0.0062813, 0.0023395, -0.0023375, -0.0059556, -0.007437, -0.006578, -0.003709, 0.0004635, 0.0047218, 0.0074735, 0.0074965, 0.0047538, 0.0004504, -0.0037495, -0.0065622, -0.0073221, -0.0058105, -0.002282, 0.0022583, 0.0061269, 0.0077283, 0.0064878, 0.0030912, -0.0011078, -0.0048184, -0.0070602, -0.0071107, -0.0047029, -0.000466, 0.0040236, 0.0069655, 0.0073271, 0.0052414, 0.001613, -0.0024536, -0.0058475, -0.0074544, -0.0064859, -0.003087, 0.0014548, 0.0053277, 0.0071797, 0.0066286, 0.0040536, 0.0002078, -0.0038635, -0.0068152, -0.0074085, -0.0052619, -0.0012325, 0.0030475, 0.0060855, 0.0070868, 0.0058985, 0.0028087, -0.0013857, -0.0052966, -0.0073656, -0.0067129, -0.0036832, 0.0004373, 0.0042077, 0.006575, 0.0069171, 0.0050071, 0.0012492, -0.0031159, -0.0063816, -0.0072638, -0.0055802, -0.0021533, 0.0018097, 0.0051812, 0.0069922, 0.0065212, 0.0036907, -0.0005734, -0.0046041, -0.0068599, -0.0066857, -0.0043843, -0.0007819, 0.0031073, 0.0061333, 0.007165, 0.0056206, 0.0019895, -0.0022824, -0.0055735, -0.0068709, -0.0059699, -0.003222, 0.0006411, 0.0044724, 0.0068703, 0.0067947, 0.0042351, 0.0002679, -0.0035915, -0.0061306, -0.0067152, -0.005192, -0.0018845, 0.0022445, 0.0056945, 0.0070732, 0.0058749, 0.002707, -0.0011886, -0.0045817, -0.0065396, -0.0064421, -0.0041348, -0.0002456, 0.0038095, 0.0064368, 0.0067054, 0.0047172, 0.0013105, -0.0024449, -0.0054853, -0.0068219, -0.0058187, -0.0026647, 0.0014768, 0.0049874, 0.0066339, 0.0060432, 0.0035752, -1.38e-05, -0.0037094, -0.0062988, -0.0067252, -0.0046955, -0.0009878, 0.0029315, 0.0056882, 0.0065248, 0.0053124, 0.0023849, -0.0014608, -0.0049595, -0.0067501, -0.0060785, -0.003258, 0.0005512, 0.0040107, 0.0061166, 0.0063044, 0.0044358, 0.0009545, -0.0029966, -0.0059071, -0.0066445, -0.0050393, -0.0018338, 0.0018371, 0.0048907, 0.0064355, 0.0058752, 0.0032147, -0.0006809, -0.0043241, -0.0063353, -0.0061078, -0.0039103, -0.0005361, 0.0030258, 0.0057063, 0.0065256, 0.005025, 0.001676, -0.0022237, -0.0052092, -0.0063378, -0.0054116, -0.0027924, 0.0007808, 0.0042304, 0.006317, 0.0061558, 0.0037632, 0.0001076, -0.0034306, -0.0057163, -0.0061523, -0.004637, -0.0015405, 0.0022171, 0.0052935, 0.0064713, 0.0053116, 0.002359, -0.0012467, -0.0043429, -0.0060506, -0.0058354, -0.0036305, -5.75e-05, 0.0036058, 0.0059462, 0.0061284, 0.0042366, 0.0010473, -0.002414, -0.0051374, -0.0062425, -0.0052184, -0.0022886, 0.0014892, 0.004666, 0.0061213, 0.0055012, 0.0031471, -0.000195, -0.0035499, -0.0058209, -0.0061045, -0.0041832, -0.0007699, 0.0028139, 0.0053078, 0.0059991, 0.0047807, 0.0020156, -0.001511, -0.0046422, -0.006186, -0.0054985, -0.0028713, 0.0006449, 0.0038115, 0.005681, 0.0057426, 0.0039266, 0.0007015, -0.0028771, -0.0054678, -0.006073, -0.0045421, -0.0015495, 0.0018431, 0.0046058, 0.0059198, 0.0052923, 0.0027925, -0.0007694, -0.00406, -0.0058459, -0.0055716, -0.0034804, -0.0003285, 0.0029306, 0.0053052, 0.0059434, 0.0044886, 0.0013968, -0.0021616, -0.004863, -0.0058377, -0.0048996, -0.0024133, 0.0008886, 0.0039957, 0.0058085, 0.0055742, 0.0033351, -3.17e-05, -0.0032688, -0.0053209, -0.0056307, -0.0041385, -0.0012435, 0.002178, 0.0049205, 0.0059186, 0.0047952, 0.0020452, -0.0012854, -0.0041056, -0.0055921, -0.0052842, -0.003183, 0.0001017, 0.0034115, 0.0054908, 0.0055946, 0.0037971, 0.0008186, -0.0023661, -0.0048046, -0.0057112, -0.0046779, -0.0019546, 0.0014921, 0.0043625, 0.0056417, 0.0050006, 0.002764, -0.0003422, -0.0033873, -0.0053779, -0.0055401, -0.0037204, -0.0005771, 0.0026958, 0.0049456, 0.0055087, 0.0042981, 0.0016934, -0.0015399, -0.0043432, -0.0056685, -0.0049691, -0.0025209, 0.0007207, 0.0036129, 0.0052689, 0.0052274, 0.0034726, 0.0004853, -0.0027573, -0.0050606, -0.0055466, -0.0040865, -0.001297, 0.0018319, 0.0043284, 0.005442, 0.0047659, 0.002418, -0.0008402, -0.0038108, -0.0053902, -0.0050756, -0.0030912, -0.0001538, 0.0028255, 0.0049284, 0.0054126, 0.0040054, 0.0011493, -0.0020962, -0.0045352, -0.00537, -0.0044305, -0.0020785, 0.0009695, 0.0037687, 0.0053405, 0.0050452, 0.0029479, -0.0001515, -0.0031081, -0.0049454, -0.0051484, -0.0036902, -0.0009874, 0.0021291, 0.0045731, 0.005411, 0.0043229, 0.0017629, -0.0013081, -0.0038722, -0.005163, -0.0047832, -0.0027856, 0.000235, 0.0032254, 0.0050683, 0.0051018, 0.0033962, 0.0006204, -0.002305, -0.0044869, -0.0052236, -0.0041912, -0.0016589, 0.0014863, 0.004076, 0.0051942, 0.0045394, 0.0024213, -0.0004606, -0.0032235, -0.0049669, -0.0050267, -0.0033033, -0.0004078, 0.0025778, 0.0046021, 0.0050521, 0.0038601, 0.0014126, -0.0015513, -0.0040609, -0.0051935, -0.0044866, -0.0022047, 0.0007805, 0.003417, 0.0048801, 0.0047551, 0.0030674, 0.0003015, -0.0026377, -0.0046828, -0.0050621, -0.00367, -0.0010735, 0.001807, 0.00406, 0.0049995, 0.0042901, 0.0020866, -0.0008953, -0.0035751, -0.0049664, -0.0046178, -0.0027392, -7.3e-06, 0.0027133, 0.0045745, 0.0049281, 0.0035706, 0.0009309, -0.0020279, -0.0042254, -0.0049341, -0.0040015, -0.001783, 0.0010279, 0.0035496, 0.0049093, 0.0045638, 0.0025986, -0.0002537, -0.0029495, -0.00459, -0.0047027, -0.0032873, -0.000767, 0.002072, 0.0042491, 0.004945, 0.0038917, 0.00151, -0.0013174, -0.0036445, -0.0047619, -0.0043273, -0.0024328, 0.0003453, 0.0030472, 0.0046763, 0.0046476, 0.0030314, 0.0004492, -0.0022342, -0.0041848, -0.0047758, -0.0037527, -0.0013978, 0.0014725, 0.003806, 0.0047776, 0.0041152, 0.0021149, -0.0005544, -0.0030603, -0.0045854, -0.0045595, -0.0029278, -0.0002604, 0.0024605, 0.0042772, 0.0046282, 0.0034629, 0.0011681, -0.0015477, -0.0037943, -0.0047573, -0.004047, -0.0019201, 0.0008262, 0.0032253, 0.0045145, 0.0043223, 0.0027058, 0.0001459, -0.0025185, -0.0043319, -0.0046167, -0.0032903, -0.0008762, 0.0017713, 0.0038019, 0.0045899, 0.0038598, 0.0017936, -0.0009359, -0.003352, -0.0045727, -0.0041963, -0.0024213, 0.0001147, 0.0025966, 0.0042427, 0.0044857, 0.0031794, 0.0007391, -0.0019573, -0.0039332, -0.0045285, -0.0036094, -0.0015224, 0.0010673, 0.0033386, 0.0045118, 0.004126, 0.0022844, -0.0003395, -0.0027941, -0.0042548, -0.0042913, -0.0029248, -0.0005778, 0.0020083, 0.0039465, 0.0045173, 0.003499, 0.0012841, -0.0013154, -0.0034238, -0.0043879, -0.0039127, -0.0021199, 0.0004354, 0.0028762, 0.0043129, 0.0042298, 0.0027, 0.0003016, -0.002156, -0.0038981, -0.0043643, -0.0033576, -0.001168, 0.0014516, 0.0035516, 0.0043904, 0.003726, 0.0018414, -0.0006275, -0.0028989, -0.0042312, -0.0041342, -0.0025901, -0.0001327, 0.0023446, 0.0039711, 0.0042353, 0.0031029, 0.0009553, -0.001532, -0.0035425, -0.0043564, -0.0036473, -0.0016649, 0.0008595, 0.003039, 0.0041715, 0.0039258, 0.002383, 1.5e-05, -0.0024004, -0.0040061, -0.0042077, -0.0029447, -0.0007026, 0.0017271, 0.0035547, 0.0042109, 0.0034704, 0.0015348, -0.0009639, -0.0031408, -0.0042076, -0.0038087, -0.0021348, 0.0002155, 0.0024774, 0.0039315, 0.0040815, 0.0028276, 0.0005716, -0.0018847, -0.0036582, -0.0041519, -0.0032516, -0.0012926, 0.0010909, 0.003136, 0.004145, 0.0037279, 0.0020021, -0.0004106, -0.0026425, -0.0039397, -0.0039122, -0.0025988, -0.0004159, 0.0019395, 0.0036639, 0.0041246, 0.0031416, 0.0010831, -0.0013039, -0.0032113, -0.0040393, -0.0035353, -0.0018421, 0.0005077, 0.002712, 0.0039759, 0.0038459, 0.0023996, 0.0001752, -0.0020729, -0.0036269, -0.0039862, -0.0030016, -0.0009661, 0.0014243, 0.0033119, 0.0040312, 0.0033693, 0.0015973, -0.000683, -0.0027405, -0.0039022, -0.0037467, -0.0022869, -2.31e-05, 0.0022304, 0.0036833, 0.0038718, 0.0027766, 0.0007706, -0.0015063, -0.003305, -0.0039879, -0.003284, -0.0014366, 0.000882, 0.002859, 0.0038505, 0.0035628, 0.0020949, -9.41e-05, -0.0022836, -0.0037034, -0.0038325, -0.0026308, -0.0005505, 0.0016763, 0.0033188, 0.0038604, 0.0031183, 0.0013067, -0.0009809, -0.002941, -0.0038692, -0.0034533, -0.0018771, 0.0002981, 0.0023574, 0.0036403, 0.0037121, 0.0025116, 0.0004258, -0.0018107, -0.0033995, -0.0038031, -0.0029254, -0.0010902, 0.0011014, 0.0029418, 0.0038066, 0.003366, 0.0017493, -0.0004685, -0.0024955, -0.0036441, -0.0035632, -0.0023056, -0.0002778, 0.0018669, 0.0033996, 0.0037644, 0.0028172, 0.0009048, -0.0012847, -0.0030073, -0.003715, -0.0031919, -0.001596, 0.0005645, 0.0025547, 0.0036634, 0.0034939, 0.0021279, 6.74e-05, -0.0019864, -0.0033708, -0.003639, -0.0026808, -0.0007893, 0.0013918, 0.0030863, 0.0036983, 0.0030431, 0.00138, -0.0007235, -0.0025859, -0.0035968, -0.003394, -0.002015, 7.02e-05, 0.0021185, 0.0034131, 0.003536, 0.0024812, 0.0006102, -0.0014727, -0.0030808, -0.0036492, -0.002954, -0.0012329, 0.0008951, 0.0026857, 0.0035505, 0.0032306, 0.0018377, -0.0001844, -0.0021685, -0.003422, -0.0034883, -0.0023464, -0.0004177, 0.0016205, 0.0030946, 0.0035365, 0.0027995, 0.0011057, -0.0009881, -0.0027518, -0.0035558, -0.0031276, -0.0016454, 0.000365, 0.0022377, 0.0033678, 0.0033743, 0.0022277, 0.0002997, -0.0017358, -0.0031568, -0.0034804, -0.0026284, -0.0009122, 0.0011009, 0.0027564, 0.0034943, 0.0030372, 0.0015233, -0.0005145, -0.0023533, -0.0033673, -0.0032424, -0.0020423, -0.0001606, 0.0017916, 0.0031527, 0.0034339, 0.0025227, 0.0007472, -0.0012591, -0.0028125, -0.0034138, -0.0028796, -0.0013778, 0.0006078, 0.002404, 0.0033739, 0.0031712, 0.0018827, -2.41e-05, -0.0018981, -0.0031295, -0.00332, -0.0023918, -0.0006348, 0.0013546, 0.002874, 0.0033904, 0.0027449, 0.0011866, -0.0007514, -0.0024359, -0.0033133, -0.0030727, -0.0017715, 0.0001488, 0.002009, 0.00316, 0.0032262, 0.002214, 0.0004716, -0.0014328, -0.0028695, -0.0033379, -0.0026546, -0.0010517, 0.0009, 0.0025195, 0.0032709, 0.0029267, 0.0016084, -0.0002584, -0.002056, -0.0031605, -0.003173, -0.0020888, -0.0003023, 0.0015613, 0.002882, 0.0032374, 0.0025111, 0.000929, -0.0009872, -0.0025729, -0.0032657, -0.0028296, -0.0014378, 0.0004182, 0.0021195, 0.0031132, 0.0030657, 0.0019728, 0.0001911, -0.0016606, -0.0029292, -0.0031824, -0.0023584, -0.0007559, 0.0010915, 0.0025795, 0.003206, 0.0027385, 0.0013214, -0.0005502, -0.0022162, -0.0031087, -0.0029476, -0.0018057, -6.14e-05, 0.0017146, 0.0029221, 0.0031308, 0.002256, 0.0006083, -0.0012283, -0.002627, -0.0031344, -0.0025957, -0.0011848, 0.0006394, 0.0022597, 0.0031056, 0.0028759, 0.0016615, -0.0001009, -0.0018089, -0.0029025, -0.003027, -0.0021315, -0.0005004, 0.0013137, 0.0026746, 0.0031058, 0.002473, 0.0010148, -0.0007688, -0.0022908, -0.0030502, -0.0027802, -0.0015535, 0.0002142, 0.0019025, 0.0029233, 0.0029409, 0.0019724, 0.0003519, -0.0013878, -0.0026705, -0.0030516, -0.0023831, -0.0008909, 0.0008979, 0.0023606, 0.0030106, 0.0026491, 0.001404, -0.0003182, -0.001946, -0.0029175, -0.0028841, -0.0018561, -0.0002025, 0.0014995, 0.002681, 0.0029613, 0.0022503, 0.0007739, -0.0009791, -0.0024037, -0.0029975, -0.0025573, -0.0012519, 0.0004596, 0.0020037, 0.0028756, 0.0027837, 0.0017442, 9.82e-05, -0.0015855, -0.0027161, -0.0029074, -0.0021133, -0.0006191, 0.0010748, 0.0024111, 0.0029401, 0.0024672, 0.0011417, -0.0005765, -0.0020846, -0.0028676, -0.0026772, -0.0015934, 2.18e-05, 0.0016369, 0.0027066, 0.0028529, 0.0020148, 0.0004865, -0.0011934, -0.0024509, -0.0028754, -0.0023375, -0.0010142, 0.000661, 0.0021218, 0.0028572, 0.0026059, 0.0014627, -0.000165, -0.00172, -0.0026894, -0.002758, -0.0018971, -0.0003839, 0.0012698, 0.0024872, 0.002843, 0.0022253, 0.0008627, -0.0007771, -0.0021514, -0.0028062, -0.0025139, -0.0013589, 0.000268, 0.001799, 0.002702, 0.0026784, 0.0017544, 0.0002491, -0.0013392, -0.0024831, -0.0027885, -0.0021371, -0.0007486, 0.0008898, 0.0022092, 0.0027686, 0.0023955, 0.0012219, -0.0003656, -0.001839, -0.0026917, -0.0026199, -0.0016462, -0.0001167, 0.0014364, 0.0024914, 0.0027068, 0.0020144, 0.0006381, -0.000965, -0.0022439, -0.0027497, -0.0023087, -0.0010858, 0.0004908, 0.0018908, 0.002654, 0.0025259, 0.0015391, 1.92e-05, -0.0015109, -0.0025166, -0.0026541, -0.0018909, -0.0004994, 0.0010523, 0.0022513, 0.0026946, 0.002221, 0.0009818, -0.0005949, -0.0019585, -0.0026429, -0.0024294, -0.001403, 9.12e-05, 0.001559, 0.0025054, 0.0025982, 0.0017966, 0.00038, -0.0011553, -0.0022842, -0.0026357, -0.002103, -0.0008636, 0.000674, 0.0019903, 0.0026271, 0.0023592, 0.0012841, -0.0002178, -0.0016319, -0.0024897, -0.0025113, -0.0016862, -0.0002832, 0.0012237, 0.0023112, 0.0026007, 0.002, 0.0007281, -0.000778, -0.0020175, -0.00258, -0.0022714, -0.0011852, 0.0003115, 0.0016989, 0.0024958, 0.0024372, 0.0015577, 0.0001611, -0.0012879, -0.0023071, -0.0025467, -0.0019144, -0.0006231, 0.0008765, 0.0020652, 0.0025439, 0.0021641, 0.00106, -0.0004025, -0.0017353, -0.0024821, -0.0023781, -0.001457, -4.33e-05, 0.0013722, 0.0023129, 0.0024723, 0.0018013, 0.0005195, -0.0009459, -0.002093, -0.0025208, -0.0020822, -0.0009377, 0.0005133, 0.0017812, 0.0024475, 0.0022905, 0.0013555, -4.73e-05, -0.0014371, -0.0023302, -0.0024208, -0.0016893, -0.0003951, 0.0010251, 0.0020999, 0.0024683, 0.0019975, 0.0008401, -0.0006061, -0.0018378, -0.0024339, -0.0022024, -0.0012325, 0.0001485, 0.0014817, 0.0023177, 0.0023648, 0.0015996, 0.0002872, -0.0011147, -0.0021265, -0.0024141, -0.0018901, -0.0007306, 0.0006797, 0.001865, 0.0024143, 0.002134, 0.0011239, -0.0002606, -0.0015454, -0.0023028, -0.002285, -0.0014966, -0.0001966, 0.001176, 0.0021462, 0.0023773, 0.0017951, 0.0006093, -0.0007727, -0.0018895, -0.0023706, -0.0020508, -0.0010303, 0.0003461, 0.0016022, 0.0023035, 0.0022157, 0.0013804, 8.59e-05, -0.0012349, -0.0021417, -0.0023246, -0.0017128, -0.0005127, 0.0008591, 0.0019286, 0.0023357, 0.0019532, 0.000916, -0.0004301, -0.0016351, -0.0022875, -0.0021571, -0.0012869, 1.9e-05, 0.001308, 0.0021452, 0.0022564, 0.0016086, 0.000416, -0.0009229, -0.0019506, -0.0023096, -0.0018759, -0.0008059, 0.0005285, 0.0016755, 0.0022554, 0.0020754, 0.0011913, -0.000103, -0.0013646, -0.002156, -0.0022063, -0.0015069, -0.0003045, 0.0009942, 0.0019566, 0.0022596, 0.0017949, 0.0007144, -0.0006112, -0.0017227, -0.0022398, -0.0019948, -0.0010798, 0.0001954, 0.0014054, 0.0021425, 0.0021509, 0.0014219, 0.0002068, -0.0010723, -0.0019778, -0.0022094, -0.0016968, -0.0006137, 0.0006794, 0.0017458, 0.0022175, 0.0019286, 0.0009806, -0.0002949, -0.0014609, -0.002128, -0.0020776, -0.0013262, -0.0001223, 0.0011271, 0.0019916, 0.0021716, 0.0016091, 0.0005047, -0.0007623, -0.0017675, -0.0021766, -0.0018501, -0.0008926, 0.0003725, 0.0015091, 0.0021244, 0.0020127, 0.0012209, 2.24e-05, -0.0011809, -0.0019867, -0.0021205, -0.0015306, -0.0004159, 0.0008383, 0.0017992, 0.0021428, 0.0017611, 0.0007882, -0.0004499, -0.0015386, -0.0021068, -0.0019553, -0.0011339, 7.14e-05, 0.0012442, 0.0019877, 0.0020578, 0.0014347, 0.0003262, -0.0008966, -0.0018165, -0.0021148, -0.0016882, -0.0006887, 0.000537, 0.0015738, 0.0020767, 0.0018792, 0.0010444, -0.000149, -0.0012935, -0.0019935, -0.0020093, -0.0013421, -0.0002259, 0.0009607, 0.0018213, 0.0020673, 0.0016112, 0.0006034, -0.0006112, -0.001613, -0.0020596, -0.0018051, -0.0009433, 0.0002331, 0.0013305, 0.0019794, 0.0019553, 0.0012618, 0.0001374, -0.0010288, -0.0018378, -0.0020205, -0.0015216, -0.0005109, 0.0006738, 0.0016326, 0.0020355, 0.0017413, 0.0008525, -0.0003216, -0.0013789, -0.001965, -0.0018876, -0.001173, -5.91e-05, 0.0010778, 0.0018469, 0.0019823, 0.0014405, 0.0004129, -0.0007478, -0.0016514, -0.0019972, -0.0016677, -0.0007702, 0.0003922, 0.0014195, 0.001958, 0.0018266, 0.0010775, -3.11e-05, -0.0011263, -0.0018414, -0.0019332, -0.0013661, -0.0003313, 0.0008146, 0.0016769, 0.0019643, 0.0015862, 0.000675, -0.000463, -0.0014459, -0.0019393, -0.001771, -0.0009967, 0.000115, 0.0011811, 0.0018404, 0.0018752, 0.0012778, 0.0002483, -0.0008677, -0.0016903, -0.0019351, -0.0015177, -0.0005848, 0.0005404, 0.0014763, 0.0019109, 0.0017002, 0.0009131, -0.0001866, -0.0012242, -0.001842, -0.0018284, -0.0011933, -0.0001582, 0.0009251, 0.0016938, 0.0018902, 0.0014448, 0.0005057, -0.0006067, -0.0015088, -0.0018924, -0.0016319, -0.0008215, 0.0002629, 0.0012575, 0.0018274, 0.0017761, 0.0011178, 7.8e-05, -0.0009845, -0.0017062, -0.0018463, -0.0013629, -0.0004208, 0.0006639, 0.0015252, 0.0018674, 0.0015708, 0.0007383, -0.0003418, -0.0012994, -0.0018131, -0.0017136, -0.0010356, -5.6e-06, 0.0010282, 0.0017114, 0.0018083, 0.0012877, 0.0003325, -0.0007298, -0.0015414, -0.0018314, -0.0015019, -0.0006615, 0.0004058, 0.0013338, 0.0018034, 0.0016564, 0.0009488, -7.58e-05, -0.0010719, -0.0017054, -0.0017614, -0.0012176, -0.0002576, 0.0007887, 0.0015615, 0.0017995, 0.0014272, 0.0005747, -0.0004704, -0.0013571, -0.001784, -0.0016028, -0.0008738, 0.000151, 0.0011193, 0.0017024, 0.0017076, 0.0011364, 0.0001812, -0.000837, -0.0015717, -0.0017697, -0.0013628, -0.000493, 0.0005391, 0.0013831, 0.001757, 0.001537, 0.0007961, -0.0002168, -0.0011569, -0.0017009, -0.0016626, -0.0010591, -0.0001001, 0.000888, 0.0015738, 0.0017271, 0.0012943, 0.0004196, -0.0005984, -0.00141, -0.0017377, -0.0014738, -0.0007129, 0.0002859, 0.0011866, 0.0016859, 0.0016123, 0.0009882, 2.72e-05, -0.00094, -0.0015827, -0.0016859, -0.0012192, -0.000342, 0.0006505, 0.0014235, 0.0017121, 0.0014157, 0.0006365, -0.0003562, -0.0012227, -0.0016714, -0.0015545, -0.0009124, 3.93e-05, 0.0009788, 0.0015848, 0.0016484, 0.0011495, 0.0002623, -0.0007092, -0.0014371, -0.0016781, -0.0013514, -0.0005653, 0.0004143, 0.0012518, 0.0016598, 0.0015007, 0.0008333, -0.0001128, -0.0010179, -0.0015782, -0.0016037, -0.0010836, -0.0001936, 0.000761, 0.0014527, 0.0016471, 0.0012827, 0.0004861, -0.000473, -0.0012722, -0.0016402, -0.0014493, -0.0007639, 0.0001804, 0.0010589, 0.0015736, 0.0015537, 0.001009, 0.0001234, -0.0008048, -0.0014601, -0.0016173, -0.0012223, -0.0004121, 0.0005339, 0.0012943, 0.0016143, 0.0013883, 0.0006918, -0.0002406, -0.0010917, -0.0015695, -0.0015106, -0.0009381, -5.05e-05, 0.0008501, 0.0014609, 0.001577, 0.001158, 0.0003442, -0.0005871, -0.0013164, -0.0015944, -0.0013298, -0.0006162, 0.0003029, 0.0011179, 0.0015543, 0.0014626, 0.000872, -1.57e-05, -0.0008955, -0.0014669, -0.0015383, -0.0010891, -0.0002733, 0.0006344, 0.0013273, 0.0015687, 0.0012746, 0.0005461, -0.0003657, -0.0011491, -0.0015398, -0.0014089, -0.000802, 7.68e-05, 0.0009301, 0.0014665, 0.0015017, 0.0010246, 0.0002012, -0.0006865, -0.0013385, -0.0015367, -0.0012146, -0.0004802, 0.0004183, 0.0011735, 0.0015266, 0.0013584, 0.0007298, -0.0001431, -0.0009649, -0.0014594, -0.0014592, -0.000963, -0.0001384, 0.0007322, 0.0013504, 0.0015066, 0.0011514, 0.0004082, -0.0004716, -0.0011913, -0.0015069, -0.0013096, -0.0006657, 0.0002036, 0.0010002, 0.0014534, 0.0014126, 0.0008943, 7.4e-05, -0.0007716, -0.0013554, -0.0014772, -0.0010949, -0.0003408, 0.0005256, 0.0012099, 0.0014822, 0.0012528, 0.000599, -0.0002587, -0.0010287, -0.0014473, -0.0013714, -0.0008293, -8.3e-06, 0.0008118, 0.0013551, 0.0014391, 0.0010347, 0.0002781, -0.0005733, -0.0012279, -0.0014621, -0.0011985, -0.0005302, 0.000315, 0.0010518, 0.0014319, 0.0013257, 0.0007677, -5.17e-05, -0.0008515, -0.0013585, -0.0014025, -0.0009717, -0.0002135, 0.000616, 0.0012364, 0.0014365, 0.0011464, 0.000466, -0.000371, -0.0010785, -0.0014175, -0.001276, -0.0007033, 0.0001075, 0.000882, 0.001356, 0.001367, 0.0009118, 0.0001483, -0.0006622, -0.0012455, -0.0014062, -0.0010906, -0.0004051, 0.0004185, 0.0010989, 0.0014032, 0.0012286, 0.0006373, -0.0001674, -0.0009129, -0.0013485, -0.0013268, -0.0008543, -9.08e-05, 0.0007025, 0.0012542, 0.0013771, 0.0010324, 0.0003394, -0.0004668, -0.0011142, -0.0013837, -0.0011823, -0.000578, 0.0002218, 0.0009433, 0.0013413, 0.0012833, 0.0007912, 3.19e-05, -0.0007379, -0.0012573, -0.0013483, -0.0009795, -0.0002782, 0.0005146, 0.0011296, 0.0013599, 0.0011295, 0.0005165, -0.000272, -0.0009681, -0.0013337, -0.0012441, -0.0007315, 2.71e-05, 0.0007734, 0.0012558, 0.0013123, 0.0009235, 0.0002205, -0.0005574, -0.0011442, -0.0013397, -0.0010792, -0.0004541, 0.0003226, 0.0009882, 0.0013185, 0.0012008, 0.0006742, -8.18e-05, -0.0008081, -0.0012572, -0.0012777, -0.0008656, -0.0001618, 0.0005958, 0.0011508, 0.0013145, 0.0010301, 0.0003951, -0.0003728, -0.001011, -0.0013039, -0.0011547, -0.000615, 0.0001326, 0.0008351, 0.001253, 0.0012435, 0.00081, 0.0001026, -0.0006368, -0.0011579, -0.0012859, -0.0009782, -0.0003389, 0.0004157, 0.0010281, 0.0012889, 0.0011101, 0.0005548, -0.0001864, -0.0008624, -0.001245, -0.0012056, -0.0007565, -5.02e-05, 0.0006723, 0.001164, 0.0012578, 0.0009245, 0.0002792, -0.0004592, -0.0010411, -0.0012696, -0.0010663, -0.0005001, 0.0002354, 0.0008883, 0.001237, 0.0011648, 0.0006986, -3.6e-06, -0.0007041, -0.0011654, -0.0012298, -0.0008752, -0.0002235, 0.0005016, 0.0010537, 0.0012468, 0.0010173, 0.0004433, -0.0002811, -0.0009099, -0.0012283, -0.0011277, -0.0006437, 5.67e-05, 0.0007354, 0.0011629, 0.0011958, 0.000823, 0.0001705, -0.0005399, -0.0010654, -0.0012268, -0.0009707, -0.0003865, 0.0003266, 0.0009273, 0.0012131, 0.0010867, 0.0005906, -0.0001063, -0.0007657, -0.0011625, -0.0011632, -0.0007697, -0.0001169, 0.0005744, 0.0010703, 0.0012021, 0.0009246, 0.0003324, -0.0003715, -0.0009466, -0.0011986, -0.0010439, -0.0005362, 0.0001526, 0.0007893, 0.001157, 0.0011302, 0.0007185, 6.34e-05, -0.0006106, -0.0010755, -0.001175, -0.0008764, -0.0002807, 0.0004101, 0.0009607, 0.0011832, 0.0010021, 0.0004811, -0.0002012, -0.0008132, -0.0011488, -0.0010946, -0.0006686, -1.56e-05, 0.0006419, 0.0010793, 0.0011481, 0.0008268, 0.0002264, -0.0004493, -0.0009718, -0.0011642, -0.0009609, -0.0004307, 0.0002452, 0.0008355, 0.0011399, 0.0010566, 0.0006154, -3.34e-05, -0.0006703, -0.0010793, -0.0011209, -0.0007809, -0.0001758, 0.000487, 0.0009819, 0.0011424, 0.0009154, 0.0003785, -0.0002867, -0.0008541, -0.0011305, -0.0010213, -0.0005652, 8.13e-05, 0.0006977, 0.001076, 0.0010889, 0.0007323, 0.0001271, -0.0005212, -0.0009911, -0.0011226, -0.0008721, -0.0003268, 0.0003275, 0.0008692, 0.0011153, 0.0009826, 0.0005158, -0.0001262, -0.0007242, -0.0010742, -0.0010581, -0.0006835, -7.84e-05, 0.0005519, 0.0009944, 0.0010986, 0.0008289, 0.0002772, -0.0003676, -0.0008854, -0.001101, -0.0009429, -0.000466, 0.0001683, 0.0007449, 0.0010678, 0.0010266, 0.000636, 3e-05, -0.0005841, -0.0009981, -0.001073, -0.0007843, -0.0002298, 0.0004023, 0.0008969, 0.0010853, 0.0009037, 0.0004156, -0.0002121, -0.0007659, -0.0010592, -0.000993, -0.0005898, 1.36e-05, 0.0006115, 0.001, 0.0010471, 0.0007384, 0.0001802, -0.0004376, -0.0009061, -0.0010669, -0.0008651, -0.0003692, 0.0002515, 0.0007849, 0.0010498, 0.0009575, 0.0005409, -5.82e-05, -0.0006369, -0.000999, -0.0010211, -0.0006957, -0.0001343, 0.0004709, 0.0009141, 0.0010459, 0.0008229, 0.0003211, -0.0002892, -0.0008008, -0.0010396, -0.0009242, -0.0004947, 0.0001013, 0.0006609, 0.0009948, 0.0009907, 0.0006506, 8.96e-05, -0.0005015, -0.0009211, -0.0010265, -0.0007827, -0.0002741, 0.0003258, 0.0008136, 0.0010248, 0.0008879, 0.0004491, -0.0001421, -0.0006839, -0.0009918, -0.0009618, -0.0006058, -4.55e-05, 0.0005289, 0.0009232, 0.0010034, 0.0007422, 0.0002286, -0.0003617, -0.0008273, -0.0010107, -0.000851, -0.0004034, 0.0001802, 0.000702, 0.0009847, 0.0009317, 0.0005618, 1.7e-06, -0.0005573, -0.0009255, -0.0009792, -0.000701, -0.0001853, 0.0003928, 0.0008366, 0.0009949, 0.0008142, 0.0003573, -0.0002196, -0.0007203, -0.0009758, -0.0009003, -0.0005192, 3.8e-05, 0.0005815, 0.0009259, 0.0009544, 0.0006585, 0.0001402, -0.0004244, -0.0008441, -0.0009772, -0.0007781, -0.0003146, 0.000255, 0.0007364, 0.000966, 0.0008669, 0.0004741, -7.86e-05, -0.0006039, -0.0009239, -0.0009295, -0.0006189, -9.85e-05, 0.0004539, 0.0008502, 0.0009569, 0.0007389, 0.0002706, -0.0002891, -0.0007501, -0.0009556, -0.0008357, -0.0004318, 0.0001174, 0.0006249, 0.000919, 0.0009009, 0.0005771, 5.76e-05, -0.0004811, -0.0008556, -0.0009381, -0.0007015, -0.0002277, 0.0003219, 0.0007608, 0.000941, 0.0008014, 0.0003896, -0.0001544, -0.000645, -0.0009151, -0.0008736, -0.0005358, -1.75e-05, 0.0005056, 0.0008564, 0.0009159, 0.0006638, 0.0001861, -0.0003539, -0.0007722, -0.0009271, -0.0007672, -0.0003477, 0.0001888, 0.0006607, 0.0009073, 0.0008451, 0.0004952, -2.23e-05, -0.0005307, -0.0008576, -0.000893, -0.0006256, -0.0001465, 0.0003818, 0.0007796, 0.0009115, 0.0007328, 0.0003055, -0.0002242, -0.0006765, -0.0008985, -0.0008155, -0.0004559, 5.83e-05, 0.0005518, 0.0008565, 0.0008692, 0.0005864, 0.0001054, -0.0004101, -0.0007857, -0.0008943, -0.000699, -0.0002664, 0.0002561, 0.0006901, 0.0008883, 0.0007843, 0.0004145, -9.51e-05, -0.0005717, -0.0008538, -0.0008454, -0.0005497, -6.75e-05, 0.0004362, 0.0007901, 0.0008749, 0.0006627, 0.000226, -0.0002868, -0.0007018, -0.0008777, -0.0007548, -0.0003756, 0.0001301, 0.00059, 0.0008484, 0.0008185, 0.0005109, 3.02e-05, -0.0004605, -0.0007939, -0.0008566, -0.0006281, -0.000187, 0.0003161, 0.0007107, 0.0008634, 0.0007229, 0.0003368, -0.0001636, -0.0006074, -0.0008438, -0.0007929, -0.0004731, 6.1e-06, 0.0004821, 0.0007937, 0.0008353, 0.0005928, 0.0001489, -0.0003448, -0.0007201, -0.0008498, -0.0006911, -0.0002984, 0.0001947, 0.0006208, 0.0008355, 0.0007658, 0.0004355, -4.22e-05, -0.0005043, -0.0007939, -0.0008137, -0.0005575, -0.0001128, 0.0003698, 0.0007259, 0.0008345, 0.0006589, 0.0002598, -0.0002265, -0.0006347, -0.0008267, -0.0007381, -0.0003992, 7.49e-05, 0.0005227, 0.0007919, 0.000791, 0.0005213, 7.53e-05, -0.0003951, -0.0007306, -0.0008179, -0.0006274, -0.0002239, 0.000255, 0.000646, 0.0008163, 0.0007089, 0.0003612, -0.0001084, -0.0005404, -0.0007885, -0.0007684, -0.0004873, -4.1e-05, 0.0004181, 0.0007337, 0.0007993]}
\ No newline at end of file
packages/core/test/fixtures/fem_center_24.jsonadded+187−0View file
@@ -0,0 +1,187 @@
1+{
2+ "model": "Q4 Mindlin-Reissner FEM; sparse eigensolve; coupled Hunt-Crossley/Hertz impact",
3+ "config": {
4+ "width": 0.03,
5+ "height": 0.03,
6+ "thickness": 0.001,
7+ "density": 2700.0,
8+ "young": 68900000000.0,
9+ "poisson": 0.33,
10+ "material_q": 1400.0,
11+ "shear_correction": 0.8333333333333334,
12+ "mesh_x": 24,
13+ "mesh_y": 24,
14+ "hole_diameter": 0.0008,
15+ "hole_edge_offset": 0.0015,
16+ "thread_length": 0.2,
17+ "thread_dashpot": 0.00075,
18+ "hit_x": 0.0,
19+ "hit_y": 0.0,
20+ "hammer_mass": 0.01,
21+ "hammer_tip_radius": 0.002,
22+ "hammer_speed": 0.45,
23+ "hammer_young": 200000000000.0,
24+ "hammer_poisson": 0.29,
25+ "restitution": 0.62,
26+ "mic_distance": 0.35,
27+ "air_density": 1.204,
28+ "sound_speed": 343.0,
29+ "sample_rate": 96000,
30+ "duration": 4.0,
31+ "modes": 72,
32+ "normalize_dbfs": -1.0
33+ },
34+ "derived": {
35+ "plate_mass_kg": 0.002428642831973649,
36+ "fem_degrees_of_freedom": 1875,
37+ "retained_mode_count": 17,
38+ "low_frequency_momentum_modes": 3,
39+ "contact_duration_s": 0.000132383212274934,
40+ "impact_impulse_Ns": 0.0016375249665463905,
41+ "peak_contact_force_N": 25.020216625829487,
42+ "hammer_exit_velocity_m_s_positive_into_plate": 0.28624739914553415,
43+ "plate_contact_point_exit_velocity_m_s_positive_into_plate": 0.439856202266527,
44+ "relative_separation_velocity_m_s": -0.15360880312099284,
45+ "effective_relative_restitution": 0.3413528958244285,
46+ "critical_frequency_hz": 12120.88938013311,
47+ "unnormalized_peak_pressure_pa_approx": 5.98923539686272e-08
48+ },
49+ "modes": [
50+ {
51+ "mode": 1,
52+ "frequency_hz": 3600.4080725844165,
53+ "q_total": 409.9600287899171,
54+ "hit_participation": -3.7361900304658456e-06,
55+ "support_participation": -1.182833164210448e-05,
56+ "radiation_participation": 7.011689609460157e-14
57+ },
58+ {
59+ "mode": 2,
60+ "frequency_hz": 6639.739020911385,
61+ "q_total": 324.15243706355733,
62+ "hit_participation": -2.2052648520365476e-05,
63+ "support_participation": -39.58143497701389,
64+ "radiation_participation": -1.003074329685664e-09
65+ },
66+ {
67+ "mode": 3,
68+ "frequency_hz": 7797.7312056009,
69+ "q_total": 307.4082240111584,
70+ "hit_participation": 34.611700918603866,
71+ "support_participation": -2.9612931106441662,
72+ "radiation_participation": -5.444273259956069e-11
73+ },
74+ {
75+ "mode": 4,
76+ "frequency_hz": 10172.630015212579,
77+ "q_total": 276.7158801032387,
78+ "hit_participation": 2.510144291433077e-07,
79+ "support_participation": -1.4234263664121496,
80+ "radiation_participation": -1.5405156300320145e-11
81+ },
82+ {
83+ "mode": 5,
84+ "frequency_hz": 10172.630134456833,
85+ "q_total": 275.3434060522427,
86+ "hit_participation": 2.884633502811222e-06,
87+ "support_participation": -39.20665815015334,
88+ "radiation_participation": -4.2324889334074424e-10
89+ },
90+ {
91+ "mode": 6,
92+ "frequency_hz": 17903.15007530998,
93+ "q_total": 218.7552049277444,
94+ "hit_participation": -33.375565808294695,
95+ "support_participation": 39.64317589459399,
96+ "radiation_participation": 1.3817031456895743e-10
97+ },
98+ {
99+ "mode": 7,
100+ "frequency_hz": 19943.217161615372,
101+ "q_total": 209.44671720295057,
102+ "hit_participation": -9.019585547821138e-07,
103+ "support_participation": 8.13013427636628,
104+ "radiation_participation": 2.2839874621178842e-11
105+ },
106+ {
107+ "mode": 8,
108+ "frequency_hz": 19943.217215293316,
109+ "q_total": 209.37998855168408,
110+ "hit_participation": -1.0796642404743496e-06,
111+ "support_participation": 17.8975611218953,
112+ "radiation_participation": 5.027550358008791e-11
113+ },
114+ {
115+ "mode": 9,
116+ "frequency_hz": 21475.009482872443,
117+ "q_total": 202.9584720225707,
118+ "hit_participation": 4.2761803157052736e-08,
119+ "support_participation": -2.910903665476984e-07,
120+ "radiation_participation": 1.4648658805058196e-15
121+ },
122+ {
123+ "mode": 10,
124+ "frequency_hz": 23628.814219679014,
125+ "q_total": 194.80535034125583,
126+ "hit_participation": -1.1503867194476365e-08,
127+ "support_participation": -4.5949901727538813e-07,
128+ "radiation_participation": -8.449675421157155e-15
129+ },
130+ {
131+ "mode": 11,
132+ "frequency_hz": 30367.480079975117,
133+ "q_total": 174.70338962367603,
134+ "hit_participation": 2.8175480561084736e-08,
135+ "support_participation": -0.7117384521903711,
136+ "radiation_participation": -8.572951512098373e-13
137+ },
138+ {
139+ "mode": 12,
140+ "frequency_hz": 30367.480151056006,
141+ "q_total": 174.53033939754278,
142+ "hit_participation": -4.63943716187346e-07,
143+ "support_participation": 38.00489885438103,
144+ "radiation_participation": 4.604076418141087e-11
145+ },
146+ {
147+ "mode": 13,
148+ "frequency_hz": 38243.12862144079,
149+ "q_total": 157.80816245202462,
150+ "hit_participation": 1.0539450369569254e-06,
151+ "support_participation": -13.984335739185726,
152+ "radiation_participation": -1.0681959327317469e-11
153+ },
154+ {
155+ "mode": 14,
156+ "frequency_hz": 39589.25822354179,
157+ "q_total": 155.33716752945867,
158+ "hit_participation": 43.47168387323361,
159+ "support_participation": 33.20328679472101,
160+ "radiation_participation": 2.367158653812716e-11
161+ },
162+ {
163+ "mode": 15,
164+ "frequency_hz": 41319.417867984834,
165+ "q_total": 152.48675827895758,
166+ "hit_participation": 1.576132340727319e-06,
167+ "support_participation": 1.650160552457418,
168+ "radiation_participation": 1.0766953192735407e-12
169+ },
170+ {
171+ "mode": 16,
172+ "frequency_hz": 41319.41802659892,
173+ "q_total": 152.37514634337217,
174+ "hit_participation": 1.2919019444635567e-06,
175+ "support_participation": 40.81063909609001,
176+ "radiation_participation": 2.6712064228303148e-11
177+ },
178+ {
179+ "mode": 17,
180+ "frequency_hz": 43143.86478601319,
181+ "q_total": 149.57615752151258,
182+ "hit_participation": -1.5018443770406984e-07,
183+ "support_participation": -2.351467631129968e-08,
184+ "radiation_participation": -1.44574971569153e-16
185+ }
186+ ]
187+}
packages/core/test/fixtures/fem_center_24_wav.jsonadded+1−0View file
@@ -0,0 +1 @@
1+{"fs": 96000, "samples": [-4e-07, -0.6370902, 0.132383, 0.891251, -0.0017917, -0.6310155, 0.0068208, 0.5191463, 0.0440065, -0.1140144, 0.3512985, 0.2425739, -0.2989593, -0.4802135, -0.1773381, 0.3617824, 0.3611194, -0.1753905, -0.5809779, -0.3962951, 0.2727185, 0.5806057, 0.2602903, -0.2771433, -0.3922503, 0.0979658, 0.4740346, 0.2976897, -0.3081406, -0.6354713, -0.2634424, 0.2975742, 0.474287, 0.0413654, -0.3865521, -0.2395999, 0.272718, 0.5823833, 0.2091192, -0.3847682, -0.5453789, -0.1402213, 0.362982, 0.2872562, -0.1781872, -0.4583619, -0.15235, 0.4358616, 0.557413, 0.131361, -0.4022955, -0.4057869, 0.0730021, 0.3657942, 0.1451912, -0.3823101, -0.5012597, -0.0470372, 0.4624954, 0.4946938, -0.0131193, -0.3742867, -0.2067762, 0.2532057, 0.413054, -0.0150803, -0.4840541, -0.4840152, 0.0271261, 0.458748, 0.3067203, -0.142496, -0.3559314, -0.0151337, 0.4296865, 0.3945203, -0.0900326, -0.5300614, -0.3788653, 0.1202374, 0.3754473, 0.1219145, -0.3194437, -0.3100154, 0.1330264, 0.518666, 0.3703568, -0.1761992, -0.4564826, -0.2275812, 0.2243965, 0.2996509, -0.096688, -0.4290618, -0.2918659, 0.2393688, 0.5265364, 0.2636822, -0.2098286, -0.3652578, -0.0126439, 0.3253622, 0.2169041, -0.2421193, -0.5155528, -0.2219967, 0.2736556, 0.4501725, 0.121215, -0.2744713, -0.2196989, 0.1696758, 0.4217197, 0.1517048, -0.3426909, -0.488553, -0.1515694, 0.2979335, 0.3060801, -0.0636239, -0.3171618, -0.1176987, 0.337123, 0.4523541, 0.0940346, -0.3619425, -0.405947, -0.0122211, 0.2834745, 0.1545737, -0.2435565, -0.3679088, -0.016112, 0.4039965, 0.4368724, 0.0169597, -0.3379558, -0.2435425, 0.1262558, 0.296499, 0.0004911, -0.3773343, -0.3779098, 0.0379193, 0.4203977, 0.3248753, -0.0675832, -0.2905983, -0.0718997, 0.2864942, 0.2848349, -0.0943642, -0.4495385, -0.3410438, 0.0960491, 0.3525427, 0.1796601, -0.1877631, -0.2375059, 0.0902262, 0.3932647, 0.2833377, -0.1665693, -0.4282496, -0.2459532, 0.1474307, 0.2718104, -0.0106927, -0.2908308, -0.2035966, 0.2037845, 0.4480805, 0.2313154, -0.1861462, -0.3548748, -0.0949981, 0.2148944, 0.1743624, -0.1647403, -0.3861169, -0.1621512, 0.2576098, 0.4172724, 0.1521388, -0.2119309, -0.2266536, 0.0691664, 0.2856042, 0.1045153, -0.2856814, -0.4093675, -0.1250961, 0.2691026, 0.3207574, 0.0205233, -0.2236287, -0.1111909, 0.2305244, 0.3372622, 0.0498558, -0.3279744, -0.3795046, -0.0486656, 0.244015, 0.182926, -0.1241596, -0.2554295, -0.0031466, 0.3303541, 0.3556275, 0.0078057, -0.3191411, -0.2695388, 0.0422684, 0.2244706, 0.0354307, -0.2623502, -0.272298, 0.0562326, 0.3751005, 0.308506, -0.0381838, -0.2646753, -0.1283423, 0.165393, 0.2012632, -0.079313, -0.3561786, -0.2735937, 0.0985353, 0.3392566, 0.2129309, -0.1049214, -0.2004752, 0.0295012, 0.2707075, 0.19703, -0.1576548, -0.3820628, -0.2294776, 0.1182609, 0.2669283, 0.0656574, -0.1807667, -0.1454189, 0.1543586, 0.3509177, 0.175705, -0.1826538, -0.3427018, -0.1396488, 0.148782, 0.1655232, -0.0810406, -0.2639207, -0.1051822, 0.23217, 0.3646038, 0.1416453, -0.1877868, -0.2440911, -0.0134107, 0.1860849, 0.0816786, -0.2124181, -0.314405, -0.0801519, 0.2538964, 0.3176426, 0.064991, -0.175039, -0.1286645, 0.127184, 0.2309383, 0.0167468, -0.2832786, -0.3259573, -0.0451931, 0.2318056, 0.2126407, -0.0357248, -0.177709, -0.013877, 0.2426465, 0.2633949, -0.017885, -0.3000034, -0.2708961, 0.0022388, 0.1926846, 0.081838, -0.1547047, -0.1832202, 0.0625758, 0.314251, 0.2611904, -0.0416082, -0.2587302, -0.1709773, 0.0798092, 0.1516812, -0.0426263, -0.2552895, -0.195252, 0.1081222, 0.3172382, 0.2147303, -0.0679355, -0.1924523, -0.0356728, 0.1646598, 0.1297617, -0.135329, -0.3150244, -0.1852718, 0.1181814, 0.2685739, 0.1179338, -0.1083783, -0.120862, 0.0913829, 0.2478229, 0.1148484, -0.1787908, -0.3150163, -0.1455645, 0.122196, 0.17798, -0.0036014, -0.1645957, -0.0672472, 0.1891712, 0.2924564, 0.1042085, -0.1839609, -0.2553691, -0.0658791, 0.1275878, 0.085242, -0.1302474, -0.2179086, -0.0362308, 0.2335863, 0.2897116, 0.0711181, -0.1604996, -0.1571551, 0.0407573, 0.1492034, 0.0056429, -0.2223064, -0.2530489, -0.018485, 0.2285322, 0.2278258, 0.0150378, -0.137766, -0.0445207, 0.1512354, 0.1761551, -0.0397703, -0.2677249, -0.2436137, -0.0012172, 0.1877907, 0.1263813, -0.0695043, -0.1223854, 0.0480588, 0.2386568, 0.1952175, -0.0601829, -0.2536242, -0.189009, 0.0343294, 0.134489, 0.0079505, -0.1588743, -0.1247252, 0.1084728, 0.276878, 0.1867116, -0.0649473, -0.1996378, -0.0900017, 0.087208, 0.0914352, -0.0953126, -0.2337152, -0.1276394, 0.1271171, 0.2613396, 0.1383231, -0.0744009, -0.1236856, 0.0241003, 0.1542983, 0.0656877, -0.1610029, -0.2668958, -0.1204076, 0.1218591, 0.1956221, 0.0542128, -0.098241, -0.05529, 0.1302043, 0.210221, 0.0577641, -0.1819329, -0.2483592, -0.0838413, 0.1056141, 0.1072438, -0.0518211, -0.1355084, -0.0082585, 0.1978597, 0.2385708, 0.0494003, -0.1638671, -0.1811619, -0.0176822, 0.1002461, 0.0182098, -0.1498386, -0.1743483, 0.0126834, 0.2183594, 0.2192676, 0.0293353, -0.1286003, -0.0840805, 0.0701795, 0.1082568, -0.0446482, -0.2179003, -0.1933239, 0.017835, 0.1926081, 0.1557843, -0.0163236, -0.093457, 0.0143353, 0.1571025, 0.1269179, -0.0765275, -0.2355836, -0.1786518, 0.0241243, 0.1397709, 0.0591785, -0.0802559, -0.0757574, 0.0909017, 0.2183121, 0.1385958, -0.0790985, -0.2062935, -0.1215571, 0.0440414, 0.0822222, -0.042708, -0.1507416, -0.072989, 0.1285983, 0.235955, 0.1276122, -0.0707833, -0.1410036, -0.0344688, 0.0835891, 0.0390011, -0.1249626, -0.2026224, -0.0781156, 0.1309022, 0.2036933, 0.083743, -0.0667306, -0.0664816, 0.0644732, 0.1317865, 0.0189452, -0.1681917, -0.2186497, -0.0718051, 0.1083446, 0.134234, 0.0097791, -0.0787243, -0.0032302, 0.1461307, 0.1734219, 0.0154895, -0.1684392, -0.1883362, -0.0435575, 0.0829891, 0.0476217, -0.0771583, -0.1047758, 0.0328996, 0.192079, 0.1865435, 0.0161694, -0.1366579, -0.118863, 0.0116674, 0.0680552, -0.0286715, -0.1547654, -0.1323512, 0.042792, 0.1915978, 0.1616286, 0.003477, -0.0914589, -0.0291414, 0.0823044, 0.071435, -0.0783688, -0.199154, -0.1443178, 0.0371029, 0.153172, 0.0976149, -0.0289823, -0.0540128, 0.0557586, 0.1496731, 0.0847103, -0.0934829, -0.2000096, -0.1253575, 0.0325788, 0.0939748, 0.0113934, -0.0799358, -0.0346394, 0.1134222, 0.191544, 0.0944238, -0.0836773, -0.1579634, -0.0729643, 0.0427407, 0.0369666, -0.0750883, -0.1330171, -0.034647, 0.134185, 0.1930267, 0.0839723, -0.063683, -0.0907066, 0.0046291, 0.0702624, -0.0011568, -0.1373311, -0.1700983, -0.0412201, 0.1204602, 0.1526152, 0.0456092, -0.0516074, -0.0193939, 0.0857974, 0.1076001, -0.0148571, -0.1615766, -0.1732029, -0.0401973, 0.0884616, 0.0818825, -0.0170084, -0.0559367, 0.0338728, 0.1488435, 0.1371358, -0.0104966, -0.1466828, -0.1373767, -0.018072, 0.0558403, 0.0033323, -0.0886021, -0.075174, 0.0592824, 0.1750934, 0.1429399, -0.0034018, -0.1050727, -0.0695912, 0.026035, 0.0384479, -0.0613045, -0.1474012, -0.0968044, 0.0579804, 0.1607821, 0.1141731, -0.0076483, -0.0565747, 0.0108027, 0.0832683, 0.0392892, -0.0956529, -0.1751954, -0.1044123, 0.0431351, 0.113817, 0.054688, -0.0317365, -0.0191906, 0.0809276, 0.1348308, 0.0521052, -0.0979884, -0.162471, -0.0857068, 0.0309012, 0.0537106, -0.0216322, -0.0710411, -0.0033925, 0.1224075, 0.1621579, 0.061494, -0.076955, -0.1148919, -0.0379474, 0.0336922, 0.0006975, -0.0922605, -0.1127553, -0.0065103, 0.1277509, 0.1531667, 0.0536377, -0.0501661, -0.0480895, 0.0283889, 0.0540763, -0.0302579, -0.137704, -0.1381725, -0.0175158, 0.103376, 0.108359, 0.0210867, -0.0328782, 0.015637, 0.0951467, 0.083218, -0.0360138, -0.1464124, -0.1339471, -0.0204209, 0.0645058, 0.0409186, -0.031522, -0.0337877, 0.0588715, 0.1412368, 0.1060099, -0.0248179, -0.1207084, -0.095668, -0.004869, 0.0299995, -0.0288563, -0.0895836, -0.0495188, 0.0728759, 0.1533141, 0.1067163, -0.0113845, -0.073989, -0.0325005, 0.031005, 0.0122529, -0.0803658, -0.1339523, -0.068162, 0.0623007, 0.1286749, 0.0781289, -0.0101357, -0.0253967, 0.0376787, 0.0770091, 0.0144426, -0.1018325, -0.1485279, -0.0742923, 0.040213, 0.0782578, 0.0235798, -0.0272805, 0.0082606, 0.0939272, 0.1168438, 0.0279757, -0.0926691, -0.1276226, -0.0568956, 0.0229169, 0.0201153, -0.0418696, -0.0590638, 0.0194063, 0.1209675, 0.1335426, 0.0390136, -0.0643678, -0.0774372, -0.0149742, 0.0214893, -0.0263525, -0.0988729, -0.0919299, 0.0113202, 0.11457, 0.1179637, 0.0338148, -0.0331932, -0.0148458, 0.0416693, 0.0373725, -0.0491998, -0.129769, -0.1100505, -0.0034155, 0.0824487, 0.0722945, 0.0068229, -0.0143631, 0.0406064, 0.0953257, 0.0618635, -0.0472026, -0.126838, -0.1010928, -0.0104454, 0.0408537, 0.0098832, -0.0372731, -0.0142169, 0.0730008, 0.1283432, 0.0801468, -0.0298278, -0.0939869, -0.0633344, 0.0005203, 0.0067616, -0.0499096, -0.0844116, -0.0290741, 0.0771693, 0.1292961, 0.0787447, -0.011965, -0.0455794, -0.0058035, 0.0295762, -0.0083498, -0.0895136, -0.1173049, -0.0466108, 0.0587695, 0.0986611, 0.0513233, -0.0067322, 0.000234, 0.054023, 0.0674058, -0.0037416, -0.0993669, -0.1226109, -0.0525928, 0.0319603, 0.0475255, 0.0028085, -0.0197171, 0.0286574, 0.0977771, 0.0982689, 0.0120152, -0.0817759, -0.096544, -0.0373526, 0.0119221, -0.0060284, -0.0529563, -0.0460404, 0.0339557, 0.1128702, 0.1077173, 0.0247133, -0.0485152, -0.0469569, -0.0007812, 0.0086756, -0.0450488, -0.0977764, -0.0732108, 0.0212011, 0.0975412, 0.0883916, 0.0222436, -0.0160612, 0.0101434, 0.0470804, 0.0224693, -0.0595857, -0.1171987, -0.0862197, 0.0028716, 0.0609955, 0.0439979, -0.0002706, 0.0023201, 0.0564205, 0.0901458, 0.0442687, -0.0506418, -0.1055862, -0.0751036, -0.0068934, 0.0191102, -0.0121692, -0.0373809, 0.0013463, 0.0789739, 0.1126012, 0.060151, -0.0284841, -0.068835, -0.03906, 0.0005713, -0.0121475, -0.0622742, -0.0758799, -0.0139412, 0.0745018, 0.105897, 0.0578009, -0.0077299, -0.0212598, 0.0121477, 0.0249608, -0.0235155, -0.0909854, -0.1001077, -0.0315388, 0.050504, 0.0718845, 0.0325766, -0.0005062, 0.0200317, 0.0624123, 0.0565703, -0.015421, -0.0915101, -0.0988779, -0.0379471, 0.020938, 0.0225551, -0.0102947, -0.0110434, 0.0422773, 0.0952739, 0.0810858, 0.0025511, -0.0676816, -0.0703557, -0.0248725, 0.0003078, -0.0252978, -0.0572008, -0.0340989, 0.0417715, 0.1008176, 0.085544, 0.0169389, -0.0321202, -0.0230078, 0.0069078, -0.0029653, -0.0563998, -0.0920014, -0.0573167, 0.024709, 0.0792706, 0.0645708, 0.016415, -0.0002719, 0.0275636, 0.0474809, 0.0103464, -0.0632939, -0.1022904, -0.0672197, 0.003857, 0.0407289, 0.0227323, -0.0025554, 0.0158337, 0.0650636, 0.0818509, 0.0308926, -0.0485191, -0.0848322, -0.0551598, -0.0076606, 0.000677, -0.026853, -0.0342996, 0.0127269, 0.0786949, 0.0963581, 0.0454125, -0.0230789, -0.046499, -0.021737, -0.0021634, -0.0265341, -0.067867, -0.0660865, -0.00387, 0.06743, 0.0843595, 0.0429567, -0.0010347, -0.0016093, 0.0233771, 0.0189718, -0.0333428, -0.0872681, -0.0838662, -0.0218688, 0.0396196, 0.0492852, 0.0199991, 0.0066944, 0.0341996, 0.0649827, 0.046247, -0.0217648, -0.0804004, -0.0783089, -0.0288248, 0.0092498, 0.0031013, -0.0176044, -0.0029495, 0.0500897, 0.0887656, 0.0661327, -0.0017021, -0.0526198, -0.0490627, -0.017576, -0.0104549, -0.0383389, -0.057002, -0.0240659, 0.0441928, 0.0869315, 0.0673941, 0.0137303, -0.0166156, -0.0051146, 0.0102457, -0.0124224, -0.0618837, -0.0835165, -0.0447674, 0.0237286, 0.0614283, 0.0460308, 0.0144765, 0.0129906, 0.0388007, 0.0448353, 0.0014009, -0.0620096, -0.0869627, -0.0526218, 0.001334, 0.0228527, 0.0074475, -0.0020685, 0.0258964, 0.0680902, 0.0723507, 0.0214801, -0.0427865, -0.0657495, -0.0405068, -0.0107374, -0.0140336, -0.0357114, -0.0297297, 0.0200048, 0.0742102, 0.0808669, 0.0352255, -0.0154884, -0.0276835, -0.0098639, -0.0061182, -0.0364366, -0.0685922, -0.0564383, 0.0019377, 0.0577556, 0.0655814, 0.0328925, 0.0064771, 0.0134308, 0.0295321, 0.0130714, -0.0386225, -0.0802287, -0.0694674, -0.0164969, 0.0279175, 0.0308977, 0.0120945, 0.0135016, 0.0433466, 0.0636742, 0.037246, -0.0238006, -0.0678839, -0.0611689, -0.0237292, -0.0018253, -0.0112269, -0.0209743, 0.0037328, 0.0531809, 0.0800117, 0.0538678, -0.0022576, -0.0379438, -0.0323916, -0.0138242, -0.0194167, -0.0462465, -0.0540282, -0.0164137, 0.0426613, 0.0727476, 0.0530349, 0.0135977, -0.003049, 0.0076472, 0.0108789, -0.0193169, -0.0628223, -0.0739533, -0.0353929, 0.0197772, 0.0450957, 0.0321047, 0.0147842, 0.0233518, 0.0450895, 0.0406896, -0.0044346, -0.0573506, -0.0723261, -0.0419139, -0.0030988, 0.007902, -0.0030292, -0.0002041, 0.032498, 0.0671109, 0.0628219, 0.0155108, -0.0349865, -0.0490799, -0.030074, -0.0147783, -0.0249801, -0.0401958, -0.0248989, 0.0237485, 0.0670594, 0.0669636, 0.0286621, -0.0071428, -0.0124848, -0.0021775, -0.0100901, -0.0423696, -0.0660213, -0.0477375, 0.0043106, 0.0470073, 0.0497932, 0.0264359, 0.013665, 0.0242156, 0.0321562, 0.0080136, -0.0401759, -0.071401, -0.0572994, -0.0142503, 0.0165429, 0.0165291, 0.0074487, 0.0191354, 0.048309, 0.0599448, 0.0300268, -0.0227015, -0.0552083, -0.0473566, -0.0213923, -0.0114225, -0.02119, -0.0217843, 0.0085806, 0.0526695, 0.0703663, 0.0442309, -0.0003345, -0.0245963, -0.0197605, -0.0122775, -0.0261968, -0.0500495, -0.0496309, -0.0111209, 0.0384485, 0.059256, 0.0420576, 0.0152226, 0.0081372, 0.0162163, 0.0100532, -0.0236206, -0.0605279, -0.0643338, -0.0288473, 0.0141534, 0.0308684, 0.0219451, 0.0161884, 0.0307366, 0.0476748, 0.0360854, -0.0075355, -0.0506079, -0.0591086, -0.0343472, -0.0082865, -0.0039905, -0.0097898, 0.002008, 0.036026, 0.0634215, 0.0540214, 0.0123163, -0.0263534, -0.0350485, -0.0229037, -0.0187897, -0.0324705, -0.0415812, -0.0205178, 0.0245969, 0.0585342, 0.0549892, 0.0248135, 0.0009761, -0.0007374, 0.002515, -0.0134006, -0.0449734, -0.0614225, -0.0404041, 0.0041672, 0.0362198, 0.0369544, 0.0225111, 0.0198176, 0.0313482, 0.0324379, 0.0042192, -0.0389197, -0.0619034, -0.0473852, -0.0141279, 0.0062834, 0.0056913, 0.0049479, 0.0232122, 0.0499522, 0.0549455, 0.0246314, -0.0194764, -0.0432326, -0.0365334, -0.020739, -0.0191494, -0.027565, -0.0211295, 0.0115393, 0.0496062, 0.0607269, 0.0369796, 0.0029993, -0.0130632, -0.0104916, -0.0119348, -0.0307118, -0.0507866, -0.0447131, -0.0079528, 0.0326293, 0.0470663, 0.033873, 0.0176482, 0.0168065, 0.0215322, 0.0086507, -0.0255964, -0.0560807, -0.0553371, -0.0246054, 0.0078509, 0.0189631, 0.0147452, 0.0178388, 0.0353878, 0.0476177, 0.031687, -0.0083995, -0.0428418, -0.0476208, -0.029201, -0.0133935, -0.0129725, -0.0138323, 0.003953, 0.0369754, 0.0581031, 0.0463307, 0.011189, -0.017753, -0.0236332, -0.0180918, -0.022171, -0.0369788, -0.0408987, -0.0169842, 0.0232754, 0.0495628, 0.0450107, 0.0228524, 0.008221, 0.0079566, 0.0051614, -0.0156788, -0.044963, -0.0557662, -0.0345342, 0.0023165, 0.0261151, 0.026786, 0.0202333, 0.024577, 0.0354822, 0.0313253, 0.0017792, -0.035691, -0.0525146, -0.0395482, -0.0152638, -0.0024543, -0.0021735, 0.0037194, 0.02564, 0.0491262, 0.0494688, 0.0209185, -0.0150056, -0.0324612, -0.0282345, -0.0209607, -0.0248797, -0.0311481, -0.0197766, 0.0127703, 0.0448884, 0.0516815, 0.0317115, 0.0069364, -0.0035396, -0.0038965, -0.0120689, -0.033134, -0.0493465, -0.0398803, -0.0065302, 0.0260621, 0.0364591, 0.0278932, 0.0201697, 0.023082, 0.0244337, 0.0072362, -0.025625, -0.050383, -0.0473108, -0.0221206, 0.0015914, 0.0093585, 0.0097469, 0.0192111, 0.037678, 0.0458058, 0.0278647, -0.0075856, -0.0348233, -0.0379409, -0.0257928, -0.0178852, -0.0193658, -0.0159758, 0.0052806, 0.0359204, 0.0519712, 0.0398784, 0.0114784, -0.0097789, -0.0146055, -0.0148959, -0.0245947, -0.0390515, -0.0389622, -0.0144241, 0.0204495, 0.0408055, 0.0368966, 0.0220765, 0.0142458, 0.0140671, 0.0065206, -0.0168165, -0.0430262, -0.049744, -0.030048, -0.0005329, 0.0171099, 0.0189118, 0.0189116, 0.0278269, 0.0372833, 0.0294924, 0.0006083, -0.031225, -0.0437329, -0.0334827, -0.0169944, -0.0095032, -0.0076528, 0.0031339, 0.0265268, 0.0465742, 0.0440515, 0.0186092, -0.0099808, -0.0231388, -0.02197, -0.0214628, -0.0287069, -0.0326488, -0.0182416, 0.0125579, 0.0392561, 0.0435436, 0.0279983, 0.0108798, 0.0039971, 0.0006641, -0.0122106, -0.0337533, -0.0464823, -0.0354749, -0.006429, 0.0193838, 0.0275059, 0.0235539, 0.0223367, 0.0272244, 0.0256299, 0.0061388, -0.0241537, -0.0441167, -0.0403932, -0.0208704, -0.0041411, 0.0018661, 0.0063164, 0.0200048, 0.0380552, 0.0429339, 0.0247687, -0.0056204, -0.0270996, -0.0299692, -0.0235457, -0.021463, -0.0235688, -0.0168779, 0.0058631, 0.0334119, 0.0456196, 0.0346248, 0.0126183, -0.0027757, -0.007651, -0.0127218, -0.0259471, -0.0392392, -0.0363647, -0.012807, 0.0167109, 0.0326786, 0.0304101, 0.0219344, 0.0189098, 0.0180933, 0.007145, -0.0168674, -0.0397775, -0.0438234, -0.0267468, -0.0038229, 0.0094093, 0.0129174, 0.0180388, 0.0296291, 0.0373523, 0.027405, 0.0005041, -0.0261209, -0.0358337, -0.0288457, -0.0188265, -0.0148845, -0.0112906, 0.0027666, 0.026091, 0.0429256, 0.0390083, 0.0173835, -0.0049223, -0.015323, -0.0172685, -0.0218564, -0.0308403, -0.0326774, -0.0168329, 0.0112411, 0.0332744, 0.0364448, 0.0254195, 0.0144329, 0.0097045, 0.0037382, -0.0120825, -0.0329266, -0.0427841, -0.0316581, -0.0072362, 0.0130399, 0.0201274, 0.0203725, 0.0238891, 0.0295688, 0.0256903, 0.0055027, -0.0216182, -0.0377818, -0.0345703, -0.0204014, -0.0090619, -0.0037826, 0.0039389, 0.0201025, 0.0369692, 0.0395197, 0.0224068, -0.0029725, -0.0200144, -0.023515, -0.0219966, -0.0240066, -0.0260038, -0.0170274, 0.0057108, 0.0299488, 0.039448, 0.0304294, 0.0141573, 0.0030904, -0.0024158, -0.0111295, -0.0262595, -0.0380466, -0.0335213, -0.0119976, 0.0125401, 0.0254154, 0.0252706, 0.0220159, 0.0222292, 0.0205019, 0.0074197, -0.0159872, -0.0357342, -0.0382857, -0.0243876, -0.0071323, 0.0030564, 0.0084033, 0.0172722, 0.0301474, 0.0362092, 0.0253509, 0.0012162, -0.0208405, -0.028932, -0.0252957, -0.0204314, -0.0187314, -0.0135629, 0.0023584, 0.0246081, 0.0386757, 0.0344974, 0.0169197, -0.0001897, -0.0089463, -0.0137188, -0.0219109, -0.0315561, -0.0317262, -0.0157056, 0.0091585, 0.0273556, 0.0303841, 0.0236075, 0.0173601, 0.0138146, 0.0057741, -0.0115596, -0.031016, -0.0386964, -0.0284619, -0.0085844, 0.0073104, 0.0141627, 0.0179551, 0.0247177, 0.0304697, 0.0250479, 0.0053511, -0.0184158, -0.0317138, -0.0297401, -0.020346, -0.0130377, -0.0078925, 0.0022267, 0.0195074, 0.0348376, 0.0359273, 0.0206951, -2.42e-05, -0.0137583, -0.018343, -0.0207988, -0.0255234, -0.0270703, -0.0167732, 0.004925, 0.0259501, 0.0337014, 0.0271055, 0.0157549, 0.0077888, 0.0014443, -0.009812, -0.0256536, -0.0359107, -0.0306964, -0.0118151, 0.0083084, 0.0191103, 0.0211952, 0.0220431, 0.0243152, 0.0217054, 0.0075887, -0.0143807, -0.0313065, -0.0332778, -0.02272, -0.0101714, -0.0020109, 0.0050132, 0.0164024, 0.0296031, 0.0342759, 0.0234875, 0.0024863, -0.0157175, -0.0230303, -0.0225287, -0.0216112, -0.0212426, -0.0148624, 0.0017771, 0.0223627, 0.0341994, 0.0305641, 0.0169295, 0.0039917, -0.0038675, -0.0109804, -0.0215216, -0.0311476, -0.0301749, -0.0149068, 0.0066199, 0.0217754, 0.0252825, 0.0222597, 0.0195526, 0.0165899, 0.0071173, -0.0106205, -0.0283597, -0.0345334, -0.0258389, -0.0101706, 0.0023466, 0.0094098, 0.0160019, 0.0248191, 0.0302655, 0.0240155, 0.0056274, -0.0148798, -0.0261186, -0.0257551, -0.0204268, -0.0160469, -0.0107625, 0.0009019, 0.018302, 0.032022, 0.0323943, 0.0195054, 0.0029296, -0.0084044, -0.0142127, -0.0197114, -0.0261027, -0.0271237, -0.016344, 0.0036514, 0.0217484, 0.0285078, 0.0244553, 0.0171763, 0.0113778, 0.0042411, -0.0085744, -0.0242996, -0.0331887, -0.0280424, -0.0120662, 0.0042818, 0.0137624, 0.0179263, 0.0218455, 0.0253353, 0.0220472, 0.007789, -0.0122652, -0.0268034, -0.0288479, -0.0215157, -0.0127597, -0.0059186, 0.0024486, 0.015324, 0.0282334, 0.0318807, 0.0218774, 0.004077, -0.0109737, -0.0180615, -0.0202906, -0.0222733, -0.0226432, -0.0154971, 0.0009799, 0.0196221, 0.0297614, 0.0271829, 0.0171733, 0.007504, 9.07e-05, -0.0087888, -0.0206718, -0.0298973, -0.0283011, -0.0144141, 0.0038865, 0.0167011, 0.0210185, 0.0211452, 0.0209936, 0.0182906, 0.008021, -0.0093117, -0.0252507, -0.0305023, -0.0236981, -0.011762, -0.0017984, 0.005659, 0.0142995, 0.0242598, 0.0292587, 0.0228053, 0.0062339, -0.0112748, -0.0211024, -0.0224543, -0.0204501, -0.0181428, -0.0126663, -0.0002198, 0.0166103, 0.0288188, 0.0290613, 0.0186942, 0.0056793, -0.0039448, -0.0109009, -0.0185826, -0.0258809, -0.0264622, -0.0158774, 0.0020499, 0.0175931, 0.0239111, 0.0222946, 0.0182756, 0.01397, 0.0062391, -0.0073088, -0.0223838, -0.0301594, -0.0256288, -0.0125709, 0.0006351, 0.0093099, 0.0152457, 0.0213392, 0.0254792, 0.0218009, 0.0080795, -0.0098461, -0.0224427, -0.0249816, -0.0205829, -0.0148053, -0.0088241, 0.0004724, 0.0140065, 0.0262682, 0.029266, 0.0205219, 0.0057868, -0.0067402, -0.0139204, -0.0183843, -0.0224009, -0.0231578, -0.0156974, -1.65e-05, 0.0166183, 0.0255368, 0.0242879, 0.0174661, 0.0103104, 0.0031139, -0.0069505, -0.0194045, -0.0280577, -0.0262973, -0.0141666, 0.0011642, 0.0122155, 0.0174561, 0.0201005, 0.0217291, 0.0191567, 0.0086595, -0.0077177, -0.0219294, -0.0267267, -0.0219321, -0.0131938, -0.0051392, 0.002713, 0.0127077, 0.0231456, 0.0277048, 0.0215507, 0.0070568, -0.007797, -0.0166988, -0.0196843, -0.0202949, -0.0194248, -0.0138396, -0.001249, 0.0145726, 0.0254588, 0.025997, 0.0181246, 0.0080889, -0.0003196, -0.0082147, -0.0173317, -0.0250142, -0.0253255, -0.0154431, 0.0002741, 0.0136579, 0.0198994, 0.0204657, 0.0189793, 0.0157051, 0.0076522, -0.0059711, -0.0200877, -0.0270284, -0.0234705, -0.0131748, -0.002532, 0.0056573, 0.0129795, 0.0205036, 0.0249373, 0.0211738, 0.0084671, -0.0073022, -0.0183661, -0.0216272, -0.0197718, -0.0162829, -0.0108929, -0.0010935, 0.01247, 0.0239136, 0.0266011, 0.0193856, 0.0074576, -0.0030777, -0.010487, -0.0166663, -0.0220298, -0.0229942, -0.0156268, -0.0011613, 0.0135407, 0.0216286, 0.0217956, 0.0176767, 0.0124306, 0.005379, -0.0053326, -0.0177965, -0.0258428, -0.0242877, -0.014086, -0.0013955, 0.0083404, 0.0144622, 0.0190203, 0.0218433, 0.0193961, 0.0091443, -0.0059383, -0.0185819, -0.0232682, -0.0204351, -0.0143607, -0.0077347, 0.0003984, 0.0111454, 0.0216, 0.0258095, 0.0203255, 0.0079849, -0.0045812, -0.0128937, -0.0173109, -0.0198988, -0.0200144, -0.0144752, -0.0022414, 0.0123267, 0.0221124, 0.0232215, 0.0176783, 0.0100843, 0.0025601, -0.0059975, -0.0159308, -0.0236594, -0.0238972, -0.0150644, -0.0015424, 0.0100536, 0.0164274, 0.0188433, 0.0192682, 0.0167297, 0.0086454, -0.0045605, -0.0175737, -0.0239403, -0.0215485, -0.0137571, -0.0051777, 0.0026951, 0.0109978, 0.0193615, 0.0238858, 0.0203166, 0.0089276, -0.0047784, -0.0146554, -0.0187154, -0.018975, -0.0172137, -0.0122836, -0.0023763, 0.010764, 0.0213435, 0.0239965, 0.0184162, 0.0089743, 4.3e-06, -0.0076419, -0.015041, -0.0212272, -0.0223333, -0.0153948, -0.0023885, 0.0105348, 0.0180872, 0.0196204, 0.0177218, 0.0139207, 0.0070431, -0.003852, -0.0159402, -0.0234245, -0.0223446, -0.0140934, -0.0036908, 0.0050558, 0.0119182, 0.0178475, 0.0214391, 0.019181, 0.0095394, -0.0040733, -0.0153439, -0.0201454, -0.0191134, -0.0152066, -0.0096681, -0.0014287, 0.0095756, 0.0197476, 0.0237311, 0.0191629, 0.0089204, -0.0017099, -0.009643, -0.0152247, -0.0192437, -0.0200394, -0.0147249, -0.0032148, 0.0099963, 0.0188971, 0.0207236, 0.0172615, 0.0116382, 0.004796, -0.0041281, -0.0143884, -0.021962, -0.0223119, -0.0147361, -0.0032916, 0.0068402, 0.0134333, 0.0173355, 0.0191613, 0.0171849, 0.0093403, -0.0031028, -0.0149789, -0.0209887, -0.0198264, -0.0142305, -0.0073029, 0.0003136, 0.0092096, 0.017962, 0.0224788, 0.0193328, 0.0094211, -0.0023845, -0.0113467, -0.016173, -0.0181232, -0.0176493, -0.0131385, -0.0034614, 0.0089524, 0.0186974, 0.021518, 0.017557, 0.0102615, 0.0025311, -0.0052764, -0.0134518, -0.0200765, -0.0213259, -0.0150694, -0.0036294, 0.0077045, 0.0149248, 0.0176852, 0.0175573, 0.0148575, 0.0082393, -0.0024632, -0.0139296, -0.0209354, -0.0205039, -0.0141182, -0.0056627, 0.002317, 0.0097247, 0.0165614, 0.0206244, 0.0186485, 0.0098753, -0.0022126, -0.0123074, -0.0173489, -0.017892, -0.0157142, -0.0110334, -0.002883, 0.0079927, 0.0177041, 0.0215862, 0.018069, 0.0097851, 0.0007756, -0.0068877, -0.0133431, -0.0183435, -0.0196227, -0.0147026, -0.0041631, 0.0076846, 0.0158883, 0.0184758, 0.0168062, 0.0127584, 0.0064899, -0.0025178, -0.0127357, -0.0200491, -0.020664, -0.0144372, -0.0048933, 0.0040401, 0.0108504, 0.0158807, 0.0187027, 0.0171976, 0.0098223, -0.0016372, -0.0124118, -0.0182285, -0.0182623, -0.0145388, -0.0089368, -0.0015887, 0.0075564, 0.0163682, 0.0208446, 0.0182897, 0.0099036, -0.0001971, -0.0084441, -0.0139319, -0.0171787, -0.0176575, -0.0135796, -0.0044011, 0.007102, 0.0160813, 0.0191988, 0.0167565, 0.0112779, 0.004549, -0.0032974, -0.0118725, -0.0186647, -0.0200926, -0.0146883, -0.004822, 0.005118, 0.0121295, 0.0159255, 0.0171706, 0.0153263, 0.0090753, -0.0011475, -0.0118523, -0.018472, -0.0187769, -0.0141034, -0.0072855, 6.57e-05, 0.0078036, 0.0151671, 0.0195025, 0.0179032, 0.0101604, -0.0004307, -0.0095285, -0.0148537, -0.0167149, -0.0158942, -0.0119245, -0.004051, 0.0064107, 0.0155696, 0.0194563, 0.017035, 0.0105212, 0.0028657, -0.004564, -0.0116072, -0.0172313, -0.0188748, -0.0144916, -0.0050676, 0.0054724, 0.0131279, 0.0164435, 0.0162691, 0.0134757, 0.0077367, -0.0011053, -0.0110155, -0.0180254, -0.0190154, -0.0141411, -0.0062934, 0.0016484, 0.0086148, 0.0144427, 0.0179493, 0.0168772, 0.0101482, -0.0002065, -0.0099536, -0.0156863, -0.0168157, -0.0146525, -0.0101262, -0.0031004, 0.0060053, 0.0146463, 0.0190853, 0.0172278, 0.0103341, 0.0017364, -0.0059307, -0.0119332, -0.0161288, -0.0173132, -0.0137068, -0.0052245, 0.0052747, 0.0135717, 0.01705, 0.0159721, 0.0120089, 0.0061159, -0.0016291, -0.0102983, -0.0170754, -0.0187259, -0.0142696, -0.0059152, 0.0028139, 0.0096753, 0.0142921, 0.0165712, 0.0154116, 0.0096354, 9.62e-05, -0.0097835, -0.0161009, -0.0171604, -0.014008, -0.0085589, -0.0017607, 0.006096, 0.0136862, 0.0181667, 0.0170225, 0.010391, 0.0012156, -0.0070355, -0.0126276, -0.0155455, -0.0157757, -0.0124295, -0.0049965, 0.0048538, 0.0134264, 0.0173949, 0.016045, 0.0110919, 0.0045726, -0.0026098, -0.0099791, -0.015952, -0.0178909, -0.0141494, -0.0059042, 0.0034183, 0.0106331, 0.0145917, 0.0156273, 0.0138347, 0.0086205, 0.000149, -0.0092742, -0.0159743, -0.0174047, -0.0138218, -0.0074611, -0.0003572, 0.0066698, 0.0130053, 0.0169635, 0.0163143, 0.0103538, 0.0011488, -0.0076606, -0.0133688, -0.0154524, -0.0145638, -0.010927, -0.0042955, 0.0045413, 0.0128601, 0.0172796, 0.0161687, 0.0106794, 0.0033926, -0.003777, -0.01013, -0.0149788, -0.0166901, -0.0135998, -0.0059445, 0.0035235, 0.011219, 0.0150688, 0.0151731, 0.0124602, 0.0072925, -0.0002121, -0.0087385, -0.0153832, -0.0172945, -0.0138186, -0.0068712, 0.0008075, 0.0075288, 0.0127503, 0.0157836, 0.015193, 0.0099833, 0.0012593, -0.0077839, -0.0138645, -0.0156435, -0.0138065, -0.0095006, -0.0032234, 0.0045612, 0.0121497, 0.0166967, 0.0160613, 0.0105569, 0.0026869, -0.004836, -0.0106372, -0.0143632, -0.0153989, -0.0126263, -0.0057639, 0.0033506, 0.0113386, 0.0154346, 0.0150816, 0.0114782, 0.0059234, -0.0009687, -0.0084376, -0.014554, -0.0167501, -0.0137149, -0.0066497, 0.0015607, 0.0084045, 0.0128896, 0.0148751, 0.0138873, 0.0092129, 0.0012686, -0.0075564, -0.0139586, -0.0158528, -0.0134578, -0.0083845, -0.0020095, 0.0049678, 0.0115675, 0.0158066, 0.0155811, 0.0104603, 0.002395, -0.0055679, -0.0112706, -0.0141463, -0.0142806, -0.0113966, -0.0052339, 0.0031623, 0.0110656, 0.0154848, 0.0151225, 0.0109156, 0.0047667, -0.0019478, -0.0084867, -0.0137458, -0.0158571, -0.0133196, -0.0065649, 0.0018896, 0.0090537, 0.0132451, 0.0143396, 0.0126517, 0.0081382, 0.0009983, -0.0072105, -0.0136517, -0.0158473, -0.0133344, -0.0076656, -0.0009028, 0.0056554, 0.0112784, 0.0148407, 0.0147407, 0.0101659, 0.0023286, -0.0058993, -0.0117871, -0.0142131, -0.0134865, -0.0101398, -0.0043783, 0.0031718, 0.010592, 0.0151578, 0.0150573, 0.0106474, 0.0039594, -0.0029229, -0.0088512, -0.0131607, -0.0148089, -0.0125821, -0.0063831, 0.0019284, 0.0093529, 0.0135924, 0.0141304, 0.0116755, 0.0069528, 0.0004079, -0.0069739, -0.0130849, -0.015515, -0.013213, -0.007285, -7.92e-05, 0.0064312, 0.0113121, 0.0140189, 0.0136861, 0.0095723, 0.0022658, -0.0059011, -0.0120232, -0.01437, -0.0130341, -0.0090661, -0.0033462, 0.0034711, 0.0101376, 0.0145351, 0.014734, 0.0104791, 0.0035062, -0.0036941, -0.0093788, -0.0128801, -0.0138216, -0.0115916, -0.0059624, 0.001873, 0.00931, 0.0137414, 0.0140922, 0.0110285, 0.0058674, -0.000406, -0.0069784, -0.0124543, -0.0148747, -0.0129129, -0.0070838, 0.0004032, 0.0070902, 0.0115654, 0.0134628, 0.012612, 0.0087079, 0.0020343, -0.0057349, -0.0119318, -0.0144178, -0.0128133, -0.008286, -0.0023303, 0.0040221, 0.0098653, 0.0137795, 0.0141152, 0.0102167, 0.0032907, -0.0041616, -0.009879, -0.0128564, -0.0130476, -0.0105118, -0.0052748, 0.0019113, 0.0090466, 0.013602, 0.0140358, 0.0106534, 0.0050234, -0.0012803, -0.0072429, -0.0119407, -0.0140516, -0.0123521, -0.0068738, 0.0006113, 0.0075015, 0.0118748, 0.0131803, 0.0116907, 0.0076996, 0.0015604, -0.0055869, -0.0115881, -0.0142345, -0.0126594, -0.0077964, -0.0014933, 0.0046966, 0.0098403, 0.0130739, 0.0132821, 0.009746, 0.0031461, -0.0043399, -0.0101981, -0.012959, -0.0125425, -0.0095196, -0.0044059, 0.0021505, 0.0087299, 0.0131985, 0.0138156, 0.0104166, 0.0044658, -0.0020442, -0.0076776, -0.0116438, -0.0132124, -0.0115633, -0.0065169, 0.0006818, 0.0076303, 0.0120757, 0.0130774, 0.011013, 0.0067128, 0.0008837, -0.0055884, -0.0111308, -0.0137949, -0.0124134, -0.0074984, -0.0009171, 0.0053319, 0.0100162, 0.0125418, 0.0123743, 0.0090499, 0.0029191, -0.0043319, -0.0102632, -0.0130278, -0.0122525, -0.0087303, -0.0034956, 0.0025992, 0.0085075, 0.0126369, 0.0133663, 0.0101599, 0.0041339, -0.0025908, -0.0081419, -0.0115631, -0.0124974, -0.010654, -0.0059547, 0.0007703, 0.0075437, 0.0120684, 0.0130128, 0.0105693, 0.005879, 0.0001129, -0.0057905, -0.0107121, -0.0131702, -0.0119817, -0.0072492, -0.0005825, 0.0058038, 0.0102823, 0.0122253, 0.011539, 0.0082029, 0.0025216, -0.0042813, -0.0101012, -0.0129451, -0.0120642, -0.0081768, -0.0026846, 0.0031809, 0.0084614, 0.0120602, 0.0127213, 0.009771, 0.003911, -0.0028968, -0.0085019, -0.0116192, -0.0119811, -0.009765, -0.0052254, 0.0009844, 0.0073611, 0.0118386, 0.0128577, 0.0102764, 0.0052652, -0.0006144, -0.00615, -0.0104339, -0.0124818, -0.0113574, -0.0069247, -0.0004026, 0.0060539, 0.0105042, 0.012077, 0.0108717, 0.0073265, 0.0019536, -0.0043073, -0.0098026, -0.01266, -0.0118464, -0.0078064, -0.0020617, 0.0037739, 0.0085851, 0.0115823, 0.0119733, 0.0092059, 0.0036685, -0.0030198, -0.0086749, -0.0116907, -0.0116504, -0.0090044, -0.0044234, 0.0013614, 0.0072074, 0.0114473, 0.0125349, 0.0100125, 0.0048509, -0.0011963, -0.0065712, -0.0103263, -0.0118495, -0.010604, -0.006453, -0.0002553, 0.006109, 0.0105855, 0.0119985, 0.0103941, 0.0065352, 0.0012802, -0.0044768, -0.0094881, -0.0122042, -0.0115064, -0.0075177, -0.0016406, 0.0042695, 0.0088109, 0.0112636, 0.0112402, 0.0084995, 0.0033175, -0.0030636, -0.0086548, -0.0116732, -0.0114341, -0.0084249, -0.0036631, 0.0018635, 0.0071677, 0.0109998, 0.0120448, 0.0096766, 0.0045609, -0.0015877, -0.0069436, -0.0103484, -0.011354, -0.0098269, -0.0058391, -4.29e-05, 0.0060476, 0.0104887, 0.0118829, 0.0100619, 0.0059032, 0.0006059, -0.0047804, -0.0092523, -0.0116588, -0.0110133, -0.0072064, -0.0013725, 0.0045992, 0.0090356, 0.0110903, 0.0106125, 0.0077348, 0.0028324, -0.0031297, -0.0084941, -0.0115041, -0.01123, -0.0080074, -0.0030297, 0.0024066, 0.0072619, 0.0105945, 0.0114422, 0.0092112, 0.004294, -0.0018128, -0.0071863, -0.0104145, -0.0110081, -0.0091201, -0.0051399, 0.0002871, 0.00597, 0.0102413, 0.0116527, 0.0097872, 0.0054377, 2.03e-05, -0.0051588, -0.0091418, -0.0111215, -0.010397, -0.0067984, -0.0011677, 0.0047624, 0.0091733, 0.0109997, 0.01013, 0.0070056, 0.0022455, -0.0032893, -0.0082834, -0.0111872, -0.0109545, -0.007686, -0.0025563, 0.0029006, 0.0074552, 0.0102978, 0.0108172, 0.0086236, 0.0039684, -0.001941, -0.007273, -0.0104376, -0.0107745, -0.0085446, -0.0044435, 0.000725, 0.0059556, 0.0099155, 0.0112871, 0.009485, 0.0050966, -0.0004219, -0.0055262, -0.0091441, -0.0106686, -0.0097313, -0.0062771, -0.0009429, 0.0048047, 0.0091749, 0.010908, 0.0097767, 0.0063845, 0.001631, -0.0035547, -0.0081037, -0.0107715, -0.0105637, -0.0073779, -0.0022228, 0.0032772, 0.0076733, 0.0101179, 0.0102481, 0.0079649, 0.0035446, -0.0020539, -0.0072272, -0.0103534, -0.0105778, -0.0081037, -0.0038255, 0.0012174, 0.0060389, 0.0095911, 0.0108125, 0.0090946, 0.004804, -0.0007212, -0.0058098, -0.0092032, -0.0103295, -0.0090933, -0.0056709, -0.0006408, 0.0048023, 0.0090449, 0.0107446, 0.0094939, 0.0058949, 0.001063, -0.0038953, -0.0080087, -0.0103321, -0.0100645, -0.0070139, -0.0019677, 0.0035182, 0.0078444, 0.0100211, 0.0097835, 0.0073081, 0.0030315, -0.0022187, -0.0071126, -0.010148, -0.0103478, -0.0077603, -0.0033283, 0.001694, 0.0062094, 0.0093328, 0.0102932, 0.0086051, 0.0044905, -0.0009196, -0.0059729, -0.009251, -0.0100906, -0.0085437, -0.005044, -0.0002527, 0.0048238, 0.008828, 0.0104772, 0.0092143, 0.0055196, 0.0005977, -0.0042478, -0.0080042, -0.0099359, -0.009504, -0.0065657, -0.0017241, 0.0036448, 0.0079166, 0.0099449, 0.0094261, 0.0067179, 0.0024775, -0.0024608, -0.0069951, -0.0098456, -0.0100362, -0.0074517, -0.0029513, 0.0020926, 0.0064173, 0.0091621, 0.0097936, 0.0080431, 0.0041112, -0.0010788, -0.006019, -0.0092278, -0.0099003, -0.0081002, -0.0044584, 0.0001925, 0.0049106, 0.0085857, 0.010112, 0.0088801, 0.0052055, 0.0002488, -0.0045518, -0.0080588, -0.0096201, -0.0089418, -0.0060411, -0.0014365, 0.0037112, 0.0078794, 0.0098298, 0.0091412, 0.0062264, 0.0019404, -0.0027703, -0.0069278, -0.0095006, -0.009635, -0.0071188, -0.0026565, 0.0023859, 0.0066063, 0.0090663, 0.0093633, 0.0074619, 0.0036592, -0.0012581, -0.0059892, -0.0091097, -0.0097035, -0.0077465, -0.0039594, 0.0006428, 0.0050676, 0.0083756, 0.0096916, 0.0084705, 0.0048971, -3.8e-06, -0.0047666, -0.0081217, -0.0093874, -0.0084335, -0.0054833, -0.0010856, 0.0037732, 0.0077564, 0.0096387, 0.0088785, 0.0058335, 0.0014726, -0.0031039, -0.0069277, -0.0091681, -0.0091698, -0.0067277, -0.0023932, 0.0025773, 0.0067271, 0.0090022, 0.0090164, 0.0069157, 0.0031626, -0.0014884, -0.0059339, -0.0089033, -0.0094531, -0.0074385, -0.0035586, 0.0010457, 0.0052655, 0.008226, 0.0092655, 0.0079938, 0.0045497, -0.0002048, -0.0048833, -0.0081416, -0.0092062, -0.0080036, -0.0049406, -0.0006822, 0.0038729, 0.0075921, 0.009365, 0.0085874, 0.0055051, 0.0010952, -0.003414, -0.006982, -0.0088874, -0.0086848, -0.0062729, -0.0021118, 0.0027028, 0.0067612, 0.0089226, 0.0087371, 0.0064394, 0.0026637, -0.001774, -0.0059016, -0.0086453, -0.0091324, -0.0071287, -0.0032362, 0.0013701, 0.0054626, 0.0081389, 0.0088801, 0.0074869, 0.0041476, -0.0004023, -0.0049249, -0.0080907, -0.0090351, -0.0076513, -0.0044563, -0.0002623, 0.0040246, 0.0074348, 0.0090334, 0.0082423, 0.0051993, 0.0008039, -0.0036613, -0.0070548, -0.0086709, -0.0082269, -0.0057806, -0.0017881, 0.002805, 0.0067163, 0.0087904, 0.0084895, 0.0060436, 0.0022078, -0.0020877, -0.0059145, -0.0083786, -0.0087516, -0.0067825, -0.0029559, 0.0016084, 0.0056157, 0.0080864, 0.0085555, 0.0069921, 0.0037032, -0.0006288, -0.0049276, -0.0079635, -0.0088323, -0.0073477, -0.0040481, 0.0001328, 0.0042146, 0.0073144, 0.0086803, 0.0078396, 0.0048754, 0.0005689, -0.0038297, -0.0071052, -0.0085026, -0.0078231, -0.0052861, -0.0014204, 0.002923, 0.0066228, 0.0085918, 0.0082325, 0.0057091, 0.0018193, -0.002395, -0.0059727, -0.0081401, -0.008341, -0.0063868, -0.0026765, 0.0017818, 0.0057027, 0.0080338, 0.0082891, 0.0065433, 0.0032455, -0.0008968, -0.004933, -0.0077829, -0.0085775, -0.0070568, -0.0037099, 0.0004719, 0.0044142, 0.0072422, 0.0083458, 0.0074025, 0.0045139, 0.0003527, -0.0039288, -0.007105, -0.0083535, -0.0074825, -0.0048277, -0.0010314, 0.003076, 0.0065185, 0.0083381, 0.0079397, 0.0054065, 0.0015038, -0.00266, -0.0060533, -0.0079476, -0.0079378, -0.0059544, -0.0023727, 0.0019207, 0.0057209, 0.0079472, 0.0080574, 0.0061566, 0.0028114, -0.0011921, -0.0049646, -0.0075798, -0.0082695, -0.0067468, -0.0034177, 0.0007414, 0.0045875, 0.0072026, 0.0080521, 0.0069621, 0.0041156, 0.0001252, -0.003983, -0.0070419, -0.0081887, -0.0071881, -0.0044255, -0.0006521, 0.0032609, 0.0064323, 0.0080543, 0.0076003, 0.0051011, 0.0012432, -0.0028644, -0.0061255, -0.0077962, -0.007569, -0.0055097, -0.0020349, 0.0020592, 0.005689, 0.0078094, 0.0078288, 0.0058243, 0.0024248, -0.001491, -0.0050309, -0.0073872, -0.007928, -0.0064007, -0.003139, 0.0009512, 0.0047126, 0.0071716, 0.007805, 0.0065489, 0.0036993, -0.0001304, -0.0040256, -0.0069281, -0.0079874, -0.0069147, -0.0040826, -0.0003105, 0.0034597, 0.0063796, 0.0077728, 0.0072269, 0.0047727, 0.001011, -0.0030078, -0.006163, -0.0076674, -0.0072483, -0.0050844, -0.0016758, 0.0022174, 0.005635, 0.0076226, 0.007579, 0.0055275, 0.0020977, -0.0017631, -0.0051187, -0.0072238, -0.0075809, -0.0060222, -0.0028503, 0.0011213, 0.0047799, 0.0071206, 0.0075911, 0.0061809, 0.0032928, -0.0004092, -0.004079, -0.0067841, -0.0077418, -0.0066348, -0.0037844, -2.32e-05, 0.0036449, 0.0063547, 0.0075152, 0.0068408, 0.0044149, 0.0007802, -0.0031055, -0.006151, -0.0075339, -0.0069681, -0.004698, -0.0013167, 0.0023999, 0.0055845, 0.0074019, 0.0072935, 0.0052382, 0.0018207, -0.0019903, -0.0052067, -0.0070931, -0.0072519, -0.0056263, -0.0025376, 0.0012798, 0.0048001, 0.0070319, 0.0073879, 0.0058584, 0.002918, -0.0006965, -0.0041562, -0.0066376, -0.0074627, -0.0063307, -0.0035074, 0.000212, 0.0037963, 0.0063424, 0.0072927, 0.0064678, 0.0040383, 0.0005333, -0.0031823, -0.0060931, -0.0073764, -0.0067127, -0.0043596, -0.0009816, 0.0025969, 0.0055547, 0.0071728, 0.0069772, 0.0049375, 0.0015763, -0.0021663, -0.0052724, -0.0069845, -0.0069575, -0.0052378, -0.0022063, 0.0014455, 0.0047915, 0.0069013, 0.007174, 0.0055709, 0.0025896, -0.0009685, -0.0042516, -0.0065066, -0.0071697, -0.0059999, -0.0032308, 0.0004067, 0.003901, 0.0063195, 0.0070989, 0.0061255, 0.0036626, 0.0002687, -0.0032583, -0.0060022, -0.0071836, -0.0064589, -0.0040619, -0.000687, 0.0027882, 0.005546, 0.0069543, 0.0066431, 0.0046152, 0.0013421, -0.0022988, -0.0053, -0.0068777, -0.0066963, -0.0048745, -0.0018697, 0.0016278, 0.0047762, 0.0067371, 0.0069343, 0.0052968, 0.0023046, -0.0012083, -0.0043516, -0.0063992, -0.0068833, -0.0056502, -0.0029395, 0.0005831, 0.0039632, 0.0062702, 0.0069194, 0.0058193, 0.0033064, -5.8e-06, -0.0033484, -0.0058999, -0.00696, -0.0061909, -0.0037889, -0.0004362, 0.0029566, 0.0055505, 0.00676, 0.0063123, 0.0042757, 0.0011017, -0.0024058, -0.0052882, -0.0067565, -0.0064597, -0.0045483, -0.0015471, 0.0018222, 0.0047705, 0.0065575, 0.0066684, 0.0050204, 0.0020528, -0.0014064, -0.0044378, -0.0063108, -0.0066192, -0.0053002, -0.0026337, 0.000757, 0.0039943, 0.0061862, 0.0067365, 0.0055445, 0.0029849, -0.0002724, -0.0034517, -0.0058024, -0.0067179, -0.0059019, -0.0035234, -0.0002235, 0.0030886, 0.0055505, 0.0065886, 0.0059999, 0.0039318, 0.0008495, -0.0025035, -0.0052435, -0.006608, -0.0062299, -0.0042568, -0.0012535, 0.0020158, 0.0047792, 0.0063787, 0.0063829, 0.0047292, 0.0018165, -0.0015657, -0.004496, -0.0062279, -0.0063807, -0.0049645, -0.0023201, 0.0009401, 0.0040119, 0.0060712, 0.006536, 0.0052863, 0.0026991, -0.0005171, -0.0035611, -0.0057199, -0.006474, -0.0055952, -0.0032507, -3.3e-05, 0.003184, 0.0055327, 0.0064319, 0.0057151, 0.0035986, 0.0005881, -0.0026067, -0.0051821, -0.0064328, -0.0059934, -0.0039907, -0.0009955, 0.0021946, 0.0047995, 0.0062145, 0.0060941, 0.0044237, 0.0015817, -0.0016973, -0.0045214, -0.0061371, -0.0061644, -0.0046561, -0.0020139, 0.0011321, 0.0040303, 0.0059372, 0.0063142, 0.0050317, 0.002445, -0.0007286, -0.0036626, -0.0056522, -0.0062428, -0.0052831, -0.0029681, 0.0001484, 0.0032483, 0.0054873, 0.0062764, 0.0054567, 0.0032899, 0.00033, -0.0027182, -0.0051173, -0.0062374, -0.0057414, -0.0037361, -0.0007715, 0.0023456, 0.0048186, 0.0060669, 0.0058146, 0.0041112, 0.0013404, -0.0018146, -0.0045159, -0.006026, -0.0059574, -0.0043759, -0.0017276, 0.0013256, 0.0040568, 0.0057969, 0.0060732, 0.0047681, 0.0022089, -0.0009069, -0.0037439, -0.005591, -0.0060296, -0.0049777, -0.0026773, 0.000333, 0.0032954, 0.0054144, 0.0061097, 0.0052154, 0.00301, 8.63e-05, -0.0028356, -0.0050604, -0.0060349, -0.0054739, -0.0034806, -0.0005709, 0.002466, 0.0048263, 0.005933, 0.0055543, 0.0038035, 0.001092, -0.0019308, -0.0044911, -0.0058926, -0.005749, -0.0041196, -0.0014693, 0.0015097, 0.004092, 0.0056635, 0.0058249, 0.004494, 0.0019797, -0.001058, -0.0037984, -0.0055262, -0.0058351, -0.0046912, -0.0023899, 0.0005226, 0.0033368, 0.005321, 0.0059264, 0.0049812, 0.0027581, -0.0001318, -0.0029482, -0.0050133, -0.005837, -0.0051985, -0.003219, -0.0003834, 0.0025574, 0.0048122, 0.0058023, 0.0053152, 0.0035123, 0.0008444, -0.0020504, -0.0044571, -0.0057391, -0.0055296, -0.0038769, -0.0012399, 0.0016729, 0.0041275, 0.0055408, 0.005579, 0.0042118, 0.0017483, -0.0011917, -0.0038254, -0.0054467, -0.0056502, -0.0044266, -0.0021151, 0.0007142, 0.0033808, 0.0052172, 0.0057256, 0.0047427, 0.0025249, -0.0003223, -0.0030466, -0.0049725, -0.0056509, -0.0049243, -0.0029504, -0.0001978, 0.0026298, 0.0047749, 0.0056652, 0.005092, 0.003243, 0.0006059, -0.0021741, -0.0044253, -0.0055753, -0.0052978, -0.0036376, -0.0010334, 0.0018114, 0.0041558, 0.0054293, 0.0053456, 0.0039306, 0.0015126, -0.0013195, -0.0038325, -0.005349, -0.0054673, -0.0041832, -0.0018618, 0.0008997, 0.0034299, 0.0051141, 0.0055153, 0.0044971, 0.0023019, -0.0004873, -0.0031236, -0.0049301, -0.005479, -0.0046623, -0.0026827, -1.08e-05, 0.002692, 0.0047174, 0.0055155, 0.0048776, 0.0029976, 0.0003867, -0.0022949, -0.0043985, -0.0054104, -0.005057, -0.0033954, -0.0008422, 0.0019238, 0.0041672, 0.0053216, 0.0051278, 0.0036596, 0.0012767, -0.0014465, -0.0038267, -0.0052327, -0.0052773, -0.0039533, -0.0016325, 0.0010701, 0.0034796, 0.0050164, 0.0053028, 0.0042436, 0.0020803, -0.0006342, -0.003177, -0.0048774, -0.005316, -0.0044162, -0.0024226, 0.0001777, 0.0027523, 0.0046468, 0.0053506, 0.0046624, 0.0027701, 0.0001898, -0.0024059, -0.0043765, -0.0052519, -0.0048139, -0.0031482, -0.0006564, 0.002017, 0.0041595, 0.0052108, 0.0049239, 0.0034047, 0.0010464, -0.0015757, -0.0038185, -0.0051045, -0.0050775, -0.0037297, -0.0014241, 0.0012207, 0.0035247, 0.0049268, 0.0050972, 0.003989, 0.001857, -0.0007715, -0.0032108, -0.0048103, -0.0051568, -0.0041879, -0.0021784, 0.0003622, 0.0028144, 0.0045724, 0.0051755, 0.0044427, 0.0025544, 1.57e-05, -0.0024998, -0.0043541, -0.0051034, -0.0045776, -0.0029005, -0.0004723, 0.0020972, 0.0041326, 0.0050902, 0.004729, 0.0031694, 0.0008303, -0.0017023, -0.0038114, -0.0049713, -0.0048687, -0.0035061, -0.0012316, 0.0013486, 0.0035568, 0.0048406, 0.0049022, 0.0037398, 0.0016334, -0.000905, -0.0032302, -0.0047264, -0.0049932, -0.0039725, -0.0019534, 0.0005357, 0.0028766, 0.0044992, 0.0049949, 0.0042163, 0.0023427, -0.0001405, -0.002574, -0.0043243, -0.0049621, -0.0043523, -0.0026569, -0.0002877, 0.0021726, 0.004092, 0.0049573, 0.0045356, 0.0029501, 0.0006317, -0.0018221, -0.0038071, -0.00484, -0.0046558, -0.003279, -0.0010469, 0.0014583, 0.0035733, 0.0047532, 0.0047185, 0.0035021, 0.0014132, -0.0010384, -0.0032437, -0.0046304, -0.0048223, -0.0037645, -0.0017469, 0.0006934, 0.0029354, 0.004431, 0.0048171, 0.0039876, 0.0021316, -0.0002854, -0.0026299, -0.0042831, -0.004825, -0.0041412, -0.0024248, -0.0001063, 0.0022465, 0.0040442, 0.0048141, 0.0043399, 0.0027431, 0.0004531, -0.0019284, -0.0038022, -0.0047146, -0.0044457, -0.0030512, -0.0008663, 0.0015538, 0.0035725, 0.0046586, 0.0045431, 0.0032797, 0.0012033, -0.0011692, -0.0032549, -0.0045266, -0.0046431, -0.0035584, -0.0015557, 0.0008322, 0.0029842, 0.004365, 0.0046456, 0.0037609, 0.0019205, -0.0004239, -0.0026709, -0.0042273, -0.0046854, -0.0039412, -0.0022075, 6.71e-05, 0.0023195, 0.0039942, 0.0046637, 0.0041387, 0.0025418, 0.0002909, -0.0020186, -0.003792, -0.0045947, -0.0042423, -0.0028251, -0.0006864, 0.001642, 0.0035579, 0.0045538, 0.0043703, 0.003071, 0.001007, -0.0012949, -0.0032669, -0.0044216, -0.0044588, -0.0033507, -0.0013735, 0.0009542, 0.0030204, 0.0042987, 0.0044826, 0.0035421, 0.0017117, -0.0005604, -0.0027037, -0.0041598, -0.0045406, -0.0037491, -0.0020061, 0.0002283, 0.0023894, 0.0039463, 0.0045129, 0.0039349, 0.0023428, 0.0001413, -0.002092, -0.0037729, -0.0044786, -0.0040497, -0.0026072, -0.0005096, 0.0017263, 0.0035342, 0.0044392, 0.0041969, 0.002874, 0.0008276, -0.0014099, -0.0032777, -0.004319, -0.0042742, -0.0031422, -0.0011972, 0.0010616, 0.0030411, 0.0042268, 0.0043265, 0.0033345, 0.0015104, -0.0006937, -0.0027316, -0.004083, -0.0043883, -0.0035601, -0.0018187, 0.0003736, 0.0024515, 0.0038993, 0.0043648, 0.0037308, 0.0021443, -1e-06, -0.002151, -0.0037411, -0.004361, -0.0038661, -0.0024002, -0.0003386, 0.0018084, 0.0035059, 0.0043168, 0.0040191, 0.0026835, 0.0006627, -0.0015117, -0.0032845, -0.0042198, -0.0040933, -0.0029341, -0.0010228, 0.0011604, 0.003049, 0.0041468, 0.0041735, 0.0031383, 0.0013193, -0.000823, -0.0027584, -0.0040027, -0.0042309, -0.0033709, -0.0016408, 0.0005039, 0.0025035, 0.0038521, 0.0042228, 0.0035316, 0.0019479, -0.0001391, -0.0022005, -0.0036986, -0.0042398, -0.0036902, -0.0022066, -0.0001773, 0.0018876, 0.0034771, 0.0041916, 0.0038388, 0.0024967, 0.0005106, -0.0015987, -0.0032839, -0.0041234, -0.00392, -0.0027317, -0.0008518, 0.0012535, 0.0030466, 0.0040578, 0.0040208, 0.0029528, 0.0011421, -0.0009435, -0.0027831, -0.0039219, -0.004071, -0.0031809, -0.0014696, 0.0006204, 0.002542, 0.0038004, 0.0040857, 0.0033404, 0.0017565, -0.0002737, -0.0022433, -0.0036461, -0.0041121, -0.0035177, -0.002025, -2.88e-05, 0.0019603, 0.0034478, 0.0040665, 0.0036567, 0.002311, 0.0003666, -0.0016723, -0.0032728, -0.0040263, -0.0037535, -0.002537, -0.0006849, 0.0013434, 0.003038, 0.0039608, 0.003865, 0.0027738, 0.0009778, -0.0010535, -0.0028045, -0.0038426, -0.0039123, -0.0029908, -0.0013015, 0.0007275, 0.0025687, 0.0037422, 0.0039518, 0.0031582, 0.001573, -0.0004047, -0.0022833, -0.0035886, -0.0039793, -0.0033462, -0.001853, 0.0001066, 0.0020245, 0.0034178, 0.003945, 0.0034776, 0.0021272, 0.0002278, -0.0017357, -0.0032517, -0.0039264, -0.003594, -0.0023531, -0.0005258, 0.0014297, 0.0030268, 0.0038598, 0.0037067, 0.0025994, 0.0008253, -0.0011506, -0.0028195, -0.003765, -0.0037586, -0.0028044, -0.0011368, 0.0008277, 0.0025848, 0.0036758, 0.0038185, 0.0029851, 0.0014007, -0.0005283, -0.0023203, -0.0035282, -0.0038428, -0.0031742, -0.001688, 0.0002291, 0.0020773, 0.003384, 0.0038266, 0.0033034, 0.0019469, 9.32e-05, -0.0017914, -0.0032208, -0.003821, -0.0034378, -0.0021794, -0.0003771, 0.0015106, 0.0030136, 0.0037565, 0.0035459, 0.0024264, 0.0006813, -0.0012355, -0.002826, -0.0036865, -0.0036097, -0.0026233, -0.0009756, 0.0009236, 0.0025938, 0.0036019, 0.003683, 0.0028182, 0.0012392, -0.0006435, -0.0023541, -0.0034679, -0.0037059, -0.0030018, -0.0015268, 0.0003424, 0.0021195, 0.003345, 0.0037111, 0.0031362, 0.0017726, -3.78e-05, -0.001843, -0.0031838, -0.0037107, -0.0032831, -0.0020146, -0.0002396, 0.0015845, 0.0029994, 0.0036548, 0.0033864, 0.0022553, 0.0005434, -0.0013101, -0.0028232, -0.0036061, -0.0034665, -0.0024508, -0.0008208, 0.0010157, 0.0025983, 0.0035228, 0.0035453, 0.0026562, 0.0010887, -0.0007476, -0.0023824, -0.0034077, -0.0035714, -0.0028317, -0.0013694, 0.0004476, 0.0021513, 0.0032985, 0.0035959, 0.0029764, 0.0016072, -0.0001626, -0.0018905, -0.0031425, -0.0035962, -0.0031284, -0.0018568, -0.0001136, 0.0016487, 0.0029814, 0.0035547, 0.0032296, 0.0020869, 0.00041, -0.0013765, -0.0028111, -0.0035208, -0.0033262, -0.0022864, -0.000674, 0.0011028, 0.0026, 0.0034401, 0.0034047, 0.002496, 0.0009463, -0.0008408, -0.0024034, -0.0033467, -0.00344, -0.0026649, -0.0012147, 0.0005482, 0.0021755, 0.0032451, 0.0034794, 0.0028223, 0.0014508, -0.0002805, -0.001935, -0.0030997, -0.00348, -0.0029732, -0.0017033, 3.3e-06, 0.0017033, 0.0029591, 0.0034566, 0.0030781, 0.0019231, 0.0002803, -0.0014379, -0.0027924, -0.0034311, -0.0031879, -0.0021303, -0.0005369, 0.0011839, 0.0025996, 0.0033574, 0.0032643, 0.0023378, 0.0008105, -0.0009241, -0.0024161, -0.0032839, -0.0033131, -0.0025048, -0.0010655, 0.0006443, 0.002194, 0.0031859, 0.0033607, 0.0026727, 0.001304, -0.0003887, -0.0019742, -0.0030558, -0.0033642, -0.0028191, -0.0015535, 0.0001122, 0.0017478, 0.0029302, 0.0033586, 0.0029321, 0.0017658, 0.0001558, -0.0014946, -0.0027683, -0.0033367, -0.0030495, -0.0019805, -0.0004097, 0.0012565, 0.0025958, 0.0032747, 0.0031248, 0.0021815, 0.0006793, -0.0009992, -0.0024202, -0.0032166, -0.0031885, -0.002351, -0.0009227, 0.000736, 0.0022088, 0.0031224, 0.0032393, 0.0025252, 0.0011648, -0.0004878, -0.0020074, -0.0030108, -0.0032501, -0.0026672, -0.0014064, 0.0002159, 0.0017849, 0.0028949, 0.0032597, 0.0027909, 0.0016161, 3.73e-05, -0.0015479, -0.0027416, -0.0032399, -0.0029107, -0.0018352, -0.0002912, 0.0013208, 0.002588, 0.0031931, 0.0029891, 0.0020288, 0.0005521, -0.0010686, -0.0024176, -0.0031456, -0.0030658, -0.0022044, -0.0007879, 0.0008221, 0.0022209, 0.0030574, 0.0031171, 0.0023799, 0.0010322, -0.0005774, -0.002033, -0.0029638, -0.003139, -0.0025201, -0.0012639, 0.0003146, 0.0018157, 0.0028535, 0.0031587, 0.0026541, 0.0014747, -7.31e-05, -0.0015962, -0.0027127, -0.0031421, -0.0027725, -0.0016936, -0.0001805, 0.0013758, 0.0025742, 0.0031114, 0.0028572, 0.001881, 0.0004295, -0.0011327, -0.0024089, -0.0030696, -0.002943, -0.0020634, -0.0006616, 0.0009011, 0.0022293, 0.0029911, 0.0029945, 0.0022358, 0.0009043, -0.0006591, -0.0020508, -0.002913, -0.0030296, -0.0023777, -0.0011263, 0.0004091, 0.001842, 0.0028074, 0.003055, 0.0025191, 0.0013402, -0.0001755, -0.0016392, -0.0026823, -0.0030447, -0.0026354, -0.0015546, -7.52e-05, 0.0014236, 0.0025548, 0.0030286, 0.0027293, 0.0017393, 0.000312, -0.0011933, -0.0023967, -0.0029908, -0.0028201, -0.0019269, -0.0005434, 0.0009726, 0.0022342, 0.0029252, 0.0028744, 0.0020946, 0.0007806, -0.0007347, -0.0020618, -0.0028585, -0.0029219, -0.0022414, -0.0009955, 0.0004985, 0.001865, 0.0027586, 0.00295, 0.0023865, 0.001212, -0.0002693, -0.0016755, -0.0026497, -0.002949, -0.0025015, -0.0014195, 2.49e-05, 0.0014646, 0.0025293, 0.002944, 0.0026051, 0.0016046, 0.0002013, -0.001249, -0.0023814, -0.0029099, -0.002697, -0.001794, -0.0004324, 0.0010359, 0.0022336, 0.0028584, 0.0027566, 0.0019571, 0.000661, -0.0008048, -0.0020667, -0.0027994, -0.0028141, -0.0021099, -0.0008719, 0.000582, 0.0018842, 0.0027078, 0.0028437, 0.0022546, 0.0010884, -0.0003557, -0.0017047, -0.0026135, -0.0028543, -0.0023711, -0.0012884, 0.000121, 0.001501, 0.0024987, 0.0028567, 0.0024827, 0.0014757, 9.75e-05, -0.0012999, -0.0023643, -0.0028285, -0.0025743, -0.0016636, -0.0003269, 0.0010922, 0.0022279, 0.0027908, 0.002642, 0.0018243, 0.0005457, -0.0008711, -0.0020676, -0.002737, -0.0027061, -0.0019826, -0.0007554, 0.0006588, 0.0019, 0.0026565, 0.0027387, 0.0021251, 0.0009688, -0.0004357, -0.0017275, -0.002574, -0.002761, -0.0022457, -0.001163, 0.0002122, 0.0015332, 0.0024649, 0.0027678, 0.0023623, 0.0013529, 1.4e-06, -0.0013447, -0.0023445, -0.0027475, -0.0024538, -0.0015365, -0.0002266, 0.0011417, 0.0022162, 0.0027212, 0.0025302, 0.0016971, 0.0004364, -0.0009326, -0.0020645, -0.002672, -0.0025975, -0.0018586, -0.0006456, 0.000728, 0.0019107, 0.0026037, 0.002635, 0.0019981, 0.0008532, -0.0005103, -0.0017439, -0.00253, -0.0026672, -0.0021241, -0.0010433, 0.0002983, 0.0015616, 0.0024283, 0.0026772, 0.0022426, 0.0012344, -8.79e-05, -0.0013833, -0.0023215, -0.002667, -0.0023357, -0.0014125, -0.0001302, 0.0011863, 0.0021997, 0.0026492, 0.0024202, 0.001575, 0.0003328, -0.0009898, -0.0020591, -0.0026056, -0.002489, -0.0017371, -0.0005412, 0.0007908, 0.0019166, 0.0025499, 0.0025336, 0.0018749, 0.0007414, -0.0005809, -0.0017561, -0.0024827, -0.0025734, -0.0020064, -0.0009301, 0.0003785, 0.0015867, 0.0023903, 0.0025867, 0.0021247, 0.00112, -0.0001708, -0.0014156, -0.0022953, -0.0025872, -0.0022213, -0.0012933, -3.84e-05, 0.0012263, 0.0021791, 0.002575, 0.0023118, 0.0014584, 0.0002363, -0.0010413, -0.0020508, -0.0025386, -0.0023816, -0.0016184, -0.0004418, 0.0008471, 0.001917, 0.0024941, 0.002434, 0.0017561, 0.0006346, -0.0006468, -0.0017638, -0.0024319, -0.0024786, -0.0018915, -0.0008227, 0.000452, 0.0016069, 0.0023504, 0.0024965, 0.0020084, 0.0010089, -0.0002487, -0.0014421, -0.0022646, -0.0025069, -0.00211, -0.0011789, 4.89e-05, 0.0012627, 0.0021554, 0.002499, 0.0022039, 0.0013458, 0.0001457, -0.0010874, -0.0020396, -0.0024713, -0.0022756, -0.0015023, -0.0003462, 0.0008984, 0.0019126, 0.002436, 0.002336, 0.0016416, 0.0005327, -0.0007087, -0.0017689, -0.0023794, -0.0023837, -0.0017791, -0.0007204, 0.0005196, 0.0016229, 0.0023094, 0.0024078, 0.0018952, 0.0009015, -0.0003221, -0.0014638, -0.0022308, -0.0024263, -0.0020021, -0.0010699, 0.0001308, 0.0012953, 0.0021297, 0.0024223, 0.0020976, 0.001237, 6.12e-05, -0.0011275, -0.0020249, -0.0024043, -0.0021725, -0.00139, -0.000255, 0.000945, 0.0019039, 0.0023756, 0.0022393, 0.0015318, 0.0004369, -0.0007654, -0.0017707, -0.0023255, -0.0022889, -0.001669, -0.000623, 0.0005809, 0.0016335, 0.0022662, 0.0023203, 0.0017853, 0.0007982, -0.0003911, -0.0014811, -0.0021932, -0.0023445, -0.0018966, -0.0009661, 0.0002071, 0.0013237, 0.0021017, 0.0023451, 0.0019921, 0.0011312, -1.85e-05, -0.0011621, -0.0020063, -0.0023365, -0.0020715, -0.0012815, -0.0001678, 0.0009878, 0.0018917, 0.002313, 0.0021429, 0.0014256, 0.0003464, -0.0008173, -0.0017698, -0.0022709, -0.002195, -0.001561, -0.0005292, 0.0006374, 0.0016396, 0.0022208, 0.002234, 0.0016789, 0.0006992, -0.0004563, -0.0014952, -0.0021535, -0.0022624, -0.0017933, -0.000867, 0.0002778, 0.001348, 0.0020722, 0.0022688, 0.001889, 0.0010288, -9.37e-05, -0.001192, -0.0019844, -0.0022684, -0.0019735, -0.0011778, -8.54e-05, 0.0010267, 0.0018771, 0.0022494, 0.0020477, 0.001323, 0.0002617, -0.0008636, -0.0017654, -0.0022159, -0.002103, -0.0014563, -0.0004395, 0.0006891, 0.0016413, 0.0021729, 0.0021485, 0.0015765, 0.0006053, -0.0005168, -0.001506, -0.0021117, -0.0021797, -0.0016919, -0.0007724, 0.0003427, 0.0013672, 0.0020403, 0.0021927, 0.0017881, 0.0009298, -0.0001646, -0.0012172, -0.0019586, -0.0021989, -0.0018773, -0.0010784, -7.9e-06, 0.0010617, 0.0018598, 0.0021844, 0.0019529, 0.0012231, 0.0001814, -0.0009047, -0.0017573, -0.0021598, -0.0020124, -0.0013545, -0.0003533, 0.0007371, 0.0016394, 0.0021228, 0.0020633, 0.0014771, 0.0005163, -0.0005729, -0.0015138, -0.0020688, -0.0020975, -0.0015924, -0.0006812, 0.000403, 0.0013824, 0.0020064, 0.0021176, 0.0016903, 0.0008346, -0.000232, -0.0012392, -0.0019305, -0.0021287, -0.0017833, -0.0009834, 6.46e-05, 0.0010929, 0.0018407, 0.0021198, 0.0018598, 0.0011263, 0.0001056, -0.0009412, -0.0017458, -0.0021032, -0.0019242, -0.0012566, -0.0002716, 0.0007811, 0.0016345, 0.002071, 0.0019786, 0.0013812, 0.0004324, -0.0006239, -0.0015184, -0.0020248, -0.0020163, -0.0014954, -0.0005939, 0.0004586, 0.001393, 0.0019699]}
\ No newline at end of file
packages/core/test/fixtures/fem_center_32.jsonadded+187−0View file
@@ -0,0 +1,187 @@
1+{
2+ "model": "Q4 Mindlin-Reissner FEM; sparse eigensolve; coupled Hunt-Crossley/Hertz impact",
3+ "config": {
4+ "width": 0.03,
5+ "height": 0.03,
6+ "thickness": 0.001,
7+ "density": 2700.0,
8+ "young": 68900000000.0,
9+ "poisson": 0.33,
10+ "material_q": 1400.0,
11+ "shear_correction": 0.8333333333333334,
12+ "mesh_x": 32,
13+ "mesh_y": 32,
14+ "hole_diameter": 0.0008,
15+ "hole_edge_offset": 0.0015,
16+ "thread_length": 0.2,
17+ "thread_dashpot": 0.00075,
18+ "hit_x": 0.0,
19+ "hit_y": 0.0,
20+ "hammer_mass": 0.01,
21+ "hammer_tip_radius": 0.002,
22+ "hammer_speed": 0.45,
23+ "hammer_young": 200000000000.0,
24+ "hammer_poisson": 0.29,
25+ "restitution": 0.62,
26+ "mic_distance": 0.35,
27+ "air_density": 1.204,
28+ "sound_speed": 343.0,
29+ "sample_rate": 96000,
30+ "duration": 4.0,
31+ "modes": 72,
32+ "normalize_dbfs": -1.0
33+ },
34+ "derived": {
35+ "plate_mass_kg": 0.002428642831973649,
36+ "fem_degrees_of_freedom": 3267,
37+ "retained_mode_count": 17,
38+ "low_frequency_momentum_modes": 3,
39+ "contact_duration_s": 0.00013865830364735113,
40+ "impact_impulse_Ns": 0.001625639289723404,
41+ "peak_contact_force_N": 24.613186280193073,
42+ "hammer_exit_velocity_m_s_positive_into_plate": 0.2874359540968044,
43+ "plate_contact_point_exit_velocity_m_s_positive_into_plate": 0.4408402370605666,
44+ "relative_separation_velocity_m_s": -0.15340428296376218,
45+ "effective_relative_restitution": 0.3408984065861382,
46+ "critical_frequency_hz": 12120.88938013311,
47+ "unnormalized_peak_pressure_pa_approx": 5.966473817498397e-08
48+ },
49+ "modes": [
50+ {
51+ "mode": 1,
52+ "frequency_hz": 3582.287001112292,
53+ "q_total": 410.6918254018363,
54+ "hit_participation": 2.1416398533238758e-08,
55+ "support_participation": 3.637252360529015e-08,
56+ "radiation_participation": -9.224476786801397e-14
57+ },
58+ {
59+ "mode": 2,
60+ "frequency_hz": 6068.324523496819,
61+ "q_total": 335.06232153520267,
62+ "hit_participation": -2.517904703383734e-06,
63+ "support_participation": -39.55865726303948,
64+ "radiation_participation": -1.2001423104176947e-09
65+ },
66+ {
67+ "mode": 3,
68+ "frequency_hz": 7312.682083943683,
69+ "q_total": 315.1810176439337,
70+ "hit_participation": 34.58179474711481,
71+ "support_participation": -2.8562979405684015,
72+ "radiation_participation": -5.96599318189293e-11
73+ },
74+ {
75+ "mode": 4,
76+ "frequency_hz": 9767.810860233025,
77+ "q_total": 281.2491096283973,
78+ "hit_participation": -6.221612539779135e-08,
79+ "support_participation": -2.103681652672187,
80+ "radiation_participation": -2.4643821185449583e-11
81+ },
82+ {
83+ "mode": 5,
84+ "frequency_hz": 9767.81114861093,
85+ "q_total": 279.7638816066191,
86+ "hit_participation": 1.0488253168555109e-06,
87+ "support_participation": 39.358076515178404,
88+ "radiation_participation": 4.608608801978225e-10
89+ },
90+ {
91+ "mode": 6,
92+ "frequency_hz": 17406.914209660066,
93+ "q_total": 221.33714866755537,
94+ "hit_participation": 33.417203175517116,
95+ "support_participation": -39.75251658651321,
96+ "radiation_participation": -1.465584752523123e-10
97+ },
98+ {
99+ "mode": 7,
100+ "frequency_hz": 18481.958791025747,
101+ "q_total": 216.30588315903455,
102+ "hit_participation": -1.3598816406457392e-06,
103+ "support_participation": 9.2804704355961,
104+ "radiation_participation": 3.034490514529505e-11
105+ },
106+ {
107+ "mode": 8,
108+ "frequency_hz": 18481.958808756768,
109+ "q_total": 216.24944262498366,
110+ "hit_participation": 1.6183823937309037e-06,
111+ "support_participation": -16.521246392849143,
112+ "radiation_participation": -5.402825935799842e-11
113+ },
114+ {
115+ "mode": 9,
116+ "frequency_hz": 20110.349639665797,
117+ "q_total": 208.72189070297895,
118+ "hit_participation": -3.088086663809264e-07,
119+ "support_participation": 1.012840251803962e-06,
120+ "radiation_participation": 5.72160719410774e-15
121+ },
122+ {
123+ "mode": 10,
124+ "frequency_hz": 22351.705539806568,
125+ "q_total": 199.51125815375912,
126+ "hit_participation": 4.3203260322793323e-07,
127+ "support_participation": -1.1824866612383344e-06,
128+ "radiation_participation": -1.0207580494834417e-14
129+ },
130+ {
131+ "mode": 11,
132+ "frequency_hz": 29180.37324343258,
133+ "q_total": 177.61878829854192,
134+ "hit_participation": -1.0003005754879143e-06,
135+ "support_participation": 34.76438528374521,
136+ "radiation_participation": 4.561384838330134e-11
137+ },
138+ {
139+ "mode": 12,
140+ "frequency_hz": 29180.373379653127,
141+ "q_total": 177.7369393705599,
142+ "hit_participation": 1.1263637935693132e-07,
143+ "support_participation": -17.13607822217792,
144+ "radiation_participation": -2.249037435981898e-11
145+ },
146+ {
147+ "mode": 13,
148+ "frequency_hz": 35231.08987577418,
149+ "q_total": 163.64123494295808,
150+ "hit_participation": 7.210048199788075e-07,
151+ "support_participation": 13.990845489294891,
152+ "radiation_participation": 1.2594133656654641e-11
153+ },
154+ {
155+ "mode": 14,
156+ "frequency_hz": 36669.405886688495,
157+ "q_total": 160.69777757185105,
158+ "hit_participation": 43.14405845972299,
159+ "support_participation": 33.02953777815046,
160+ "radiation_participation": 2.7444531038015485e-11
161+ },
162+ {
163+ "mode": 15,
164+ "frequency_hz": 38503.73472240475,
165+ "q_total": 157.34728250845308,
166+ "hit_participation": 2.128080785910041e-07,
167+ "support_participation": 3.9629501704529826,
168+ "radiation_participation": 2.9902472811510917e-12
169+ },
170+ {
171+ "mode": 16,
172+ "frequency_hz": 38503.734792473275,
173+ "q_total": 157.22716516133076,
174+ "hit_participation": 1.6035343871642584e-06,
175+ "support_participation": 39.772837375479796,
176+ "radiation_participation": 2.9970215977391935e-11
177+ },
178+ {
179+ "mode": 17,
180+ "frequency_hz": 41631.68337933795,
181+ "q_total": 151.97618432045638,
182+ "hit_participation": -2.859091546742526e-07,
183+ "support_participation": -1.2689837464322404e-07,
184+ "radiation_participation": 2.181248752583173e-15
185+ }
186+ ]
187+}
packages/core/test/fixtures/fem_center_32_wav.jsonadded+1−0View file
@@ -0,0 +1 @@
1+{"fs": 96000, "samples": [-4e-07, -0.6591011, 0.081188, 0.891251, 0.2220321, -0.7370016, -0.1588242, 0.5231723, 0.2468435, -0.2367002, 0.207611, 0.4610496, -0.0816645, -0.4961051, -0.3887327, 0.1259104, 0.5025739, 0.168737, -0.4505029, -0.6377739, -0.2155174, 0.4534019, 0.5745606, 0.0767535, -0.3746084, -0.3219176, 0.2380855, 0.5919171, 0.2501554, -0.3651079, -0.6639555, -0.2895387, 0.331661, 0.4307313, 0.0147258, -0.4307498, -0.3400171, 0.2741498, 0.6382752, 0.3851854, -0.2275369, -0.5763086, -0.2369556, 0.2803704, 0.3652537, -0.0567534, -0.5448323, -0.4264721, 0.1742972, 0.5915013, 0.4433789, -0.1424636, -0.4428676, -0.1310119, 0.3335851, 0.4273682, -0.0555974, -0.5795266, -0.5078396, 0.0205915, 0.480701, 0.3700127, -0.1578658, -0.3845475, -0.0828974, 0.4228985, 0.540382, 0.0395827, -0.4855046, -0.5104104, -0.052256, 0.3860033, 0.2451058, -0.2338562, -0.4496077, -0.1390598, 0.4371074, 0.58837, 0.1588272, -0.3417411, -0.4253867, 0.0041411, 0.3741304, 0.1992438, -0.2839201, -0.5597671, -0.2504722, 0.3357805, 0.526212, 0.2003565, -0.267343, -0.3252885, 0.1095957, 0.4419017, 0.2787356, -0.2473125, -0.5899177, -0.324732, 0.1961698, 0.410616, 0.1324598, -0.3062678, -0.3030136, 0.1435408, 0.5136907, 0.4029204, -0.1385743, -0.4971341, -0.3061304, 0.1371272, 0.3392457, 0.0319775, -0.3957057, -0.3810134, 0.0634646, 0.5050178, 0.4513689, -0.036973, -0.3622543, -0.2196881, 0.1972023, 0.363643, 0.0108763, -0.4337822, -0.4822927, -0.0629438, 0.4031609, 0.3834793, -0.0193304, -0.3042976, -0.1485222, 0.2956928, 0.4494253, 0.1023153, -0.3726938, -0.5022065, -0.1305915, 0.2859992, 0.2707763, -0.0886506, -0.3615324, -0.1625092, 0.3163184, 0.5097782, 0.2257411, -0.2552413, -0.4139176, -0.0950102, 0.2508523, 0.220519, -0.1657384, -0.459631, -0.2551215, 0.2238746, 0.4812149, 0.2686347, -0.1718013, -0.2980179, -0.0063056, 0.3176866, 0.2740742, -0.1606242, -0.4904125, -0.3454874, 0.0948601, 0.3795486, 0.1995131, -0.1801834, -0.2625208, 0.0420475, 0.4058093, 0.3751293, -0.0607641, -0.4157156, -0.3508382, 0.0373732, 0.2876869, 0.0964928, -0.2559035, -0.3346246, -0.0025688, 0.4124645, 0.4277416, 0.0527649, -0.2972045, -0.2663881, 0.0863348, 0.2815776, 0.0665654, -0.3140074, -0.4350818, -0.1075906, 0.3154562, 0.3841348, 0.0828667, -0.2327313, -0.1738631, 0.1756878, 0.3582257, 0.1411947, -0.2851369, -0.4590313, -0.1860233, 0.1950015, 0.2843273, 0.0128303, -0.265646, -0.1644411, 0.2064826, 0.4351568, 0.2507176, -0.1801257, -0.3788519, -0.1747306, 0.1531053, 0.21726, -0.0763694, -0.349311, -0.2489492, 0.1396666, 0.4273742, 0.2958739, -0.0824356, -0.2672301, -0.092194, 0.2133079, 0.240258, -0.0863347, -0.3932233, -0.3457908, 0.0278204, 0.3303644, 0.2418244, -0.0709149, -0.2230455, -0.0229341, 0.3005603, 0.3275587, 0.0020064, -0.3456507, -0.3608087, -0.0373094, 0.2263987, 0.1463854, -0.1436267, -0.2770281, -0.0396826, 0.3172087, 0.3936443, 0.1116756, -0.2393399, -0.2797814, -0.0092493, 0.2050238, 0.1021127, -0.2177173, -0.3669026, -0.133947, 0.2367723, 0.3718663, 0.1469251, -0.160828, -0.1812898, 0.0727873, 0.2753997, 0.1504752, -0.2086735, -0.3999007, -0.2223016, 0.1254262, 0.2773358, 0.0856019, -0.170916, -0.1585766, 0.1214405, 0.3577336, 0.246332, -0.1127168, -0.3394667, -0.225519, 0.0756848, 0.1940974, -0.002826, -0.2480674, -0.2293778, 0.0816361, 0.3619563, 0.3017527, -0.0106763, -0.2362112, -0.1454021, 0.1193876, 0.195999, -0.0266792, -0.3077535, -0.32151, -0.0188184, 0.2749917, 0.2672495, 0.0109979, -0.1784278, -0.0636288, 0.2010769, 0.2756025, 0.0412214, -0.2816754, -0.3447428, -0.0949597, 0.171356, 0.1766635, -0.0560088, -0.2102632, -0.0626159, 0.232508, 0.3506153, 0.141106, -0.1830354, -0.2765811, -0.0840327, 0.1393702, 0.1153002, -0.1353088, -0.2922529, -0.1457875, 0.1747894, 0.3430705, 0.185152, -0.0946674, -0.1806311, -0.0049506, 0.1969236, 0.1404265, -0.1410471, -0.3389948, -0.2352575, 0.0726926, 0.2546175, 0.1395847, -0.0898892, -0.1439398, 0.0590788, 0.2771519, 0.2275476, -0.0600108, -0.2987311, -0.2481216, 0.0115674, 0.1643973, 0.0540686, -0.1619661, -0.1953297, 0.039615, 0.2942812, 0.2932656, 0.0381074, -0.2014573, -0.1759011, 0.0377922, 0.1520103, 0.0136535, -0.2308426, -0.2807273, -0.0514604, 0.2244276, 0.2742026, 0.0691956, -0.1306755, -0.090703, 0.1161344, 0.2212243, 0.0594264, -0.2206548, -0.3157196, -0.1334075, 0.1257043, 0.1874228, 0.014209, -0.1446757, -0.0745798, 0.1632127, 0.2979849, 0.151181, -0.1324431, -0.2644755, -0.1344, 0.0816463, 0.1141635, -0.065171, -0.2213332, -0.1418104, 0.1259226, 0.3027658, 0.2058587, -0.0418971, -0.1719296, -0.0602956, 0.1229849, 0.1219934, -0.0866433, -0.2791945, -0.2276038, 0.0313916, 0.2260796, 0.1755635, -0.0256944, -0.120657, 0.0119298, 0.2004497, 0.2002808, -0.0245143, -0.2552239, -0.2512554, -0.038878, 0.1354711, 0.0922899, -0.0886144, -0.1534138, 0.0087991, 0.2322429, 0.2711165, 0.0679389, -0.1648627, -0.1920549, -0.0265075, 0.1097703, 0.0373945, -0.1608079, -0.2335788, -0.0697832, 0.1807707, 0.2645783, 0.1099243, -0.0867274, -0.1069509, 0.048342, 0.1648392, 0.0644922, -0.1657008, -0.2802041, -0.1520399, 0.0871133, 0.1852653, 0.0689442, -0.0869697, -0.0754947, 0.1066481, 0.2404875, 0.1488037, -0.0922537, -0.2445745, -0.1639773, 0.0316466, 0.1065673, -0.0097334, -0.1563938, -0.1251781, 0.0862968, 0.258738, 0.2111725, -0.0035659, -0.1560211, -0.099312, 0.0581861, 0.0996325, -0.0465428, -0.2203926, -0.2068595, 0.001052, 0.1970922, 0.1937393, 0.024201, -0.0936397, -0.0236841, 0.1330486, 0.1660998, -0.0026008, -0.2110031, -0.2420469, -0.0735573, 0.1083162, 0.1147804, -0.0265684, -0.1109254, -0.0111634, 0.1774829, 0.238196, 0.08423, -0.1312106, -0.1968187, -0.0729913, 0.0696437, 0.0504651, -0.0998356, -0.1852339, -0.0745669, 0.1425257, 0.244262, 0.135767, -0.0506178, -0.1130427, -0.0047624, 0.1101097, 0.0616987, -0.1198985, -0.2400892, -0.1549176, 0.0551159, 0.1762417, 0.1076658, -0.0382541, -0.0681367, 0.0600555, 0.1841221, 0.1365467, -0.0626004, -0.2184742, -0.1782984, -0.0081965, 0.0957623, 0.0310192, -0.0975421, -0.101797, 0.0553356, 0.2153051, 0.202815, 0.0229667, -0.1366985, -0.1253907, 0.0055782, 0.0747525, -0.0178126, -0.164581, -0.1789706, -0.0177838, 0.1686943, 0.1975434, 0.0616729, -0.0678076, -0.0485277, 0.0758966, 0.1277976, 0.0099845, -0.1697151, -0.2237748, -0.0934475, 0.0831649, 0.1263622, 0.0232741, -0.0714185, -0.0213777, 0.1291983, 0.19965, 0.0897576, -0.1030582, -0.1917228, -0.104499, 0.0339209, 0.0567783, -0.0497581, -0.1375547, -0.0694879, 0.1094393, 0.2183703, 0.1477226, -0.0225161, -0.1114735, -0.045658, 0.0610744, 0.0532488, -0.0831555, -0.197419, -0.1470275, 0.0308625, 0.1631193, 0.1315873, 0.0018879, -0.0567926, 0.0228193, 0.1321547, 0.1165644, -0.0412624, -0.1895106, -0.1809106, -0.036728, 0.0828903, 0.0597488, -0.0460552, -0.0761029, 0.033051, 0.1737179, 0.184448, 0.0397916, -0.1174708, -0.1399949, -0.0349641, 0.0493988, 0.0027231, -0.1144737, -0.1470144, -0.0265942, 0.1413782, 0.1914015, 0.0871248, -0.0453328, -0.0639019, 0.0282102, 0.0892933, 0.0155654, -0.1332746, -0.1986257, -0.1016249, 0.0614137, 0.1303281, 0.0604804, -0.0360409, -0.0247219, 0.0873315, 0.159699, 0.0861522, -0.0803305, -0.1793975, -0.1237861, 0.0048624, 0.0580934, -0.0101403, -0.0922763, -0.0585208, 0.0822286, 0.1895917, 0.1478999, -0.0015806, -0.1055412, -0.0754482, 0.0196311, 0.0409319, -0.0538453, -0.1551132, -0.1317595, 0.0145606, 0.147189, 0.1436067, 0.0332197, -0.0444992, -0.0053869, 0.0855858, 0.0920433, -0.0264987, -0.160574, -0.1739741, -0.0549502, 0.0694243, 0.0791798, -0.0034178, -0.0503626, 0.0181292, 0.1347422, 0.1600803, 0.0479779, -0.0998668, -0.1449285, -0.0647602, 0.0261163, 0.0168679, -0.0713786, -0.1131207, -0.0280811, 0.1163073, 0.1785021, 0.1013309, -0.0266948, -0.0722011, -0.0104026, 0.0535438, 0.0156075, -0.1017528, -0.1693111, -0.1009263, 0.0443014, 0.1284576, 0.0862056, -0.0056973, -0.0240647, 0.0523309, 0.1206653, 0.0756357, -0.0623087, -0.1628107, -0.1326443, -0.0168538, 0.0558302, 0.0204972, -0.0514451, -0.0443279, 0.0609132, 0.1594838, 0.1393471, 0.0126026, -0.0975481, -0.095106, -0.0139887, 0.0270362, -0.030694, -0.1155146, -0.1113845, 0.0050243, 0.1298763, 0.1465297, 0.0554961, -0.0327066, -0.0257947, 0.0449327, 0.0661, -0.0173042, -0.1330768, -0.1597486, -0.0645932, 0.0570381, 0.0910738, 0.03001, -0.0260339, 0.0085534, 0.0994371, 0.1326252, 0.0487845, -0.0842475, -0.1426206, -0.0848938, 0.0066036, 0.0258191, -0.0352461, -0.079527, -0.0245981, 0.0944293, 0.1608832, 0.1060734, -0.0121605, -0.0756956, -0.0401179, 0.0220133, 0.0118494, -0.0747608, -0.1385186, -0.0934017, 0.0319614, 0.1222445, 0.1022779, 0.0186632, -0.0212566, 0.0240771, 0.0839201, 0.0608397, -0.0485741, -0.1440982, -0.1327281, -0.0316985, 0.0516434, 0.0434079, -0.0164826, -0.0286523, 0.0445988, 0.1295434, 0.1248421, 0.0204484, -0.0886616, -0.1062024, -0.0399725, 0.0136077, -0.0129149, -0.0797836, -0.0880437, 0.0006888, 0.1126281, 0.1424152, 0.0692751, -0.0222478, -0.0400636, 0.0107459, 0.0408765, -0.0126065, -0.107568, -0.1407283, -0.0671499, 0.0467466, 0.0967674, 0.0548548, -0.0044768, 0.0026459, 0.0685709, 0.1040231, 0.0440184, -0.0708129, -0.1352899, -0.0963239, -0.0085455, 0.030907, -0.0056444, -0.0482111, -0.0178273, 0.0759021, 0.1402633, 0.1034653, -0.0019784, -0.0759006, -0.0615671, -0.0046201, 0.0061517, -0.0520701, -0.1081672, -0.0808697, 0.0237888, 0.1132544, 0.1103326, 0.0366279, -0.0174489, 0.0018047, 0.0506116, 0.044001, -0.038717, -0.1245291, -0.1259892, -0.0404093, 0.0469307, 0.059543, 0.0121347, -0.0129954, 0.0321895, 0.1011231, 0.1064851, 0.0228657, -0.0795969, -0.1105338, -0.0585618, 0.0019034, 0.0003227, -0.0483739, -0.063822, 0.0002494, 0.0963663, 0.1329465, 0.0757469, -0.0138235, -0.0495848, -0.0167545, 0.0176871, -0.0110605, -0.0844194, -0.1190775, -0.0639477, 0.0387999, 0.0976451, 0.0720288, 0.0132715, -0.0007521, 0.0422846, 0.0758194, 0.03555, -0.0597303, -0.1245501, -0.1002907, -0.0193071, 0.0334774, 0.0178208, -0.0204421, -0.0091683, 0.0603859, 0.1182668, 0.0953633, 0.0039768, -0.0738243, -0.0758309, -0.0259458, -2.11e-05, -0.0334194, -0.0794363, -0.0651851, 0.0190525, 0.1027978, 0.1117616, 0.0484662, -0.0135643, -0.0153946, 0.0216248, 0.0267292, -0.032037, -0.1049861, -0.1143956, -0.0437342, 0.042508, 0.0698913, 0.0344981, 0.0013276, 0.0228038, 0.0750917, 0.085965, 0.0210955, -0.0709465, -0.1096525, -0.0702612, -0.0074539, 0.0100082, -0.0215131, -0.0403885, 0.0026033, 0.0814554, 0.1197322, 0.0762296, -0.0078722, -0.0553795, -0.0378499, -0.0025587, -0.0113785, -0.0639123, -0.0963949, -0.0563976, 0.0330758, 0.0950438, 0.0824316, 0.0267458, -0.002558, 0.0203282, 0.0493227, 0.0249318, -0.0509143, -0.111595, -0.0982666, -0.0258536, 0.0345596, 0.0356221, 0.0031458, 8.23e-05, 0.0474623, 0.0962101, 0.0833376, 0.0062547, -0.0702903, -0.0840732, -0.0418201, -0.0057169, -0.0183319, -0.0531144, -0.0480448, 0.0170696, 0.0917342, 0.1079233, 0.0548266, -0.0103452, -0.0282935, -0.0026084, 0.0102144, -0.0276707, -0.0861895, -0.0996182, -0.0425471, 0.0387493, 0.0755328, 0.0509133, 0.0133998, 0.0157257, 0.0518503, 0.0647633, 0.01636, -0.0631013, -0.1048052, -0.0759094, -0.0141608, 0.017063, 0.0007248, -0.0188749, 0.0066868, 0.0679886, 0.1042329, 0.0719565, -0.0044374, -0.0582913, -0.0531262, -0.0192267, -0.0125693, -0.0461053, -0.0738613, -0.0459414, 0.0293434, 0.0900357, 0.087006, 0.0359554, -0.0035732, 0.0023558, 0.0254849, 0.0133955, -0.0440157, -0.0974178, -0.0916906, -0.0285305, 0.0348135, 0.0484057, 0.0222824, 0.0088491, 0.0367494, 0.0749945, 0.0688087, 0.0055994, -0.065972, -0.0873451, -0.0524528, -0.0103261, -0.0062183, -0.0297635, -0.0307936, 0.0171337, 0.0805886, 0.1001579, 0.0564741, -0.0082347, -0.0375639, -0.0220593, -0.0046173, -0.0248275, -0.0686522, -0.0830001, -0.0378692, 0.0358151, 0.0774752, 0.0618281, 0.0227439, 0.0102937, 0.0315439, 0.0441126, 0.0097233, -0.056182, -0.097062, -0.0765126, -0.0181243, 0.0221926, 0.0184847, -7.9e-06, 0.0115119, 0.0559681, 0.0876236, 0.0641037, -0.0032655, -0.0590681, -0.0632544, -0.0320081, -0.013917, -0.0308455, -0.052398, -0.0338708, 0.0272861, 0.0834048, 0.0867782, 0.0411685, -0.0044149, -0.0119913, 0.0048611, 0.0019575, -0.0385863, -0.0828356, -0.0818329, -0.0278885, 0.0346601, 0.0569016, 0.0369244, 0.0163833, 0.0278547, 0.0552039, 0.0530559, 0.0027646, -0.0613235, -0.0866014, -0.0583369, -0.0134584, 0.0034637, -0.0097083, -0.0144271, 0.0185087, 0.0697105, 0.0896713, 0.0542231, -0.0073758, -0.0438448, -0.0369313, -0.0171095, -0.0228711, -0.0526472, -0.0656484, -0.0307413, 0.0337354, 0.0765559, 0.0678489, 0.029201, 0.0059218, 0.0141931, 0.0249239, 0.0021137, -0.0501179, -0.087366, -0.0730798, -0.0194718, 0.0259109, 0.0320998, 0.0157603, 0.016276, 0.0453247, 0.0707905, 0.0538052, -0.0039663, -0.0583184, -0.0689198, -0.0409128, -0.014897, -0.0178888, -0.032697, -0.0212469, 0.0264921, 0.075742, 0.0827799, 0.0427994, -0.0054643, -0.0231077, -0.0122974, -0.0085652, -0.0342031, -0.0684514, -0.0698041, -0.0246139, 0.034374, 0.0618087, 0.0472379, 0.0222328, 0.0203733, 0.0372251, 0.0371455, -0.0015341, -0.0565906, -0.0827353, -0.0601074, -0.0149266, 0.0111592, 0.0069625, 0.0003265, 0.0205126, 0.0593349, 0.077471, 0.0489328, -0.0077118, -0.0477227, -0.0475508, -0.0268979, -0.0212951, -0.0382747, -0.0484757, -0.0221115, 0.0324135, 0.07346, 0.0696958, 0.0328182, 0.0021638, -0.000282, 0.0078063, -0.0056608, -0.0447604, -0.0765047, -0.066565, -0.0185212, 0.0286051, 0.0419936, 0.0282084, 0.0203978, 0.0359148, 0.0544032, 0.0420958, -0.0060996, -0.0564834, -0.0708239, -0.0461773, -0.0151486, -0.0069851, -0.0152122, -0.0089179, 0.0265063, 0.0675088, 0.0759618, 0.041376, -0.0069025, -0.0314156, -0.0259408, -0.0175841, -0.0304954, -0.0546727, -0.0565783, -0.0194219, 0.034101, 0.0637544, 0.0535721, 0.0261673, 0.013943, 0.0212972, 0.0219009, -0.0066247, -0.0518862, -0.0765596, -0.0584515, -0.0147413, 0.0172344, 0.0203335, 0.0129609, 0.0225848, 0.0495873, 0.0643821, 0.0414648, -0.0090734, -0.0496886, -0.0543344, -0.0338696, -0.019703, -0.0255427, -0.0321894, -0.0128001, 0.0316528, 0.0687623, 0.0681202, 0.0337999, -0.0012742, -0.0120525, -0.0068636, -0.012941, -0.0399402, -0.0650953, -0.0578606, -0.0157044, 0.0305581, 0.0486119, 0.0373285, 0.0234884, 0.0275582, 0.0389661, 0.0298566, -0.0092015, -0.0538668, -0.0696654, -0.0481741, -0.0144795, 0.0021025, -0.000188, 0.0024536, 0.0269007, 0.0590519, 0.0671632, 0.0375047, -0.008775, -0.0373228, -0.0361682, -0.0247384, -0.0271441, -0.041767, -0.0429822, -0.0129921, 0.0338709, 0.0633036, 0.0563954, 0.028128, 0.0082833, 0.0075278, 0.0079284, -0.0119046, -0.0472515, -0.0687753, -0.0540732, -0.0130669, 0.0219922, 0.0305923, 0.0231798, 0.0242995, 0.040508, 0.0510789, 0.0326226, -0.0112203, -0.0501311, -0.0577661, -0.0380951, -0.0178171, -0.0144115, -0.0172954, -0.0035002, 0.0312128, 0.0629412, 0.0638485, 0.0324767, -0.0045629, -0.0213457, -0.01889, -0.0192393, -0.0354846, -0.0536088, -0.0477799, -0.0114962, 0.03195, 0.0524002, 0.0432837, 0.0253166, 0.0200841, 0.0248358, 0.0178026, -0.0128109, -0.0506796, -0.0661032, -0.0473599, -0.0128493, 0.0095973, 0.0122863, 0.0123841, 0.0273128, 0.0506134, 0.0571154, 0.0318165, -0.0110335, -0.041193, -0.0431999, -0.0298581, -0.0238915, -0.0299084, -0.0296804, -0.0059438, 0.0336322, 0.0609628, 0.0562323, 0.0282017, 0.0031966, -0.0040824, -0.0043484, -0.0168748, -0.0426836, -0.0599672, -0.0476675, -0.0101621, 0.0256684, 0.037989, 0.0308738, 0.0253533, 0.0320903, 0.0380995, 0.0231136, -0.0138677, -0.0493574, -0.0583607, -0.0397759, -0.0154822, -0.0048063, -0.0041277, 0.0052257, 0.0308565, 0.0563805, 0.0575602, 0.029263, -0.0077809, -0.0284076, -0.0282291, -0.0242331, -0.0312313, -0.0424048, -0.0370333, -0.0063739, 0.0328739, 0.0537955, 0.0463537, 0.0257897, 0.0133538, 0.0122391, 0.0064943, -0.0165045, -0.0470688, -0.0607313, -0.0442429, -0.0103326, 0.0157, 0.0222329, 0.0205623, 0.0274546, 0.042357, 0.0464453, 0.0249184, -0.0135603, -0.0433424, -0.0473439, -0.0329229, -0.0205576, -0.0191999, -0.01718, 0.0011797, 0.033293, 0.0571754, 0.0536246, 0.0265893, -0.0014447, -0.0136045, -0.0146641, -0.0211548, -0.0381526, -0.050617, -0.0398895, -0.0063354, 0.0284315, 0.0428164, 0.0360831, 0.0255556, 0.0243113, 0.0258524, 0.0135384, -0.0167176, -0.0476126, -0.0566292, -0.0392081, -0.0126495, 0.003368, 0.007117, 0.0129524, 0.0303737, 0.0493813, 0.0498771, 0.0246103, -0.0109339, -0.0334826, -0.0349622, -0.0277427, -0.0270492, -0.0317531, -0.0262134, -0.0007927, 0.0333637, 0.0532131, 0.0468912, 0.0249355, 0.0072638, 0.0012971, -0.0036502, -0.0199242, -0.0431335, -0.0540736, -0.0393496, -0.0070804, 0.0205781, 0.0297579, 0.0268302, 0.0271144, 0.0343958, 0.0356731, 0.0173663, -0.0161922, -0.0440458, -0.0489594, -0.03403, -0.0170418, -0.0096886, -0.0058513, 0.0079252, 0.0327501, 0.0523185, 0.0491079, 0.0235688, -0.0057102, -0.0211532, -0.0228925, -0.02448, -0.0336202, -0.0411189, -0.0313284, -0.0019165, 0.0303952, 0.0453908, 0.0389582, 0.024821, 0.0171462, 0.014631, 0.0043875, -0.0194888, -0.0450919, -0.0530573, -0.0367518, -0.0093524, 0.0102129, 0.0163534, 0.0193892, 0.0295923, 0.0421794, 0.041352, 0.0189705, -0.0139718, -0.0368067, -0.0392635, -0.029709, -0.0228505, -0.0218482, -0.0157974, 0.0048349, 0.0334196, 0.0510362, 0.045291, 0.0228761, 0.0017437, -0.0079465, -0.0123451, -0.0227896, -0.0389403, -0.0465862, -0.0331942, -0.0032933, 0.0243652, 0.0350306, 0.0311557, 0.0261585, 0.0268117, 0.0252154, 0.0096493, -0.0187486, -0.0435417, -0.0484284, -0.0333681, -0.0133123, -0.0013826, 0.0040545, 0.0139394, 0.0319055, 0.0467095, 0.0431936, 0.0194597, -0.0096195, -0.0268704, -0.0290234, -0.0266906, -0.0290573, -0.0317882, -0.0224949, 0.0027673, 0.0316342, 0.0460345, 0.0397279, 0.0231563, 0.0105749, 0.0046324, -0.0039569, -0.0219393, -0.0419504, -0.0480957, -0.0327995, -0.0056839, 0.0158254, 0.0235868, 0.0243659, 0.0283867, 0.0349621, 0.0324612, 0.0127727, -0.0168088, -0.0386045, -0.0413708, -0.0301728, -0.018593, -0.0128229, -0.0061542, 0.0101578, 0.0330259, 0.0476091, 0.0419663, 0.0198001, -0.0032444, -0.0155064, -0.0194238, -0.0248995, -0.0345398, -0.0386595, -0.0262554, 0.0008022, 0.0271663, 0.0382632, 0.0336065, 0.0245292, 0.0196662, 0.0153933, 0.0021808, -0.0210545, -0.0420355, -0.0461358, -0.0311903, -0.0093937, 0.0057353, 0.0123886, 0.0189695, 0.0306786, 0.0406163, 0.0363519, 0.0145962, -0.0131538, -0.030915, -0.0331366, -0.0277211, -0.0244533, -0.0228705, -0.0138165, 0.0074126, 0.0321987, 0.0450614, 0.0386745, 0.0206412, 0.0045862, -0.004024, -0.0112193, -0.0238749, -0.0383136, -0.0421536, -0.0277486, -0.001778, 0.0202942, 0.0288931, 0.0278161, 0.0266829, 0.0278802, 0.0236012, 0.0064065, -0.0193431, -0.0390854, -0.0415604, -0.0292558, -0.0142754, -0.0047615, 0.002447, 0.0148957, 0.032163, 0.0432385, 0.0373279, 0.0159357, -0.0077111, -0.0214416, -0.0248218, -0.0261291, -0.0299789, -0.0306202, -0.0189618, 0.0049738, 0.0290662, 0.0396923, 0.0343268, 0.022235, 0.0130075, 0.0064443, -0.004707, -0.0229554, -0.0397048, -0.0424566, -0.0277871, -0.0053532, 0.0116972, 0.0190893, 0.0228562, 0.0290161, 0.0342646, 0.0290021, 0.0093067, -0.0162704, -0.0334553, -0.0353788, -0.0275872, -0.0198199, -0.0145522, -0.0056379, 0.0117546, 0.0321244, 0.0427702, 0.0361114, 0.0174093, -0.0008202, -0.0112821, -0.0172212, -0.025155, -0.0342897, -0.0355946, -0.0219816, 0.0022078, 0.0237007, 0.032401, 0.0297595, 0.0244572, 0.0210549, 0.0150934, 0.0002102, -0.0214735, -0.0384427, -0.0401303, -0.0271369, -0.0099314, 0.0022873, 0.0098267, 0.018841, 0.0308185, 0.0381963, 0.0317664, 0.0115314, -0.0116456, -0.0258442, -0.0285571, -0.0264258, -0.0253072, -0.0227362, -0.0116841, 0.0090022, 0.0301359, 0.0395658, 0.0335166, 0.0193391, 0.0068769, -0.0014666, -0.0107646, -0.0243279, -0.0367055, -0.0377451, -0.0234646, -0.0012882, 0.0165463, 0.0241667, 0.0255237, 0.0268965, 0.0278449, 0.0215063, 0.0038971, -0.0189154, -0.0346613, -0.0359447, -0.0263714, -0.0151898, -0.0069699, 0.0017768, 0.0155783, 0.0314426, 0.0394404, 0.0323634, 0.0136297, -0.005634, -0.0171378, -0.0218709, -0.0256944, -0.0299766, -0.0287344, -0.0158519, 0.0061085, 0.0261209, 0.0342748, 0.0302852, 0.0217323, 0.0145857, 0.0071889, -0.0055379, -0.0231096, -0.0368543, -0.0373842, -0.0240333, -0.0056223, 0.0083028, 0.0158838, 0.0218576, 0.0289943, 0.0327209, 0.0256413, 0.0068377, -0.0150248, -0.0288286, -0.0307133, -0.0257998, -0.0205812, -0.0152217, -0.0047305, 0.012693, 0.0304377, 0.0381331, 0.0314122, 0.0159451, 0.0013112, -0.008238, -0.015821, -0.0250845, -0.0331783, -0.0323269, -0.0185267, 0.0026865, 0.0203338, 0.0276861, 0.0269682, 0.024331, 0.0215169, 0.0141687, -0.0013614, -0.0210351, -0.0347021, -0.0350601, -0.0242058, -0.0106145, -0.0002193, 0.0082393, 0.0187215, 0.0301867, 0.0353297, 0.0277505, 0.0094907, -0.0098382, -0.021628, -0.0251497, -0.0254607, -0.0254673, -0.0218424, -0.0096737, 0.009766, 0.0276269, 0.0347031, 0.0295355, 0.018569, 0.0085553, 7.43e-05, -0.0106213, -0.0241814, -0.0344849, -0.0336206, -0.0201817, -0.001428, 0.0132874, 0.0205845, 0.0238749, 0.0267113, 0.0270201, 0.0192729, 0.0020937, -0.0178206, -0.0305246, -0.031424, -0.0243135, -0.015865, -0.0082442, 0.0016543, 0.0158862, 0.0300316, 0.0356382, 0.0282689, 0.012185, -0.0036559, -0.0138209, -0.0197759, -0.0251771, -0.0292522, -0.0264941, -0.0132616, 0.0064509, 0.0231173, 0.0297556, 0.0272465, 0.0213608, 0.0154147, 0.007236, -0.0062404, -0.0225836, -0.0337427, -0.0329675, -0.0212568, -0.0061603, 0.0056387, 0.0136296, 0.0210758, 0.0283984, 0.0306722, 0.022576, 0.0051867, -0.0134139, -0.024821, -0.0270997, -0.0244695, -0.0208523, -0.0151421, -0.0037152, 0.0130391, 0.02829, 0.0338876, 0.0276896, 0.015058, 0.0030323, -0.0061216, -0.0148858, -0.0246438, -0.0314885, -0.0291234, -0.0158241, 0.0025603, 0.0172614, 0.0239496, 0.0248816, 0.0240105, 0.021274, 0.0129385, -0.0024854, -0.0200071, -0.0310712, -0.030858, -0.0220708, -0.0112293, -0.0019293, 0.0072873, 0.0184598, 0.0289791, 0.0323144, 0.0243464, 0.0082074, -0.0079948, -0.0182109, -0.0225924, -0.0245956, -0.0250477, -0.020503, -0.0079314, 0.0098897, 0.0249599, 0.0305135, 0.0264618, 0.0180528, 0.0096505, 0.0009001, -0.010559, -0.0235292, -0.0319428, -0.0299143, -0.0177112, -0.0019022, 0.010573, 0.0178882, 0.0225844, 0.0261301, 0.025678, 0.0171173, 0.0009044, -0.0163438, -0.0268095, -0.0278112, -0.0227768, -0.0162213, -0.0088153, 0.0018049, 0.0158113, 0.028183, 0.0320352, 0.0249485, 0.0113106, -0.0019269, -0.0113213, -0.0182351, -0.0244799, -0.0280097, -0.0241547, -0.0111952, 0.0062504, 0.0202602, 0.0260443, 0.0249167, 0.020952, 0.0156369, 0.0068699, -0.0067148, -0.0215664, -0.0306129, -0.0292069, -0.0192047, -0.0067499, 0.0036337, 0.0120436, 0.0203332, 0.0273469, 0.0283809, 0.0198977, 0.004166, -0.0116814, -0.0214392, -0.0242871, -0.0233659, -0.0206783, -0.0145738, -0.0027646, 0.0129006, 0.0259304, 0.0301192, 0.0247523, 0.0144938, 0.0043169, -0.0046985, -0.014184, -0.0238586, -0.0294571, -0.0261428, -0.0137631, 0.0020807, 0.0145767, 0.0210069, 0.0232377, 0.0234462, 0.0205337, 0.0116205, -0.0031849, -0.0186164, -0.0277025, -0.0274104, -0.0204712, -0.0116625, -0.0030046, 0.0067179, 0.0179954, 0.0273821, 0.0293516, 0.0215238, 0.0074499, -0.006278, -0.0154911, -0.020626, -0.0237008, -0.0241839, -0.0189511, -0.0065124, 0.0095551, 0.0223327, 0.0269673, 0.0240597, 0.0176114, 0.0102402, 0.0012535, -0.0104462, -0.0224919, -0.0292969, -0.0266738, -0.0158669, -0.002504, 0.0083877, 0.0158509, 0.0214654, 0.0252088, 0.0240396, 0.0151607, 0.0002103, -0.0146998, -0.0235657, -0.0249207, -0.0215441, -0.016252, -0.0088903, 0.0020474, 0.0153998, 0.0261031, 0.0287442, 0.0222789, 0.0107843, -0.0005109, -0.0094658, -0.0170295, -0.0235786, -0.0264329, -0.0218835, -0.0096045, 0.0057118, 0.0176641, 0.02302, 0.0230669, 0.020422, 0.0154021, 0.0062969, -0.0069335, -0.0202293, -0.027622, -0.0260483, -0.0176646, -0.0072618, 0.0021836, 0.0109028, 0.0195347, 0.0259682, 0.0260347, 0.0176263, 0.0036019, -0.0099885, -0.0186347, -0.0220615, -0.0223436, -0.0201385, -0.0137203, -0.0019687, 0.012399, 0.0235386, 0.0268427, 0.0224206, 0.0140771, 0.005193, -0.0037689, -0.0135685, -0.0227925, -0.0272692, -0.023464, -0.0122168, 0.0014336, 0.0123043, 0.0186836, 0.0218534, 0.0226454, 0.019474, 0.0103506, -0.0035228, -0.0170434, -0.0246729, -0.0245885, -0.0192117, -0.0118709, -0.0036017, 0.0063535, 0.0173264, 0.0255586, 0.0265656, 0.019215, 0.007032, -0.0047747, -0.0133494, -0.0190532, -0.0227176, -0.0230112, -0.0173495, -0.005416, 0.0089214, 0.0198677, 0.0239962, 0.022137, 0.0171421, 0.0104241, 0.0013202, -0.0102206, -0.0211912, -0.0266973, -0.0238912, -0.014484, -0.0031005, 0.006675, 0.014284, 0.0204043, 0.0240265, 0.0222725, 0.0134578, -0.0001107, -0.0130385, -0.0207862, -0.0225837, -0.0204684, -0.0159926, -0.0086404, 0.0022718, 0.014722, 0.0239476, 0.025811, 0.0201314, 0.0104449, 0.0005841, -0.0080963, -0.0160092, -0.0224925, -0.0246737, -0.0197775, -0.0084136, 0.004994, 0.0153793, 0.0205556, 0.0215271, 0.0197434, 0.0148476, 0.0056555, -0.0069138, -0.0187166, -0.0248601, -0.0234108, -0.0164681, -0.0076294, 0.0011767, 0.0100423, 0.018644, 0.024383, 0.0237589, 0.0157371, 0.0033458, -0.0084323, -0.016333, -0.0202526, -0.0213227, -0.019325, -0.0127319, -0.0013605, 0.0116509, 0.0212351, 0.0240309, 0.02054, 0.0136976, 0.0057187, -0.0031723, -0.0129559, -0.0215244, -0.0250618, -0.0211121, -0.0110641, 0.0007463, 0.0104247, 0.016827, 0.0206089, 0.0216477, 0.0182387, 0.0092045, -0.003576, -0.0154206, -0.0220048, -0.0222663, -0.0181532, -0.0118568, -0.0038589, 0.0060767, 0.0164828, 0.0236379, 0.024022, 0.0173358, 0.0068135, -0.0035173, -0.0116661, -0.0177312, -0.0216324, -0.0216489, -0.0158029, -0.0046083, 0.0081177, 0.0176301, 0.0215141, 0.0205442, 0.0165948, 0.0103035, 0.0012341, -0.0098658, -0.0197363, -0.0242386, -0.0215249, -0.0134244, -0.0036139, 0.0053604, 0.0130408, 0.0193415, 0.0226671, 0.0204949, 0.0120167, -0.0001708, -0.0114574, -0.0184317, -0.0206587, -0.0194585, -0.015499, -0.008198, 0.0024219, 0.0138565, 0.0218276, 0.0232382, 0.0183868, 0.0101833, 0.0013834, -0.0070795, -0.0150799, -0.0212641, -0.0228502, -0.0178835, -0.0075395, 0.0042124, 0.0134136, 0.0185326, 0.0201799, 0.0189254, 0.014091, 0.0050328, -0.0066967, -0.0171405, -0.0223696, -0.0212064, -0.0154904, -0.0078317, 0.0005062, 0.0093461, 0.0176604, 0.0226947, 0.0216293, 0.0141828, 0.0032802, -0.0070605, -0.0144494, -0.0187308, -0.0202674, -0.0183272, -0.0117129, -0.0009383, 0.0107566, 0.0190918, 0.0216318, 0.0189843, 0.0132931, 0.0059639, -0.002786, -0.012306, -0.0201316, -0.0229269, -0.0190765, -0.0101975, 9.87e-05, 0.0088936, 0.0153114, 0.0194312, 0.0205058, 0.0169343, 0.008215, -0.0034236, -0.0138395, -0.0196861, -0.0203307, -0.0172027, -0.0116476, -0.0038892, 0.0058171, 0.0155107, 0.0217172, 0.0217447, 0.0157998, 0.006693, -0.0025047, -0.0103345, -0.0165625, -0.0204592, -0.0201943, -0.014371, -0.0040387, 0.0072432, 0.0156456, 0.019433, 0.0191714, 0.0159558, 0.0099701, 0.0010847, -0.0093937, -0.0182173, -0.0219739, -0.0195184, -0.0125808, -0.0040078, 0.0043657, 0.0120127, 0.018254, 0.0212086, 0.0187854, 0.0108197, -6.37e-05, -0.0100127, -0.016447, -0.0190331, -0.0184652, -0.0148336, -0.0076612, 0.0024767, 0.0128768, 0.0198153, 0.0210019, 0.0169438, 0.0099325, 0.0019308, -0.0063078, -0.0141869, -0.0199438, -0.0210474, -0.016214, -0.0069038, 0.0034436, 0.0117464, 0.016848, 0.0189475, 0.0179956, 0.0132254, 0.0044769, -0.0063314, -0.0155822, -0.0201578, -0.0193493, -0.0146433, -0.0078748, 7.89e-05, 0.0087373, 0.0166031, 0.0209844, 0.0196837, 0.0129067, 0.0033166, -0.0058883, -0.0129012, -0.0174021, -0.0191698, -0.0172224, -0.0107292, -0.0006789, 0.0097965, 0.0171447, 0.0195847, 0.017656, 0.0128346, 0.0059974, -0.0025232, -0.0116085, -0.0186827, -0.0209212, -0.0173265, -0.0095285, -0.0004636, 0.0076572, 0.0140394, 0.0182827, 0.0192746, 0.0156363, 0.0073855, -0.0031362, -0.0123578, -0.0176853, -0.0186878, -0.0163038, -0.0112829, -0.0037804, 0.0055381, 0.0144593, 0.0198642, 0.0197309, 0.0145291, 0.006604, -0.0017142, -0.0092641, -0.0154856, -0.0192258, -0.0187228, -0.0130823, -0.0036541, 0.0063674, 0.0139122, 0.0176722, 0.0179428, 0.0152339, 0.0095019, 0.0009288, -0.0088295, -0.0167034, -0.0199244, -0.0178113, -0.0118737, -0.0042732, 0.003617, 0.0111226, 0.0171411, 0.0197163, 0.0171896, 0.0098343, 0.0001372, -0.0087291, -0.0147716, -0.0176209, -0.0174661, -0.0140554, -0.0070971, 0.0024375, 0.0118445, 0.0179516, 0.019064, 0.0157198, 0.0096551, 0.0022753, -0.0057005, -0.013304, -0.0185803, -0.0193217, -0.0147588, -0.0064369, 0.0027353, 0.0103439, 0.0154171, 0.0177833, 0.0169888, 0.0123193, 0.0040075, -0.005868, -0.0140963, -0.0182117, -0.0177641, -0.0138685, -0.0077801, -0.0001804, 0.0081702, 0.0155011, 0.0193121, 0.0179338, 0.0118517, 0.0033931, -0.0049093, -0.0116156, -0.0162029, -0.0180402, -0.0160736, -0.0098186, -0.0005503, 0.0088311, 0.0154048, 0.0178289, 0.0164846, 0.0123163, 0.0058817, -0.0023241, -0.01087, -0.0172339, -0.0190752, -0.0158231, -0.0089893, -0.0009221, 0.0066601, 0.0129385, 0.0171481, 0.0180041, 0.0143938, 0.0067039, -0.002772, -0.0110061, -0.0159612, -0.0172628, -0.0154257, -0.0108057, -0.0035967, 0.0052257, 0.0133735, 0.0181209, 0.0179609, 0.0134573, 0.0065062, -0.0011132, -0.0083828, -0.014464, -0.0179639, -0.017287, -0.0119423, -0.003404, 0.0055364, 0.0124114, 0.0161614, 0.0168079, 0.0144478, 0.0089597, 0.0007972, -0.0082041, -0.0152428, -0.0180905, -0.0163455, -0.0112474, -0.0044174, 0.0030516, 0.0103201, 0.0160146, 0.0182403, 0.0157292, 0.0090226, 0.0003773, -0.0076114, -0.0133483, -0.0163608, -0.0164577, -0.0132151, -0.0065482, 0.0023189, 0.0108094, 0.0162554, 0.0173814, 0.014652, 0.0093355, 0.0024647, -0.0051993, -0.012424, -0.0172164, -0.0177071, -0.0134965, -0.0060836, 0.002112, 0.0091666, 0.0141751, 0.016663, 0.0159414, 0.0114216, 0.0036272, -0.0053496, -0.0127153, -0.0165064, -0.0163884, -0.0131309, -0.0075762, -0.0003307, 0.0076199, 0.0143844, 0.0177182, 0.0163749, 0.0109673, 0.0034698, -0.0041038, -0.0105305, -0.0150923, -0.0168961, -0.0149286, -0.0089982, -0.0005189, 0.0079017, 0.0138652, 0.0163087, 0.0154207, 0.0117457, 0.0056686, -0.0021516, -0.0101056, -0.015826, -0.0173981, -0.0145249, -0.0085298, -0.0012746, 0.005851, 0.0119573, 0.0160265, 0.0167351, 0.0132345, 0.0061486, -0.0023761, -0.009796, -0.0144709, -0.015999, -0.0145549, -0.0102552, -0.003382, 0.00488, 0.0122913, 0.01651, 0.0164068, 0.0125308, 0.0063781, -0.0006665, -0.0076363, -0.0134804, -0.0167038, -0.0159215, -0.0109432, -0.003245, 0.0047768, 0.011117, 0.0148442, 0.0157369, 0.0136217, 0.0083885, 0.000703, -0.007548, -0.0138677, -0.0164599, -0.0150712, -0.0106663, -0.004458, 0.0026181, 0.0095741, 0.0148921, 0.0168177, 0.0144101, 0.0083483, 0.0006196, -0.006651, -0.0121264, -0.0152103, -0.0154468, -0.012354, -0.0060388, 0.0021403, 0.009807, 0.0147289, 0.0159122, 0.0136949, 0.0089725, 0.0025419, -0.0047641, -0.0115494, -0.0158857, -0.01622, -0.0124003, -0.0058013, 0.0015823, 0.0081747, 0.0130726, 0.0155761, 0.0148851, 0.0105629, 0.0033281, -0.0048116, -0.0114549, -0.0150113, -0.0151721, -0.0124116, -0.0072927, -0.0004148, 0.0070758, 0.0132798, 0.0162267, 0.0149913, 0.0102102, 0.0035232, -0.0034477, -0.0095969, -0.0140456, -0.0157573, -0.0138201, -0.0082707, -0.0005531, 0.0070343, 0.0125107, 0.0149763, 0.0144317, 0.0111363, 0.0053985, -0.0019851, -0.0093339, -0.0144879, -0.0158876, -0.0133934, -0.0081154, -0.0015293, 0.0051868, 0.0110618, 0.0149246, 0.0154991, 0.0121713, 0.0056955, -0.0019811, -0.0087267, -0.0131744, -0.0148555, -0.0136901, -0.0096654, -0.0031645, 0.0045091, 0.011242, 0.0150403, 0.0150385, 0.0117096, 0.0062125, -0.0003403, -0.0069848, -0.0125284, -0.0154719, -0.0146464, -0.0100704, -0.0031427, 0.0041008, 0.0099993, 0.0136758, 0.0147139, 0.0127794, 0.00782, 0.0006487, -0.0068868, -0.0125949, -0.0150132, -0.0139464, -0.0101086, -0.0044156, 0.0022762, 0.0088668, 0.013791, 0.0154717, 0.0132269, 0.0077788, 0.0008413, -0.0058316, -0.0110631, -0.0141417, -0.0144441, -0.0115023, -0.0055797, 0.0019223, 0.0088606, 0.0133644, 0.0146178, 0.0128163, 0.0085721, 0.002542, -0.0043697, -0.0106887, -0.0146124, -0.0148642, -0.0114422, -0.005559, 0.0011441, 0.0073323, 0.0120752, 0.0145214, 0.0138456, 0.0097605, 0.0030966, -0.0042812, -0.0103191, -0.0136958, -0.014078, -0.0117033, -0.0069563, -0.0004621, 0.0065374, 0.0122101, 0.0148495, 0.0137633, 0.0095464, 0.0035422, -0.0029149, -0.0087776, -0.0130508, -0.0146428, -0.0127696, -0.0076313, -0.0006268, 0.006243, 0.0113201, 0.0137935, 0.0134988, 0.0105048, 0.0051018, -0.0018146, -0.0085722, -0.0132369, -0.0145328, -0.0123954, -0.007725, -0.0017003, 0.0046314, 0.0102302, 0.0138525, 0.0143182, 0.0112066, 0.0053217, -0.0016081, -0.0077884, -0.0120356, -0.013804, -0.0128357, -0.0090631, -0.0029606, 0.004124, 0.0102453, 0.0137097, 0.0138267, 0.0109642, 0.0060103, -0.0001048, -0.0064008, -0.0116084, -0.0142877, -0.0134702, -0.0093058, -0.0030713, 0.0035107, 0.0090297, 0.0126231, 0.0137314, 0.0119408, 0.0072738, 0.0006298, -0.0062413, -0.0114317, -0.0137277, -0.0129383, -0.0095633, -0.0043111, 0.0019971, 0.0081909, 0.012727, 0.0142161, 0.0121681, 0.0072862, 0.0010289, -0.0051346, -0.0101248, -0.0131385, -0.0134624, -0.0106809, -0.005173, 0.0016836, 0.0079845, 0.0121487, 0.013466, 0.0119959, 0.008145, 0.0024927, -0.0040016, -0.0098523, -0.013413, -0.0136359, -0.0105972, -0.0053363, 0.0007894, 0.0066087, 0.0111592, 0.0135026, 0.0128427, 0.0090225, 0.0029181, -0.0037763, -0.0093037, -0.0125315, -0.013079, -0.0110056, -0.0065902, -0.0004921, 0.006008, 0.0111913, 0.0135894, 0.01267, 0.0089506, 0.0035242, -0.0024809, -0.008045, -0.0121021, -0.013568, -0.0117888, -0.0070699, -0.0007193, 0.0055324, 0.0102717, 0.0127294, 0.0126108, 0.009867, 0.0047991, -0.0016378, -0.0078352, -0.0120811, -0.0133184, -0.0115031, -0.0073454, -0.0018029, 0.0041574, 0.0094496, 0.0128196, 0.013206, 0.0103359, 0.0050063, -0.0012701, -0.0069683, -0.0110252, -0.0128256, -0.0119988, -0.0084678, -0.0027775, 0.0037369, 0.0093143, 0.0125105, 0.0127453, 0.0102743, 0.005777, 6.45e-05, -0.0058669, -0.0107244, -0.0131654, -0.012394, -0.0086312, -0.0030123, 0.0030025, 0.0081829, 0.0116624, 0.0127883, 0.0111229, 0.0067613, 0.0006386, -0.0056266, -0.0103775, -0.0125816, -0.0120223, -0.0090269, -0.0041635, 0.0017601, 0.0075437, 0.0117129, 0.0130563, 0.01122, 0.0068498, 0.0011783, -0.0045402, -0.0092852, -0.0121917, -0.0125133, -0.0099032, -0.0048158, 0.0014395, 0.0071845, 0.0110654, 0.0124302, 0.0112213, 0.0077025, 0.0024147, -0.003652, -0.0090491, -0.0122957, -0.0125258, -0.0098432, -0.0051206, 0.0005068, 0.0059787, 0.0103089, 0.0125246, 0.0118893, 0.0083495, 0.0027785, -0.0033085, -0.0083998, -0.0114927, -0.0121557, -0.0103213, -0.0062124, -0.0005159, 0.0054934, 0.010234, 0.0124428, 0.0116909, 0.0084038, 0.0034713, -0.0021248, -0.0073799, -0.0111988, -0.0125443, -0.0108823, -0.006575, -0.0008152, 0.0049014, 0.0093445, 0.0117614, 0.0117631, 0.0092359, 0.004504, -0.0014565, -0.0071343, -0.0110229, -0.0122284, -0.010695, -0.006971, -0.0018523, 0.0037444, 0.008714, 0.0118352, 0.0121696, 0.0095513, 0.0047328, -0.0009725, -0.0062512, -0.0101194, -0.0119091, -0.0111872, -0.0078937, -0.0026174, 0.0033578, 0.0084552, 0.0114317, 0.0117719, 0.0096272, 0.0055209, 0.0001872, -0.0053717, -0.0098814, -0.0121133, -0.0114138, -0.0080305, -0.0029544, 0.0025685, 0.0074363, 0.0107766, 0.0118861, 0.0103378, 0.0062878, 0.0006667, -0.005052, -0.0094268, -0.0115541, -0.0111798, -0.0084994, -0.0039887, 0.0015514, 0.0069261, 0.0107567, 0.011992, 0.0103678, 0.0064534, 0.0012892, -0.0040306, -0.0085248, -0.0112962, -0.0116053, -0.009176, -0.0045019, 0.0012017, 0.0064613, 0.0100979, 0.0114894, 0.0104851, 0.0072552, 0.002322, -0.0033181, -0.008287, -0.0112642, -0.0115229, -0.0091621, -0.0049049, 0.0002844, 0.005423, 0.0095149, 0.0115927, 0.0109932, 0.0077387, 0.0026654, -0.0028841, -0.0075965, -0.0105591, -0.0112954, -0.0096555, -0.0058359, -0.0005395, 0.0049996, 0.0093446, 0.0114027, 0.0108082, 0.007894, 0.0033889, -0.0018287, -0.0067692, -0.0103419, -0.0115794, -0.0100497, -0.0061353, -0.0009047, 0.0043452, 0.0085195, 0.010873, 0.0109546, 0.0086226, 0.0042242, -0.0012742, -0.0064769, -0.0100592, -0.0112466, -0.0099549, -0.0066001, -0.0018624, 0.0033772, 0.0080198, 0.0109053, 0.0112113, 0.0088433, 0.0044883, -0.0007167, -0.0056223, -0.0092993, -0.0110471, -0.0104078, -0.0073489, -0.002479, 0.002995, 0.0076693, 0.0104606, 0.0108883, 0.0090148, 0.0052502, 0.0002774, -0.004909, -0.0090833, -0.0111352, -0.0105224, -0.0074891, -0.0028902, 0.0021993, 0.0067719, 0.0099546, 0.0110271, 0.009593, 0.0058538, 0.000706, -0.0045232, -0.0085719, -0.0106276, -0.0103984, -0.0079831, -0.0037991, 0.0013628, 0.0063403, 0.0098639, 0.0110194, 0.0095979, 0.0060858, 0.0013649, -0.0035905, -0.0078298, -0.0104505, -0.0107453, -0.008502, -0.0042244, 0.0009785, 0.0058118, 0.009231, 0.0106281, 0.0097846, 0.0068127, 0.0022244, -0.0029998, -0.0075713, -0.0103176, -0.0106154, -0.0085402, -0.0046867, 0.0001109, 0.0049262, 0.0087717, 0.0107116, 0.0101585, 0.0071849, 0.0025692, -0.0025045, -0.0068817, -0.0097133, -0.0104901, -0.0090131, -0.0054703, -0.0005653, 0.0045315, 0.0085245, 0.01046, 0.0100069, 0.0074131, 0.0032834, -0.0015787, -0.0062041, -0.0095328, -0.0106773, -0.0092876, -0.0057403, -0.0009816, 0.0038563, 0.0077807, 0.0100517, 0.0101852, 0.0080347, 0.0039632, -0.0010953, -0.0058675, -0.0091845, -0.0103582, -0.0092704, -0.0062333, -0.0018445, 0.0030459, 0.0073664, 0.0100337, 0.0103294, 0.0082018, 0.0042633, -0.000501, -0.0050682, -0.008551, -0.0102359, -0.0096658, -0.0068377, -0.0023589, 0.0026547, 0.0069554, 0.0095847, 0.0100803, 0.0084327, 0.0049726, 0.0003457, -0.0044758, -0.0083339, -0.0102319, -0.0097117, -0.0069959, -0.002817, 0.0018857, 0.0061756, 0.009189, 0.0102139, 0.0088936, 0.0054574, 0.0007497, -0.0040419, -0.0078032, -0.0097872, -0.0096692, -0.0074818, -0.0036042, 0.0011891, 0.0057884, 0.0090362, 0.0101324, 0.0088983, 0.0057398, 0.0014105, -0.0032065, -0.0071901, -0.0096539, -0.009937, -0.0078806, -0.0039766, 0.0007746, 0.0052308, 0.0084502, 0.0098341, 0.0091183, 0.0063819, 0.0021276, -0.0026978, -0.0069052, -0.0094521, -0.0097914, -0.0079664, -0.0044657, -2.44e-05, 0.0044774, 0.0080755, 0.0098834, 0.0093851, 0.0066813, 0.0024823, -0.0021687, -0.0062441, -0.0089418, -0.0097342, -0.0083981, -0.0051209, -0.0005928, 0.0040929, 0.0077732, 0.0096048, 0.0092742, 0.0069562, 0.0031612, -0.0013642, -0.0056797, -0.0087731, -0.0098393, -0.0085908, -0.0053812, -0.0010426, 0.0034273, 0.0071148, 0.0092895, 0.0094563, 0.0074772, 0.0037223, -0.0009239, -0.0053076, -0.0083919, -0.0095508, -0.0086334, -0.0058732, -0.0018077, 0.002744, 0.0067537, 0.0092225, 0.0095205, 0.007618, 0.0040513, -0.0003215, -0.0045773, -0.0078644, -0.0094734, -0.0089651, -0.0063622, -0.0022538, 0.0023402, 0.006309, 0.0087923, 0.0093368, 0.0078793, 0.0046947, 0.0003995, -0.0040704, -0.0076348, -0.009401, -0.0089732, -0.006542, -0.0027343, 0.0016181, 0.005636, 0.0084747, 0.0094482, 0.0082412, 0.0050956, 0.0007927, -0.0036075, -0.0071113, -0.0090203, -0.0089864, -0.0069984, -0.0034105, 0.0010279, 0.0052723, 0.008273, 0.0093236, 0.0082585, 0.0054106, 0.0014309, -0.0028683, -0.0065988, -0.0089062, -0.0091823, -0.0073088, -0.003752, 0.0005927, 0.0047119, 0.0077438, 0.0090988, 0.0084863, 0.0059679, 0.0020347, -0.0024141, -0.0062897, -0.0086627, -0.0090406, -0.0074333, -0.0042431, -0.0001298, 0.0040691, 0.0074247, 0.0091099, 0.0086715, 0.0062219, 0.0023997, -0.0018741, -0.0056733, -0.0082345, -0.0090247, -0.0078139, -0.0047913, -0.0006214, 0.0036862, 0.0070877, 0.0088279, 0.0086006, 0.006521, 0.0030284, -0.0011773, -0.0051922, -0.0080636, -0.0090644, -0.0079535, -0.0050513, -0.0010874, 0.0030502, 0.006511, 0.0085803, 0.0087687, 0.0069528, 0.0035006, -0.0007628, -0.0047965, -0.0076735, -0.0088135, -0.0080377, -0.0055223, -0.0017591, 0.0024666, 0.0061814, 0.008471, 0.0087793, 0.0070837, 0.0038484, -0.0001738, -0.0041392, -0.0072311, -0.0087581, -0.0083073, -0.0059216, -0.0021596, 0.0020534, 0.0057254, 0.0080729, 0.0086491, 0.0073531, 0.0044217, 0.000443, -0.0036923, -0.0069863, -0.0086387, -0.0082984, -0.0061203, -0.0026424, 0.0013891, 0.0051446, 0.0078086, 0.0087308, 0.0076352, 0.0047643, 0.0008312, -0.0032178, -0.0064874, -0.0083175, -0.0083461, -0.006536, -0.003222, 0.0008783, 0.0047929, 0.0075725, 0.0085857, 0.0076702, 0.0050956, 0.0014316, -0.0025678, -0.0060513, -0.0082073, -0.008481, -0.0067835, -0.0035459, 0.0004334, 0.0042483, 0.007102, 0.0084159, 0.0078888, 0.0055743, 0.0019469, -0.0021497, -0.0057242, -0.0079433, -0.0083541, -0.0069352, -0.0040213, -0.0002123, 0.0036952, 0.0068179, 0.0083907, 0.0080144, 0.0058008, 0.0023183, -0.0016166, -0.00516, -0.0075834, -0.0083593, -0.0072626, -0.0044825, -0.0006493, 0.0033121, 0.0064636, 0.0081202, 0.0079781, 0.0061061, 0.0028896, -0.0010122, -0.0047392, -0.0074037, -0.0083501, -0.0073695, -0.0047448, -0.0011165, 0.0027176, 0.0059606, 0.0079198, 0.0081226, 0.0064625, 0.0032963, -0.0006145, -0.0043325, -0.0070218, -0.0081371, -0.0074793, -0.0051826, -0.0017033, 0.0022113, 0.0056496, 0.0077778, 0.0081002, 0.0065919, 0.0036522, -5.34e-05, -0.0037464, -0.006646, -0.0080888, -0.0076931, -0.0055141, -0.002073, 0.0017947, 0.0051987, 0.0074174, 0.0080112, 0.0068543, 0.0041571, 0.0004791, -0.0033412, -0.0063878, -0.0079408, -0.0076803, -0.0057265, -0.0025436, 0.0011917, 0.0046953, 0.007188, 0.0080618, 0.0070745, 0.0044599, 0.000863, -0.0028694, -0.0059232, -0.0076708, -0.0077456, -0.0060966, -0.0030416, 0.0007398, 0.0043503, 0.0069312, 0.0079114, 0.0071267, 0.0047939, 0.0014174, -0.0022985, -0.0055439, -0.0075562, -0.0078316, -0.0063003, -0.0033541, 0.0002959, 0.0038335, 0.0065161, 0.0077806, 0.0073259, 0.0052028, 0.0018644, -0.0019053, -0.0052067, -0.0072873, -0.0077238, -0.0064683, -0.0038023, -0.0002774, 0.003352, 0.0062536, 0.0077245, 0.0074099, 0.0054129, 0.002236, -0.0013924, -0.0046967, -0.0069822, -0.0077364, -0.0067449, -0.0041944, -0.0006751, 0.0029707, 0.0058964, 0.0074737, 0.0074009, 0.0057112, 0.0027488, -0.0008652, -0.0043193, -0.0067928, -0.0076929, -0.0068331, -0.004458, -0.0011313, 0.0024235, 0.0054573, 0.007305, 0.0075182, 0.0060058, 0.0031075, -0.0004804, -0.0039127, -0.0064297, -0.007515, -0.0069556, -0.0048568, -0.001644, 0.0019764, 0.0051574, 0.0071402, 0.0074776, 0.0061375, 0.0034618, 4.43e-05, -0.003392, -0.0061046, -0.0074645, -0.0071217, -0.0051377, -0.001992, 0.0015631, 0.0047231, 0.0068184, 0.0074182, 0.0063829, 0.0039036, 0.0005096, -0.0030166, -0.0058374, -0.0073017, -0.007112, -0.0053568, -0.0024395, 0.0010203, 0.0042831, 0.0066108, 0.0074397, 0.0065563, 0.0041786, 0.0008869, -0.0025586, -0.0054111, -0.0070739, -0.0071831, -0.0056813, -0.0028703, 0.0006125, 0.0039436, 0.0063449, 0.0072939, 0.0066226, 0.0045046, 0.001392, -0.002056, -0.0050741, -0.0069518, -0.0072315, -0.0058552, -0.0031736, 0.0001788, 0.0034618, 0.0059798, 0.0071894, 0.0067976, 0.0048538, 0.0017867, -0.0016818, -0.004735, -0.0066887, -0.0071435, -0.0060298, -0.0035882, -0.0003289, 0.0030366, 0.0057307, 0.0071095, 0.0068538, 0.0050538, 0.0021522, -0.001197, -0.0042769, -0.0064266, -0.0071547, -0.006261, -0.0039263, -0.0006974, 0.0026606, 0.005381, 0.006882, 0.0068644, 0.0053364, 0.0026093, -0.0007334, -0.0039312, -0.0062288, -0.007089, -0.0063392, -0.0041879, -0.001134, 0.0021621, 0.0049953, 0.0067333, 0.0069547, 0.0055816, 0.0029322, -0.0003606, -0.0035334, -0.0058904, -0.0069406, -0.0064644, -0.004546, -0.0015835, 0.0017605, 0.0047034, 0.0065546, 0.006906, 0.0057157, 0.0032767, 0.0001234, -0.0030708, -0.0056036, -0.0068836, -0.0065914, -0.0047894, -0.0019141, 0.001357, 0.0042932, 0.0062692, 0.0068663, 0.0059389, 0.0036626, 0.0005351, -0.0027181, -0.0053327, -0.0067164, -0.0065881, -0.0050089, -0.0023324, 0.0008707, 0.0039043, 0.0060753, 0.0068634, 0.0060778, 0.0039173, 0.0009024, -0.0022814, -0.0049456, -0.0065222, -0.0066569, -0.0052906, -0.0027087, 0.0004966, 0.0035716, 0.0058094, 0.0067275, 0.0061539, 0.0042279, 0.0013589, -0.0018367, -0.0046399, -0.0063924, -0.006678, -0.0054444, -0.0030026, 8e-05, 0.0031277, 0.0054874, 0.0066396, 0.0063033, 0.0045277, 0.0017132, -0.0014784, -0.0043055, -0.006142, -0.0066078, -0.0056179, -0.0033808, -0.0003701, 0.0027468, 0.0052474, 0.0065426, 0.0063419, 0.0047199, 0.0020669, -0.0010266, -0.003895, -0.0059123, -0.0066123, -0.0058098, -0.0036768, -0.0007153, 0.0023803, 0.0049123, 0.0063386, 0.0063649, 0.0049815, 0.0024726, -0.0006149, -0.0035734, -0.0057096, -0.006534, -0.0058831, -0.0039328, -0.0011266, 0.0019289, 0.0045708, 0.0062022, 0.0064305, 0.0051881, 0.0027683, -0.0002553, -0.0031911, -0.0053984, -0.0064098, -0.0060041, -0.0042511, -0.0015229, 0.001563, 0.0042863, 0.0060177, 0.0063804, 0.0053229, 0.0030971, 0.000187, -0.0027791, -0.0051402, -0.0063448, -0.0061004, -0.0044668, -0.0018382, 0.0011746, 0.0039041, 0.0057648, 0.0063528, 0.0055218, 0.0034348, 0.0005563, -0.0024447, -0.004871, -0.00618, -0.0061041, -0.0046812, -0.0022243, 0.0007392, 0.0035563, 0.0055795, 0.0063305, 0.0056359, 0.0036738, 0.00091, -0.0020343, -0.0045211, -0.0060117, -0.0061655, -0.0049245, -0.0025563, 0.000392, 0.0032325, 0.0053203, 0.0062068, 0.0057173, 0.0039642, 0.0013206, -0.0016376, -0.0042388, -0.0058756, -0.0061677, -0.0050641, -0.0028394, -2.5e-06, 0.0028265, 0.0050347, 0.0061283, 0.0058421, 0.0042232, 0.0016428, -0.0012947, -0.003915, -0.0056417, -0.0061121, -0.0052309, -0.0031812, -0.0004029, 0.0024807, 0.0048018, 0.0060207, 0.00587, 0.0044084, 0.0019803, -0.000878, -0.0035471, -0.0054367, -0.0061079, -0.0053903, -0.0034443, -0.0007282, 0.0021276, 0.0044857, 0.0058391, 0.0058998, 0.0046465, 0.0023404, -0.0005083, -0.0032446, -0.0052327, -0.006024, -0.0054611, -0.0036913, -0.001111, 0.0017201, 0.0041806, 0.0057099, 0.0059443, 0.0048232, 0.0026144, -0.0001637, -0.0028824, -0.0049486, -0.0059184, -0.0055734, -0.0039729, -0.0014633, 0.0013826, 0.0039041, 0.0055255, 0.0058966, 0.0049567, 0.0029234, 0.0002382, -0.0025134, -0.0047121, -0.0058457, -0.0056462, -0.0041673, -0.0017636, 0.0010133, 0.0035511, 0.0053005, 0.0058749, 0.0051311, 0.0032202, 0.0005733, -0.0021954, -0.0044489, -0.0056881, -0.0056559, -0.0043724, -0.0021166, 0.0006231, 0.0032362, 0.0051214, 0.0058382, 0.0052273, 0.0034457, 0.0009104, -0.0018135, -0.0041332, -0.0055393, -0.0057074, -0.0045824, -0.0024128, 0.0002985, 0.0029241, 0.0048735, 0.0057272, 0.0053101, 0.0037133, 0.0012786, -0.001457, -0.0038695, -0.0053992, -0.0056971, -0.0047116, -0.0026834, -7.09e-05, 0.0025544, 0.004618, 0.0056539, 0.0054127, 0.0039394, 0.0015751, -0.0011297, -0.0035602, -0.0051833, -0.0056531, -0.0048679, -0.0029904, -0.0004289, 0.0022372, 0.004392, 0.0055407, 0.0054346, 0.0041171, 0.0018933, -0.0007477, -0.0032293, -0.0049968, -0.0056398, -0.0050008, -0.0032275, -0.0007364, 0.0019004, 0.0040973, 0.005379, 0.0054665, 0.0043314, 0.0022136, -0.0004128, -0.0029433, -0.0047951, -0.0055549, -0.0050699, -0.0034624, -0.0010893, 0.0015324, 0.0038214, 0.0052542, 0.0054938, 0.0044849, 0.0024691, -8.49e-05, -0.0026035, -0.0045367, -0.0054632, -0.0051708, -0.0037109, -0.0014049, 0.0012188, 0.0035545, 0.005074, 0.0054502, 0.0046146, 0.0027561, 0.0002791, -0.0022712, -0.004317, -0.0053843, -0.0052261, -0.0038887, -0.0016898, 0.0008713, 0.0032306, 0.0048729, 0.0054306, 0.0047661, 0.0030184, 0.0005859, -0.0019689, -0.0040635, -0.0052363, -0.0052403, -0.0040815, -0.0020103, 0.0005203, 0.0029426, 0.0046989, 0.0053841, 0.0048494, 0.0032315, 0.0009042, -0.0016162, -0.0037782, -0.005102, -0.0052812, -0.0042632, -0.0022775, 0.0002158, 0.0026441, 0.0044651, 0.005285, 0.0049303, 0.0034757, 0.0012349, -0.0012929, -0.00353, -0.0049608, -0.0052634, -0.0043842, -0.0025339, -0.0001273, 0.0023081, 0.0042344, 0.0052137, 0.0050137, 0.003675, 0.0015094, -0.0009822, -0.0032377, -0.0047628, -0.0052276, -0.0045278, -0.0028089, -0.0004494, 0.0020145, 0.0040157, 0.0050993, 0.0050323, 0.0038444, 0.0018065, -0.0006335, -0.0029386, -0.0045903, -0.0052059, -0.0046394, -0.0030247, -0.0007393, 0.0016962, 0.0037429, 0.0049546, 0.0050631, 0.0040354, 0.0020924, -0.0003275, -0.0026677, -0.0043937, -0.0051231, -0.0047065, -0.0032456, -0.0010628, 0.0013635, 0.0034912, 0.0048327, 0.0050769, 0.0041708, 0.0023314, -1.76e-05, -0.0023518, -0.0041592, -0.0050416, -0.0047953, -0.0034651, -0.0013478, 0.0010706, 0.0032352, 0.0046602, 0.005038, 0.0042948, 0.0025955, 0.0003116, -0.0020503, -0.0039531, -0.0049584, -0.004838, -0.0036293, -0.0016167, 0.0007463, 0.002939, 0.0044788, 0.0050181, 0.0044256, 0.0028292, 0.0005946, -0.0017636, -0.0037116, -0.0048209, -0.0048547, -0.0038079, -0.001907, 0.0004293, 0.0026731, 0.0043098, 0.0049654, 0.0044996, 0.0030301, 0.0008926, -0.0014395, -0.0034529, -0.0046976, -0.0048851, -0.0039659, -0.0021498, 0.0001431, 0.00239, 0.0040913, 0.0048767, 0.0045761, 0.003251, 0.0011901, -0.0011443, -0.0032183, -0.0045574, -0.0048634, -0.0040795, -0.0023907, -0.0001733, 0.0020847, 0.003881, 0.0048062, 0.0046432, 0.0034286, 0.0014454, -0.0008507, -0.0029446, -0.0043764, -0.004833, -0.0042094, -0.0026368, -0.0004648, 0.0018115, 0.0036706, 0.0046935, 0.0046601, 0.0035886, 0.0017204, -0.0005331, -0.0026729, -0.0042152, -0.0048043, -0.0043042, -0.0028349, -0.0007377, 0.001513, 0.0034194, 0.004563, 0.0046878, 0.0037581, 0.0019768, -0.0002517, -0.0024164, -0.0040261, -0.0047256, -0.0043689, -0.0030406, -0.0010328, 0.0012114, 0.0031877, 0.0044439, 0.0046916, 0.0038793, 0.0022006, 3.91e-05, -0.0021241, -0.0038126, -0.0046511, -0.0044456, -0.0032349, -0.001292, 0.000937, 0.002944, 0.0042806, 0.004657, 0.0039958, 0.0024421, 0.0003372, -0.0018488, -0.0036181, -0.0045656, -0.0044789, -0.0033871, -0.0015445, 0.0006361, 0.0026733, 0.0041153, 0.0046352, 0.0041084, 0.0026516, 0.0005994, -0.0015779, -0.0033902, -0.0044386, -0.0044966, -0.0035508, -0.0018067, 0.0003486, 0.0024265, 0.0039518, 0.0045793, 0.0041752, 0.00284, 0.0008765, -0.0012809, -0.0031548, -0.0043236, -0.0045176, -0.0036891, -0.0020291, 8e-05, 0.0021598, 0.003749, 0.0044996, 0.0042458, 0.0030391, 0.0011449, -0.0010098, -0.0029328, -0.0041867, -0.0044941, -0.0037956, -0.0022537, -0.0002106, 0.001882, 0.0035558, 0.0044291, 0.0042998, 0.0031989, 0.0013828, -0.0007339, -0.0026779, -0.0040213, -0.004467, -0.0039119, -0.0024742, -0.000476, 0.0016267, 0.0033545, 0.0043203, 0.0043156, 0.0033487, 0.0016359, -0.0004448, -0.0024298, -0.0038693, -0.0044329, -0.0039934, -0.0026569, -0.0007318, 0.0013485, 0.0031238, 0.0042015, 0.0043389, 0.0034988, 0.0018671, -0.0001845, -0.0021873, -0.0036891, -0.0043589, -0.0040548, -0.0028466, -0.0010005, 0.0010741, 0.0029089, 0.0040852, 0.0043353, 0.0036083, 0.0020761, 8.67e-05, -0.0019178, -0.0034943, -0.0042895, -0.00412, -0.0030193, -0.0012375, 0.0008171, 0.0026784, 0.003932, 0.0043045, 0.0037164, 0.002296, 0.0003569, -0.0016652, -0.0033102, -0.0042034, -0.0041467, -0.0031608, -0.0014732, 0.0005393, 0.002431, 0.0037802, 0.0042802, 0.0038134, 0.002485, 0.0006005, -0.0014104, -0.0030966, -0.0040867, -0.004164, -0.0033096, -0.0017101, 0.0002772, 0.002201, 0.003623, 0.0042235, 0.0038743, 0.002661, 0.0008569, -0.0011386, -0.0028814, -0.0039781, -0.004177, -0.0034316, -0.0019146, 2.55e-05, 0.0019513, 0.0034354, 0.004151, 0.0039382, 0.0028399, 0.0010998, -0.0008883, -0.0026715, -0.003846, -0.004153, -0.0035311, -0.0021228, -0.0002406, 0.0016977, 0.0032566, 0.0040807, 0.0039815, 0.0029846, 0.0013214, -0.0006303, -0.0024352, -0.0036944, -0.0041277, -0.0036342, -0.0023208, -0.0004834, 0.0014589, 0.0030652, 0.0039768, 0.0039963, 0.0031238, 0.0015534, -0.0003669, -0.0022074, -0.0035504, -0.0040897, -0.0037051, -0.0024897, -0.0007223, 0.0012008, 0.0028533, 0.0038677, 0.0040147, 0.0032564, 0.0017627, -0.0001258, -0.0019789, -0.0033802, -0.0040208, -0.0037625, -0.0026636, -0.0009665, 0.0009502, 0.0026532, 0.0037547, 0.0040061, 0.0033562, 0.0019575, 0.000126, -0.0017313, -0.0032018, -0.003955, -0.0038174, -0.0028179, -0.0011843, 0.0007098, 0.0024365, 0.0036119, 0.0039784, 0.0034553, 0.0021571, 0.0003717, -0.0014982, -0.0030276, -0.00387, -0.0038393, -0.0029492, -0.0014032, 0.0004541, 0.00221, 0.0034715, 0.0039516, 0.0035392, 0.0023288, 0.0005983, -0.0012592, -0.0028284, -0.0037625, -0.0038551, -0.0030836, -0.0016174, 0.0002141, 0.001995, 0.0033209, 0.0038953, 0.0035949, 0.0024922, 0.0008343, -0.0010107, -0.0026306, -0.0036591, -0.0038614, -0.0031918, -0.0018061, -2.1e-05, 0.0017623, 0.0031478, 0.0038288, 0.0036516, 0.0026528, 0.0010551, -0.000779, -0.0024325, -0.0035329, -0.0038378, -0.0032843, -0.0019979, -0.0002643, 0.0015302, 0.0029813, 0.0037588, 0.0036865, 0.0027846, 0.0012614, -0.0005386, -0.0022142, -0.0033938, -0.0038131, -0.0033753, -0.0021762, -0.0004872, 0.0013069, 0.0028005, 0.0036608, 0.0037003, 0.0029129, 0.0014733, -0.0002984, -0.0020041, -0.0032572, -0.0037729, -0.0034376, -0.0023324, -0.0007094, 0.0010684, 0.0026058, 0.0035598, 0.0037138, 0.0030304, 0.0016638, -7.44e-05, -0.0017895, -0.0030972, -0.0037087, -0.0034907, -0.0024911, -0.0009316, 0.0008386, 0.0024189, 0.0034505, 0.0037019, 0.0031216, 0.0018448, 0.0001584, -0.001562, -0.002933, -0.0036457, -0.0035365, -0.0026295, -0.0011324, 0.0006139, 0.002216, 0.0033177, 0.0036763, 0.0032116, 0.0020255, 0.0003824, -0.0013463, -0.0027683, -0.0035627, -0.0035547, -0.0027511, -0.0013347, 0.000379, 0.0020082, 0.0031869, 0.0036473, 0.0032843, 0.0021819, 0.0005929, -0.0011232, -0.0025831, -0.0034635, -0.0035683, -0.002872, -0.0015289, 0.0001585, 0.0018071, 0.0030438, 0.0035926, 0.0033354, 0.002333, 0.0008097, -0.0008956, -0.0024008, -0.003365, -0.0035692, -0.0029688, -0.0017031, -6.03e-05, 0.0015911, 0.002884, 0.0035307, 0.0033852, 0.0024773, 0.001011, -0.0006807, -0.0022142, -0.0032454, -0.0035465, -0.0030541, -0.0018791, -0.0002826, 0.0013781, 0.0027283, 0.0034618, 0.0034133, 0.0025977, 0.0012026, -0.0004576, -0.0020128, -0.003117, -0.0035218, -0.0031341, -0.0020401, -0.000488, 0.0011693, 0.0025582, 0.0033698, 0.0034258, 0.0027152, 0.0013958, -0.0002381, -0.0018184, -0.0029873, -0.0034803, -0.0031894, -0.0021846, -0.000694, 0.0009491, 0.0023792, 0.0032755, 0.0034348, 0.0028197, 0.0015697, -2.99e-05, -0.0016174, -0.0028378, -0.0034206, -0.0032376, -0.0023289, -0.0008963, 0.000738, 0.0022043, 0.0031705, 0.0034207, 0.0029031, 0.0017372, 0.0001848, -0.0014085, -0.002686, -0.0033596, -0.0032756, -0.0024536, -0.0010816, 0.0005286, 0.002015, 0.0030473, 0.0033965, 0.0029842, 0.0019009, 0.0003893, -0.0012084, -0.0025306, -0.0032797, -0.003291, -0.0025659, -0.001268, 0.000313, 0.001824, 0.0029249, 0.003366, 0.0030476, 0.0020441, 0.0005848, -0.0010008, -0.002359, -0.0031879, -0.0033021, -0.0026742, -0.0014442, 0.00011, 0.001636, 0.0027894, 0.0033134, 0.0030943, 0.0021832, 0.0007836, -0.0007921, -0.00219, -0.0030938, -0.003299, -0.0027612, -0.0016053, -9.33e-05, 0.0014359, 0.0026419, 0.0032554, 0.0031374, 0.0023128, 0.0009677, -0.0005926, -0.002015, -0.0029809, -0.0032771, -0.0028394, -0.0017662, -0.0002966, 0.0012399, 0.0024961, 0.0031879, 0.0031602, 0.0024232, 0.0011452, -0.0003861, -0.0018293, -0.0028621, -0.0032519, -0.0029097, -0.0019121, -0.000486, 0.001045, 0.0023367, 0.0031017, 0.0031711, 0.0025302, 0.0013213, -0.000185, -0.0016489, -0.0027391, -0.0032102, -0.002959, -0.0020455, -0.0006765]}
\ No newline at end of file
packages/core/test/fixtures/fem_center_48.jsonadded+195−0View file
@@ -0,0 +1,195 @@
1+{
2+ "model": "Q4 Mindlin-Reissner FEM; sparse eigensolve; coupled Hunt-Crossley/Hertz impact",
3+ "config": {
4+ "width": 0.03,
5+ "height": 0.03,
6+ "thickness": 0.001,
7+ "density": 2700.0,
8+ "young": 68900000000.0,
9+ "poisson": 0.33,
10+ "material_q": 1400.0,
11+ "shear_correction": 0.8333333333333334,
12+ "mesh_x": 48,
13+ "mesh_y": 48,
14+ "hole_diameter": 0.0008,
15+ "hole_edge_offset": 0.0015,
16+ "thread_length": 0.2,
17+ "thread_dashpot": 0.00075,
18+ "hit_x": 0.0,
19+ "hit_y": 0.0,
20+ "hammer_mass": 0.01,
21+ "hammer_tip_radius": 0.002,
22+ "hammer_speed": 0.45,
23+ "hammer_young": 200000000000.0,
24+ "hammer_poisson": 0.29,
25+ "restitution": 0.62,
26+ "mic_distance": 0.35,
27+ "air_density": 1.204,
28+ "sound_speed": 343.0,
29+ "sample_rate": 96000,
30+ "duration": 4.0,
31+ "modes": 72,
32+ "normalize_dbfs": -1.0
33+ },
34+ "derived": {
35+ "plate_mass_kg": 0.002428642831973649,
36+ "fem_degrees_of_freedom": 7203,
37+ "retained_mode_count": 18,
38+ "low_frequency_momentum_modes": 3,
39+ "contact_duration_s": 0.00014386841458983345,
40+ "impact_impulse_Ns": 0.0016141368274278794,
41+ "peak_contact_force_N": 24.17614265214583,
42+ "hammer_exit_velocity_m_s_positive_into_plate": 0.288586188656118,
43+ "plate_contact_point_exit_velocity_m_s_positive_into_plate": 0.43849323681201396,
44+ "relative_separation_velocity_m_s": -0.14990704815589595,
45+ "effective_relative_restitution": 0.3331267736797688,
46+ "critical_frequency_hz": 12120.88938013311,
47+ "unnormalized_peak_pressure_pa_approx": 5.7305788699122786e-08
48+ },
49+ "modes": [
50+ {
51+ "mode": 1,
52+ "frequency_hz": 3565.9758260120643,
53+ "q_total": 411.3543618249471,
54+ "hit_participation": 1.0268782804049045e-06,
55+ "support_participation": 1.2867759580743019e-06,
56+ "radiation_participation": 2.614849102625409e-13
57+ },
58+ {
59+ "mode": 2,
60+ "frequency_hz": 5623.617834128409,
61+ "q_total": 344.4362103366806,
62+ "hit_participation": -7.018066236974367e-06,
63+ "support_participation": -39.534772651831496,
64+ "radiation_participation": -1.396742909087222e-09
65+ },
66+ {
67+ "mode": 3,
68+ "frequency_hz": 6944.342983431703,
69+ "q_total": 321.53676453787256,
70+ "hit_participation": 34.565652743070316,
71+ "support_participation": -2.7622622177857226,
72+ "radiation_participation": -6.402537341749679e-11
73+ },
74+ {
75+ "mode": 4,
76+ "frequency_hz": 9458.457072776113,
77+ "q_total": 284.88370453805516,
78+ "hit_participation": 3.080391565004549e-07,
79+ "support_participation": 1.8970949912228652,
80+ "radiation_participation": 2.3691749283298548e-11
81+ },
82+ {
83+ "mode": 5,
84+ "frequency_hz": 9458.457298368265,
85+ "q_total": 283.2943075035011,
86+ "hit_participation": 1.6166427735750155e-06,
87+ "support_participation": 39.5487522912875,
88+ "radiation_participation": 4.9391636123392e-10
89+ },
90+ {
91+ "mode": 6,
92+ "frequency_hz": 17027.374310417825,
93+ "q_total": 223.3780279795689,
94+ "hit_participation": 33.43011855856327,
95+ "support_participation": -39.85611266968268,
96+ "radiation_participation": -1.5357798965642447e-10
97+ },
98+ {
99+ "mode": 7,
100+ "frequency_hz": 17370.396397743538,
101+ "q_total": 222.04499806690183,
102+ "hit_participation": 1.0806337428699248e-05,
103+ "support_participation": -7.76508029598892,
104+ "radiation_participation": -2.8728976302906087e-11
105+ },
106+ {
107+ "mode": 8,
108+ "frequency_hz": 17370.396557451277,
109+ "q_total": 221.9727947638167,
110+ "hit_participation": -6.7329002431021205e-06,
111+ "support_participation": 16.537020743556848,
112+ "radiation_participation": 6.121617536560426e-11
113+ },
114+ {
115+ "mode": 9,
116+ "frequency_hz": 19072.987303458805,
117+ "q_total": 213.46881809675375,
118+ "hit_participation": -1.056737111764634e-06,
119+ "support_participation": 9.187822205818643e-07,
120+ "radiation_participation": -2.8530940088776387e-15
121+ },
122+ {
123+ "mode": 10,
124+ "frequency_hz": 21389.387771162706,
125+ "q_total": 203.30535587463592,
126+ "hit_participation": 8.885582278320107e-08,
127+ "support_participation": -1.4686566619710604e-07,
128+ "radiation_participation": -1.6815848640308086e-14
129+ },
130+ {
131+ "mode": 11,
132+ "frequency_hz": 28279.99201936713,
133+ "q_total": 180.2195344754748,
134+ "hit_participation": -4.3930578266748584e-08,
135+ "support_participation": 3.5648654538286664,
136+ "radiation_participation": 4.979957481340857e-12
137+ },
138+ {
139+ "mode": 12,
140+ "frequency_hz": 28279.992083537196,
141+ "q_total": 180.00957025970527,
142+ "hit_participation": -6.138636558662723e-07,
143+ "support_participation": 39.320129191363534,
144+ "radiation_participation": 5.492135301078861e-11
145+ },
146+ {
147+ "mode": 13,
148+ "frequency_hz": 32948.769564021815,
149+ "q_total": 168.54104680355613,
150+ "hit_participation": 4.864498612168764e-07,
151+ "support_participation": 14.024966906614324,
152+ "radiation_participation": 1.4452115541064647e-11
153+ },
154+ {
155+ "mode": 14,
156+ "frequency_hz": 34471.42122413984,
157+ "q_total": 165.13882431609167,
158+ "hit_participation": 42.91035723216589,
159+ "support_participation": 32.8533844153399,
160+ "radiation_participation": 3.0893313438762836e-11
161+ },
162+ {
163+ "mode": 15,
164+ "frequency_hz": 36386.13727421851,
165+ "q_total": 161.32726592436384,
166+ "hit_participation": 1.1054353397250513e-06,
167+ "support_participation": 13.230595399227422,
168+ "radiation_participation": 1.1171159698715872e-11
169+ },
170+ {
171+ "mode": 16,
172+ "frequency_hz": 36386.137340431,
173+ "q_total": 161.22674843297202,
174+ "hit_participation": 1.7379876844932346e-06,
175+ "support_participation": 36.784011754607235,
176+ "radiation_participation": 3.1053227883933365e-11
177+ },
178+ {
179+ "mode": 17,
180+ "frequency_hz": 40487.36667835226,
181+ "q_total": 153.87450395403786,
182+ "hit_participation": 5.3299006397064166e-08,
183+ "support_participation": 1.9888011839050177e-07,
184+ "radiation_participation": -4.436650157500777e-15
185+ },
186+ {
187+ "mode": 18,
188+ "frequency_hz": 43378.88751280824,
189+ "q_total": 149.09142658088695,
190+ "hit_participation": 6.270011871410755e-07,
191+ "support_participation": 44.68460671685151,
192+ "radiation_participation": 2.652834471678451e-11
193+ }
194+ ]
195+}
packages/core/test/fixtures/fem_center_48_wav.jsonadded+1−0View file
@@ -0,0 +1 @@
1+{"fs": 96000, "samples": [-4e-07, -0.7009247, 0.0503699, 0.891251, 0.4092252, -0.7876944, -0.3376023, 0.4951724, 0.4321963, -0.2286147, -0.006062, 0.5460456, 0.2005908, -0.4700085, -0.5230225, -0.1026272, 0.4473929, 0.4614988, -0.1947547, -0.7092799, -0.5490531, 0.0906495, 0.6345465, 0.4523329, -0.1904623, -0.4636201, -0.1397437, 0.4600195, 0.6670882, 0.1172527, -0.5476425, -0.6502423, -0.1789432, 0.4115955, 0.4166589, -0.1580681, -0.5393603, -0.3272334, 0.3094685, 0.7478727, 0.42763, -0.2438489, -0.5337665, -0.2432834, 0.3244478, 0.4589295, -0.0673279, -0.5940308, -0.5800992, -0.01643, 0.5816613, 0.5239473, -0.0423362, -0.4129982, -0.2424951, 0.3225056, 0.6138231, 0.1977818, -0.4368607, -0.6635609, -0.2916966, 0.3249408, 0.4517975, -0.0067645, -0.4325823, -0.3695753, 0.1945815, 0.6791191, 0.4827762, -0.128333, -0.5207278, -0.3548638, 0.197871, 0.4321158, 0.045485, -0.4698037, -0.5795935, -0.1065896, 0.5095016, 0.5667492, 0.0916077, -0.348708, -0.3173936, 0.18708, 0.5370144, 0.2577759, -0.3251013, -0.6474714, -0.3775402, 0.231512, 0.4626428, 0.127198, -0.3186442, -0.3845546, 0.0884814, 0.5892902, 0.5112899, -0.0233035, -0.4873533, -0.4367986, 0.0738949, 0.386213, 0.1400979, -0.3427177, -0.5508485, -0.1781001, 0.424698, 0.5825246, 0.2063189, -0.2752502, -0.3654377, 0.0589761, 0.4434826, 0.2963728, -0.2175843, -0.6058793, -0.4362329, 0.1373663, 0.4525409, 0.2397181, -0.2029965, -0.3751882, -0.0057088, 0.4854606, 0.5144412, 0.0671405, -0.4376926, -0.4898881, -0.041886, 0.3264255, 0.2143781, -0.218109, -0.4983189, -0.2302007, 0.3335076, 0.5738024, 0.2984457, -0.1971237, -0.3888325, -0.0573428, 0.3398059, 0.3139201, -0.1184045, -0.5435059, -0.4686488, 0.0478795, 0.4251372, 0.3283492, -0.0905794, -0.3454465, -0.0856807, 0.3742542, 0.4944549, 0.1403234, -0.3761507, -0.5160295, -0.1447894, 0.2577805, 0.2675848, -0.1004777, -0.4271794, -0.2629974, 0.241525, 0.5437437, 0.3662549, -0.1186249, -0.3906349, -0.1583486, 0.2320522, 0.3118444, -0.0305916, -0.4655495, -0.476598, -0.0325127, 0.3842197, 0.3921781, 0.0142967, -0.2998516, -0.1499599, 0.2616123, 0.4543795, 0.194889, -0.307204, -0.5179406, -0.2312249, 0.1849474, 0.3002029, 0.0065216, -0.3429526, -0.2773917, 0.1534635, 0.4959819, 0.409459, -0.0436626, -0.3744232, -0.2415289, 0.1256163, 0.2924898, 0.0438352, -0.377332, -0.4626313, -0.1004596, 0.33357, 0.431617, 0.1080295, -0.2431463, -0.1979227, 0.1526164, 0.3978873, 0.230637, -0.2351418, -0.4989019, -0.2987656, 0.1121276, 0.3137542, 0.1001047, -0.2511485, -0.2749691, 0.0730425, 0.434443, 0.4289878, 0.0243858, -0.344039, -0.3054361, 0.0250856, 0.2588855, 0.1037736, -0.2839917, -0.4298472, -0.1537315, 0.2768524, 0.4481873, 0.1878168, -0.1799879, -0.2297478, 0.0513989, 0.3290471, 0.2483278, -0.1638656, -0.4625573, -0.3461698, 0.0429434, 0.3105457, 0.1783439, -0.1569673, -0.2578678, 0.0029719, 0.3631856, 0.4267695, 0.0828145, -0.3033429, -0.3496453, -0.0658269, 0.2144929, 0.1489194, -0.1902541, -0.3817363, -0.191209, 0.2174789, 0.4442573, 0.2516978, -0.1147058, -0.2463235, -0.0388681, 0.2521147, 0.2495091, -0.0967063, -0.4127076, -0.3733072, -0.0196257, 0.2934107, 0.2401238, -0.0650902, -0.2286638, -0.0550342, 0.2862075, 0.4054788, 0.1296686, -0.256024, -0.3746546, -0.1442899, 0.1629788, 0.1796707, -0.1002305, -0.3220004, -0.2127872, 0.1585057, 0.4227818, 0.2985523, -0.0511407, -0.2491504, -0.1159674, 0.1712965, 0.2363154, -0.0363158, -0.3531431, -0.381027, -0.0732664, 0.265484, 0.2851084, 0.0205001, -0.1902116, -0.1001686, 0.2072823, 0.3682951, 0.1637662, -0.205473, -0.3817703, -0.2083596, 0.1079736, 0.196999, -0.0173004, -0.2543921, -0.2192315, 0.1025598, 0.3870813, 0.3280921, 0.0074931, -0.240189, -0.1785692, 0.0905529, 0.2112797, 0.0153765, -0.2875181, -0.3710151, -0.1163637, 0.2299673, 0.313657, 0.0966134, -0.1454923, -0.1324082, 0.1298318, 0.3187027, 0.1847029, -0.1546624, -0.3729327, -0.2569303, 0.0528762, 0.2023219, 0.0559399, -0.1825819, -0.2120315, 0.0517595, 0.3406127, 0.3407894, 0.0586704, -0.2217051, -0.2261372, 0.0134461, 0.177181, 0.0571876, -0.2192167, -0.3455936, -0.147979, 0.189943, 0.3267268, 0.1608848, -0.0974832, -0.1523966, 0.0567962, 0.2602816, 0.1927883, -0.1060839, -0.3505406, -0.2896628, 0.0007074, 0.1973964, 0.1177052, -0.1100099, -0.1932087, 0.0076864, 0.2867898, 0.3377814, 0.1005705, -0.1961327, -0.258843, -0.0570065, 0.1368741, 0.0886297, -0.1512662, -0.307527, -0.1678095, 0.1482386, 0.3257828, 0.2117867, -0.0490037, -0.1612872, -0.0094337, 0.1965424, 0.1889537, -0.0617211, -0.3172871, -0.3069187, -0.0460242, 0.1841825, 0.1669746, -0.0397793, -0.1651377, -0.0286166, 0.2288474, 0.3207727, 0.1320815, -0.1659095, -0.2774384, -0.1184069, 0.0931562, 0.1098328, -0.0862764, -0.2598462, -0.1761475, 0.1072998, 0.3126637, 0.2485736, -0.0026058, -0.1606006, -0.0670542, 0.1307995, 0.1746539, -0.0230225, -0.2759756, -0.3096478, -0.0853833, 0.1647327, 0.2034211, 0.025425, -0.1303895, -0.0566453, 0.1697095, 0.2918886, 0.1527462, -0.1333547, -0.2831368, -0.1690155, 0.0486615, 0.1214593, -0.0263783, -0.205656, -0.1737929, 0.0691309, 0.2894662, 0.2712076, 0.0395165, -0.1521, -0.1148716, 0.0660422, 0.1517211, 0.0090714, -0.2293779, -0.2992828, -0.1160351, 0.1410972, 0.227335, 0.0834183, -0.0915597, -0.0763784, 0.1119177, 0.2535411, 0.1626941, -0.1005691, -0.2775072, -0.2077706, 0.0057485, 0.1245754, 0.0267869, -0.1479876, -0.1619661, 0.0352622, 0.2584333, 0.2802776, 0.0756116, -0.1376488, -0.1522653, 0.0048636, 0.1222352, 0.0340945, -0.180115, -0.2776379, -0.1372559, 0.1152134, 0.2395013, 0.132543, -0.0511428, -0.0882181, 0.0575858, 0.2083009, 0.1625724, -0.0693388, -0.262344, -0.234245, -0.0335618, 0.1205362, 0.0720676, -0.089677, -0.1422213, 0.0067323, 0.2218248, 0.2768764, 0.1044001, -0.11911, -0.1791415, -0.0505909, 0.0883985, 0.052019, -0.1305417, -0.2467779, -0.1488824, 0.0888383, 0.2410916, 0.1716813, -0.0114288, -0.0929252, 0.0083607, 0.158751, 0.1534374, -0.0410937, -0.2395681, -0.2485925, -0.0676363, 0.1108767, 0.1087966, -0.0332462, -0.1163254, -0.0158757, 0.1818276, 0.2624922, 0.125088, -0.0982586, -0.1958763, -0.0986694, 0.0523962, 0.0631797, -0.0826882, -0.2089147, -0.1512549, 0.0634905, 0.2335595, 0.2002558, 0.0255961, -0.0915178, -0.034562, 0.1073823, 0.1366619, -0.0168804, -0.2111358, -0.2514813, -0.0952718, 0.0971853, 0.1367388, 0.0191549, -0.0861637, -0.032396, 0.1404727, 0.2389037, 0.1373691, -0.0766929, -0.2032174, -0.1382065, 0.0162988, 0.0682107, -0.038215, -0.1663056, -0.1451494, 0.040393, 0.2185167, 0.218181, 0.0582951, -0.0851914, -0.0704238, 0.0564951, 0.1138424, 0.0026515, -0.1789384, -0.2439858, -0.1156907, 0.0810205, 0.1560482, 0.0657955, -0.053645, -0.0430331, 0.0995538, 0.2080527, 0.1413744, -0.0557919, -0.2022059, -0.1685147, -0.0180275, 0.0679783, 0.0016168, -0.1211437, -0.1316731, 0.0204653, 0.1976487, 0.2258195, 0.0854175, -0.0752357, -0.0988768, 0.0081081, 0.0866852, 0.0172133, -0.1447464, -0.2274989, -0.1285331, 0.0638315, 0.167215, 0.105396, -0.0205975, -0.0482798, 0.0605947, 0.1719545, 0.1376276, -0.0366766, -0.1940882, -0.1893634, -0.0490012, 0.0634911, 0.035928, -0.0754889, -0.112182, 0.0043112, 0.1726279, 0.2239229, 0.1061225, -0.0629383, -0.1199209, -0.0360818, 0.0569289, 0.0268488, -0.1101515, -0.2036351, -0.1338395, 0.0468835, 0.1709826, 0.1371112, 0.0112976, -0.048856, 0.0248153, 0.1326062, 0.1269847, -0.0201815, -0.1802167, -0.2009191, -0.0753579, 0.0558409, 0.0642085, -0.0311958, -0.0881894, -0.0077778, 0.1450323, 0.2135412, 0.1199563, -0.0495278, -0.1338597, -0.0747405, 0.0262622, 0.0318884, -0.0765156, -0.174121, -0.1319925, 0.0312293, 0.1682911, 0.1605172, 0.0405711, -0.0456389, -0.0068858, 0.0918964, 0.1105448, -0.0068654, -0.1619847, -0.2037022, -0.0961824, 0.046133, 0.0862867, 0.0101478, -0.0612636, -0.0157827, 0.1163041, 0.195957, 0.1268371, -0.0361112, -0.1412434, -0.1069174, -0.003758, 0.0328776, -0.0449609, -0.1407206, -0.1236751, 0.0176766, 0.1602066, 0.1755811, 0.0660223, -0.0395813, -0.0339096, 0.0515565, 0.0895845, 0.0029864, -0.140753, -0.1985259, -0.1109206, 0.0354165, 0.1022701, 0.0472403, -0.0329611, -0.0199311, 0.0877029, 0.1726035, 0.1270223, -0.0236207, -0.142795, -0.1320254, -0.0317487, 0.0305268, -0.0163506, -0.1051519, -0.1098106, 0.0067736, 0.1478458, 0.1825997, 0.0867337, -0.0316613, -0.0559489, 0.0131036, 0.0654776, 0.009343, -0.1177856, -0.1864163, -0.1193533, 0.0246483, 0.1125069, 0.0790947, -0.0047531, -0.0206536, 0.0602744, 0.1449775, 0.1210472, -0.0127975, -0.1393644, -0.149832, -0.0565518, 0.0256459, 0.0087125, -0.0690147, -0.0914854, -0.0011717, 0.1323323, 0.1821581, 0.102095, -0.0228217, -0.0729567, -0.0221998, 0.0396111, 0.0123824, -0.0942205, -0.16856, -0.1215811, 0.0146477, 0.1175294, 0.1050453, 0.0220418, -0.0185227, 0.0348513, 0.1145826, 0.1096833, -0.0041673, -0.1318673, -0.1604252, -0.0772673, 0.0190763, 0.0298601, -0.0337523, -0.0698974, -0.0060861, 0.1147388, 0.1750679, 0.1118022, -0.013916, -0.0850968, -0.0533507, 0.0133327, 0.012466, -0.0710305, -0.1462331, -0.1179855, 0.0060637, 0.1179909, 0.1247248, 0.0462869, -0.0142148, 0.012042, 0.08286, 0.0938763, 0.0019628, -0.1212279, -0.1641603, -0.0932545, 0.0116522, 0.0469492, -0.000603, -0.0462855, -0.0081023, 0.096042, 0.1622974, 0.1158298, -0.0056859, -0.092714, -0.0796253, -0.0121142, 0.010092, -0.0490043, -0.1207329, -0.1091728, -0.000627, 0.1146281, 0.1380551, 0.0670693, -0.0084524, -0.0077583, 0.0511332, 0.0746785, 0.0054829, -0.1083499, -0.1616248, -0.1041455, 0.0041451, 0.0600287, 0.0294254, -0.0218642, -0.0075144, 0.0771066, 0.1449262, 0.114416, 0.001274, -0.096283, -0.100572, -0.03564, 0.005843, -0.0287516, -0.0933367, -0.0959355, -0.0051368, 0.1082068, 0.1452122, 0.0837176, -0.001953, -0.0243404, 0.0205807, 0.053205, 0.0064741, -0.0940702, -0.15358, -0.1098342, -0.002775, 0.0692982, 0.0555555, 0.0022153, -0.0047528, 0.0586535, 0.124082, 0.1080231, 0.0065324, -0.0963597, -0.1159865, -0.0563316, 0.0003551, -0.0106953, -0.0652442, -0.0791913, -0.0073582, 0.0994784, 0.1465812, 0.0957967, 0.0045999, -0.0376703, -0.0078045, 0.0305697, 0.0051756, -0.0791314, -0.1409025, -0.1104446, -0.0085537, 0.0750858, 0.0772543, 0.0249194, -0.0003372, 0.0412532, 0.1008867, 0.0972873, 0.0098153, -0.0935526, -0.1259018, -0.0734856, -0.0057321, 0.0049114, -0.0375403, -0.0599264, -0.0073407, 0.0891595, 0.1427281, 0.1031151, 0.0106021, -0.0478543, -0.033229, 0.0078355, 0.0019451, -0.064175, -0.1245481, -0.1063166, -0.0127819, 0.0778025, 0.0942185, 0.0453659, 0.0051683, 0.0253327, 0.0764251, 0.0829865, 0.0110154, -0.0884744, -0.1305505, -0.0866159, -0.0118141, 0.0179601, -0.0111762, -0.0391564, -0.0052832, 0.0778941, 0.1343461, 0.105704, 0.0155544, -0.0551045, -0.0550971, -0.0140157, -0.0027624, -0.049718, -0.1054932, -0.0979637, -0.0151988, 0.0779075, 0.1063507, 0.0628388, 0.0111811, 0.0111702, 0.0516962, 0.0659832, 0.0101732, -0.0817143, -0.1303271, -0.095446, -0.0173415, 0.0284776, 0.0130628, -0.01787, -0.0014987, 0.0662389, 0.1222111, 0.1037877, 0.0190703, -0.0597203, -0.0730212, -0.034128, -0.0084416, -0.0361558, -0.0846961, -0.086031, -0.0156838, 0.0758855, 0.113749, 0.0768147, 0.0171458, -0.0010841, 0.0275899, 0.0471784, 0.0074545, -0.0738213, -0.1257646, -0.0999119, -0.0218564, 0.03659, 0.0345615, 0.0030102, 0.0036186, 0.054661, 0.1071516, 0.0977675, 0.0208986, -0.0620489, -0.0868024, -0.0517918, -0.0145706, -0.0237715, -0.0630697, -0.0712662, -0.0142603, 0.0722097, 0.1166685, 0.0869604, 0.0225596, -0.0113883, 0.0048734, 0.0274793, 0.00314, -0.0652746, -0.1174854, -0.100137, -0.0250056, 0.0424969, 0.052871, 0.0226505, 0.0096132, 0.0435208, 0.0899987, 0.0881783, 0.0209177, -0.06246, -0.0964121, -0.0664537, -0.0206343, -0.0127321, -0.0414419, -0.0544673, -0.0110713, 0.0673235, 0.1154914, 0.0931255, 0.0269892, -0.0197998, -0.0158335, 0.0077491, -0.0024118, -0.0564783, -0.1061689, -0.096406, -0.0265441, 0.0464557, 0.0677167, 0.0403451, 0.0160097, 0.033083, 0.071559, 0.075654, 0.0191273, -0.0613304, -0.1019802, -0.0777358, -0.0261636, -0.0031132, -0.0205418, -0.0364484, -0.0063591, 0.0616295, 0.1107054, 0.0953417, 0.030099, -0.0264421, -0.0340543, -0.0112208, -0.0087909, -0.0477607, -0.0925259, -0.0891475, -0.0263507, 0.0487458, 0.0789759, 0.0555255, 0.0223391, 0.0235243, 0.0525916, 0.0609005, 0.0156469, -0.0590127, -0.1037591, -0.0854257, -0.0307523, 0.0050876, -0.0009886, -0.0180107, -0.0004517, 0.0554671, 0.1028597, 0.0937946, 0.0316545, -0.031487, -0.0494537, -0.0287306, -0.0155543, -0.0393628, -0.077257, -0.0788921, -0.0244153, 0.0496538, 0.0866643, 0.067768, 0.0281578, 0.0149338, 0.0337762, 0.0446482, 0.0106901, -0.0558279, -0.1021013, -0.0894694, -0.0340699, 0.0119411, 0.0167271, 9.95e-05, 0.0062723, 0.0491146, 0.092543, 0.0888028, 0.0315236, -0.035141, -0.0618402, -0.0442034, -0.0222645, -0.0314527, -0.0610354, -0.066245, -0.0208299, 0.0494607, 0.0909239, 0.0768069, 0.0330809, 0.0073376, 0.015707, 0.0276274, 0.0045459, -0.0520573, -0.0974397, -0.0899675, -0.0358837, 0.017557, 0.032234, 0.0172097, 0.0134056, 0.0427884, 0.0803601, 0.0808007, 0.0296859, -0.0376153, -0.0711434, -0.0571876, -0.0285063, -0.0241319, -0.0444877, -0.0518596, -0.0157852, 0.0484169, 0.0919907, 0.0825201, 0.0367909, 0.0007081, -0.0011179, 0.0105416, -0.0024344, -0.0479262, -0.0902492, -0.0871459, -0.0360554, 0.0220739, 0.0452796, 0.0327374, 0.02053, 0.0366366, 0.0668978, 0.0702978, 0.026215, -0.039119, -0.0774053, -0.0673634, -0.0339044, -0.0174383, -0.0281681, -0.0363966, -0.0095415, 0.0467426, 0.0901793, 0.0849229, 0.0390476, -0.0050261, -0.0163095, -0.0059671, -0.0098721, -0.0436124, -0.0810318, -0.0813395, -0.0345421, 0.0256465, 0.0557296, 0.0462159, 0.0272499, 0.0307565, 0.0527159, 0.0578598, 0.0212709, -0.0398471, -0.0807672, -0.0745514, -0.0381501, -0.0113696, -0.0125587, -0.0205054, -0.0024155, 0.0446208, 0.0858642, 0.0841608, 0.0397061, -0.0099549, -0.0295763, -0.0213285, -0.0173762, -0.0392447, -0.0702965, -0.0729736, -0.0313987, 0.0284222, 0.0635461, 0.0572902, 0.0332034, 0.025196, 0.0383294, 0.0440819, 0.0150923, -0.0399634, -0.0814413, -0.0786951, -0.0410029, -0.0058885, 0.0019407, -0.0048006, 0.0052351, 0.0421875, 0.0794481, 0.0804811, 0.0387059, -0.0141821, -0.040726, -0.0350606, -0.0245618, -0.0349011, -0.0585313, -0.062529, -0.0267589, 0.0305406, 0.0687816, 0.0657245, 0.038078, 0.0199609, 0.0241897, 0.029555, 0.0079678, -0.0396047, -0.0797017, -0.0798568, -0.0423005, -0.0009332, 0.015021, 0.0101672, 0.013043, 0.0395428, 0.0713533, 0.0742205, 0.0360737, -0.0178138, -0.0496614, -0.0467876, -0.0310782, -0.0306265, -0.0461996, -0.0505261, -0.0208294, 0.0321221, 0.0715671, 0.0714049, 0.0416312, 0.0150352, 0.0106887, 0.014852, 0.000227, -0.0388751, -0.0758659, -0.078208, -0.0419687, 0.003562, 0.0264511, 0.0239155, 0.0206418, 0.0367495, 0.0620023, 0.065786, 0.0319206, -0.020941, -0.0563616, -0.0562325, -0.0366149, -0.0264323, -0.0337234, -0.0375022, -0.0138774, 0.0332577, 0.0720862, 0.0743221, 0.0436881, 0.0103822, -0.001852, 0.000506, -0.0077777, -0.0378408, -0.0702703, -0.0740024, -0.0400097, 0.0076661, 0.0360819, 0.036046, 0.0276835, 0.0338348, 0.0517978, 0.0556235, 0.0264223, -0.0236434, -0.0608792, -0.063225, -0.0409168, -0.0223054, -0.0214711, -0.023981, -0.0062051, 0.0340164, 0.0705724, 0.0745692, 0.0441499, 0.0059561, -0.0131845, -0.0130117, -0.0156976, -0.0365437, -0.0632658, -0.0675677, -0.0365034, 0.0114408, 0.0438399, 0.046256, 0.0338622, 0.0308074, 0.0411211, 0.0442082, 0.019815, -0.0259793, -0.0633271, -0.0677006, -0.0437987, -0.018225, -0.0097629, -0.0104651, 0.0018575, 0.0344387, 0.0672896, 0.0723296, 0.0429988, 0.0017176, -0.0231227, -0.0252938, -0.0231938, -0.0349977, -0.0551996, -0.0592846, -0.0316012, 0.0149282, 0.049711, 0.0543301, 0.0389155, 0.027655, 0.0303174, 0.0320201, 0.01238, -0.0279802, -0.0638586, -0.0696824, -0.0451402, -0.0141615, 0.0011404, 0.0025846, 0.0099714, 0.0345378, 0.0625149, 0.0678535, 0.0402843, -0.0023694, -0.0315476, -0.0360115, -0.0299567, -0.0331951, -0.0464013, -0.0495622, -0.0255069, 0.018158, 0.0537405, 0.0601484, 0.0426399, 0.0243554, 0.019688, 0.0195218, 0.0044219, -0.0296611, -0.0626668, -0.0692823, -0.0448939, -0.0100876, 0.0110363, 0.0147661, 0.0178107, 0.0343114, 0.0565367, 0.0614514, 0.0361255, -0.0063258, -0.0383989, -0.0449203, -0.0357221, -0.031121, -0.0371838, -0.0388297, -0.0184734, 0.0211375, 0.0560199, 0.0636803, 0.044899, 0.0208895, 0.0094962, 0.0071536, -0.0037377, -0.0310134, -0.0599666, -0.0666854, -0.0430851, -0.0059899, 0.0197709, 0.0257354, 0.0250666, 0.0337387, 0.0496377, 0.0534744, 0.030701, -0.0101529, -0.0436627, -0.0518529, -0.0402714, -0.0287497, -0.0278287, -0.0275137, -0.0107861, 0.0238515, 0.0566717, 0.0649705, 0.0456173, 0.0172379, -4.39e-05, -0.0046879, -0.0117791, -0.0320098, -0.0559825, -0.0621317, -0.0397983, -0.0018628, 0.0272452, 0.0352207, 0.031466, 0.0327889, 0.0420866, 0.0442932, 0.0242312, -0.0138387, -0.0473735, -0.0567263, -0.0434454, -0.0260572, -0.0185838, -0.016022, -0.0027434, 0.026273, 0.0558511, 0.0641397, 0.0447885, 0.0133929, -0.0087628, -0.0156589, -0.0194014, -0.0326167, -0.050949, -0.0559106, -0.0351799, 0.0022799, 0.0334047, 0.0430244, 0.0367826, 0.0314328, 0.0341399, 0.0342927, 0.0169762, -0.0173492, -0.0495999, -0.0595331, -0.0451485, -0.0230302, -0.0096675, -0.0047385, 0.0053465, 0.0283563, 0.0537285, 0.061368, 0.0424697, 0.0093641, -0.0165296, -0.0254681, -0.0263265, -0.0327886, -0.0450941, -0.0483414, -0.0294242, 0.0064048, 0.0382314, 0.0490155, 0.0408362, 0.0296385, 0.0260274, 0.0238502, 0.0092179, -0.0206338, -0.0504354, -0.0603303, -0.0453417, -0.0196612, -0.0012584, 0.0059944, 0.0131863, 0.0300456, 0.0504829, 0.0568805, 0.0387707, 0.0051731, -0.0232568, -0.0338906, -0.0323156, -0.0324807, -0.0386368, -0.0397576, -0.0227598, 0.0104661, 0.0417461, 0.0531378, 0.0435057, 0.0273852, 0.0179551, 0.0133238, 0.0012436, -0.0236335, -0.0500004, -0.0592398, -0.0440502, -0.0159613, 0.0064985, 0.0158818, 0.0205019, 0.0312846, 0.0463024, 0.0509434, 0.033855, 0.0008628, -0.0288907, -0.0407646, -0.0371763, -0.0316557, -0.0317868, -0.0305026, -0.0154465, 0.0143981, 0.0439954, 0.0553986, 0.0447282, 0.0246675, 0.010106, 0.003049, -0.0066559, -0.0262753, -0.0484263, -0.0564315, -0.0413544, -0.0119609, 0.0134869, 0.0246763, 0.0270436, 0.0320121, 0.0413696, 0.0438442, 0.0279249, -0.0035056, -0.0334065, -0.0459871, -0.0407615, -0.0302811, -0.0247332, -0.0209085, -0.0077567, 0.0181228, 0.0450462, 0.0558602, 0.0444934, 0.0214907, 0.0026318, -0.0066786, -0.0142069, -0.0284841, -0.045853, -0.0521137, -0.0373808, -0.0077041, 0.0196264, 0.03219, 0.0326048, 0.0321758, 0.0358619, 0.0358815, 0.02121, -0.0078598, -0.0368108, -0.0495207, -0.0429816, -0.0283418, -0.0176486, -0.0112898, 3.6e-05, 0.0215577, 0.044988, 0.0546407, 0.0428499, 0.0178834, -0.004339, -0.0156047, -0.0211605, -0.0301884, -0.0424295, -0.046528, -0.0323029, -0.003258, 0.0248604, 0.0382885, 0.0370227, 0.0317351, 0.0299517, 0.02736, 0.0139642, -0.0121095, -0.0391284, -0.0513804, -0.0437998, -0.0258409, -0.0106893, -0.00194, 0.0076605, 0.0246112, 0.0439183, 0.0518971, 0.0398939, 0.0138932, -0.0107009, -0.0235177, -0.0272952, -0.0313185, -0.0383002, -0.0399312, -0.0263242, 0.0012951, 0.0291559, 0.04289, 0.0401787, 0.0306599, 0.0237956, 0.0185719, 0.0064471, -0.0161574, -0.0404035, -0.0516295, -0.0432277, -0.0227968, -0.0039871, 0.0068852, 0.0148668, 0.0271955, 0.0419445, 0.0478188, 0.0357624, 0.0095836, -0.0163761, -0.0302589, -0.0324323, -0.0318186, -0.0336098, -0.0325896, -0.0196706, 0.0058621, 0.0325049, 0.0459687, 0.0420088, 0.0289424, 0.0175408, 0.0097939, -0.0010841, -0.019904, -0.0406978, -0.0503788, -0.0413307, -0.0192528, 0.002341, 0.0149648, 0.02143, 0.0292299, 0.0391825, 0.0426232, 0.0306323, 0.0050403, -0.0213033, -0.0357147, -0.0364345, -0.0316485, -0.0284998, -0.024773, -0.0125864, 0.0103353, 0.0349138, 0.0475433, 0.042496, 0.0265944, 0.0113227, 0.0012826, -0.0083783, -0.0232457, -0.0400807, -0.0477704, -0.0382144, -0.0152712, 0.0081967, 0.0221163, 0.0271539, 0.0306405, 0.0357462, 0.0365385, 0.0247042, 0.0003613, -0.0254411, -0.0398182, -0.0392072, -0.0307828, -0.0231018, -0.0167383, -0.0053159, 0.014604, 0.0364053, 0.0476745, 0.0416688, 0.0236458, 0.005259, -0.006739, -0.0152084, -0.0260872, -0.0386324, -0.043975, -0.0340211, -0.0109308, 0.0135018, 0.0282037, 0.0318846, 0.0313722, 0.0317533, 0.0298014, 0.0181968, -0.0043449, -0.0287681, -0.0425508, -0.0407068, -0.0292209, -0.0175431, -0.0087298, 0.0019003, 0.0185576, 0.0370185, 0.0464634, 0.0396031, 0.0201527, -0.0005414, -0.0140774, -0.0213716, -0.0283431, -0.0364414, -0.0391865, -0.0289269, -0.0063313, 0.0181898, 0.033129, 0.035506, 0.0313869, 0.0273213, 0.0226501, 0.0113423, -0.0089586, -0.0312736, -0.043931, -0.0409313, -0.026983, -0.0119431, -0.0009725, 0.0088317, 0.022085, 0.0367986, 0.0440368, 0.0364097, 0.0161886, -0.0059843, -0.0205739, -0.0266948, -0.0299389, -0.033597, -0.0336065, -0.0231257, -0.0015824, 0.0222114, 0.0368354, 0.0379438, 0.0306647, 0.0225616, 0.0153111, 0.0043688, -0.0133607, -0.0329633, -0.0440149, -0.0399211, -0.0241092, -0.0064095, 0.0063378, 0.0152726, 0.025088, 0.0358038, 0.0405474, 0.0322319, 0.0118444, -0.0109899, -0.0261097, -0.0310451, -0.030822, -0.0301957, -0.0274454, -0.0168269, 0.0031972, 0.0255331, 0.0393082, 0.0391698, 0.0292128, 0.0175865, 0.0080007, -0.0025004, -0.0174339, -0.033856, -0.0428926, -0.0377586, -0.0206654, -0.0010458, 0.0130301, 0.0210403, 0.0274795, 0.0341002, 0.0361666, 0.0272415, 0.0072297, -0.0154867, -0.030598, -0.0343248, -0.0309579, -0.0263355, -0.0209136, -0.0102489, 0.00788, 0.0281297, 0.0405633, 0.0391933, 0.0270581, 0.0125035, 0.0009174, -0.0090543, -0.021064, -0.0339778, -0.040676, -0.0345558, -0.016734, 0.004056, 0.0189644, 0.0259823, 0.0291871, 0.0317576, 0.0310725, 0.0216237, 0.0024619, -0.0194181, -0.0339896, -0.0364752, -0.0303329, -0.0221125, -0.0142108, -0.0036036, 0.0123426, 0.0299922, 0.0406508, 0.0380625, 0.0242496, 0.0074131, -0.0057657, -0.0151067, -0.0241526, -0.0333686, -0.0375004, -0.030454, -0.0124145, 0.0088141, 0.0240352, 0.0299835, 0.0301616, 0.0288554, 0.0254517, 0.0155773, -0.0023335, -0.0227394, -0.0362699, -0.0374801, -0.0289606, -0.017628, -0.0075297, 0.0029024, 0.0164644, 0.0311232, 0.0396494, 0.0358625, 0.0208607, 0.0024151, -0.0118957, -0.020493, -0.0266132, -0.0320766, -0.0335168, -0.0256183, -0.007823, 0.0131521, 0.0281642, 0.0329608, 0.0303729, 0.0254765, 0.0194899, 0.0093057, -0.0070254, -0.0254141, -0.0374515, -0.0373564, -0.0268741, -0.0129796, -0.0010459, 0.0090767, 0.0201312, 0.0315329, 0.0376564, 0.0327039, 0.0169798, -0.0024, -0.0173479, -0.0250789, -0.0283769, -0.0301563, -0.0288819, -0.020224, -0.0030813, 0.0170071, 0.031306, 0.0348686, 0.0298131, 0.0217055, 0.0133647, 0.0030053, -0.0114888, -0.0274211, -0.0375775, -0.0361571, -0.0241292, -0.0082631, 0.0050853, 0.0147507, 0.0232462, 0.0312471, 0.0347915, 0.0287242, 0.0127127, -0.0069478, -0.0220265, -0.028764, -0.0293989, -0.0276737, -0.0237618, -0.0144582, 0.0016811, 0.0203253, 0.0334456, 0.0356993, 0.0285015, 0.0176344, 0.0072478, -0.0031334, -0.0156024, -0.0287494, -0.0367153, -0.0339676, -0.0208049, -0.0035754, 0.0107262, 0.0197762, 0.0257254, 0.0302994, 0.0311865, 0.0240798, 0.0081788, -0.0111479, -0.025859, -0.0314775, -0.0296533, -0.0246997, -0.0183204, -0.0085094, 0.0063326, 0.0230608, 0.034591, 0.0354745, 0.0264764, 0.0133542, 0.0012963, -0.0089355, -0.0192543, -0.0293974, -0.0349489, -0.0308949, -0.0169934, 0.0009934, 0.0157644, 0.024034, 0.0275042, 0.0287324, 0.0269799, 0.0189354, 0.0035021, -0.014932, -0.0288028, -0.0331837, -0.029138, -0.0213101, -0.0127163, -0.0025592, 0.0107489, 0.0251828, 0.034778, 0.0342498, 0.0237987, 0.0089569, -0.0043498, -0.0142477, -0.0223508, -0.0293789, -0.0323834, -0.0270709, -0.0128042, 0.0053568, 0.0201108, 0.0274364, 0.0285424, 0.0266017, 0.0223193, 0.0134661, -0.0011871, -0.0182391, -0.0308403, -0.0338807, -0.0278767, -0.0175899, -0.0071038, 0.0032172, 0.01481, 0.0266697, 0.0340631, 0.032108, 0.0205505, 0.0045373, -0.0095649, -0.018936, -0.0248112, -0.0287166, -0.0291337, -0.0226424, -0.0083578, 0.0094315, 0.0236974, 0.0299219, 0.0288183, 0.0239685, 0.0173501, 0.0078462, -0.0057586, -0.0210161, -0.0319749, -0.0335933, -0.0259116, -0.0136247, -0.0016241, 0.0086604, 0.0184079, 0.0275105, 0.0325186, 0.029152, 0.0168266, 0.0001849, -0.014246, -0.0228949, -0.0265759, -0.0274437, -0.0253223, -0.0177636, -0.0037779, 0.0131449, 0.0264827, 0.0314629, 0.0283347, 0.0209023, 0.0122144, 0.002243, -0.0100902, -0.0232245, -0.0322359, -0.0323771, -0.0233072, -0.0095037, 0.0035951, 0.0136311, 0.0214524, 0.0277102, 0.030236, 0.025507, 0.0127376, -0.0040119, -0.0183096, -0.0260462, -0.027609, -0.0256075, -0.0210803, -0.0125976, 0.0008057, 0.0164299, 0.0284463, 0.032061, 0.027119, 0.0174823, 0.0070521, -0.0031821, -0.0140651, -0.0248343, -0.031669, -0.0303117, -0.0201472, -0.0053198, 0.0084382, 0.018008, 0.0238671, 0.0272821, 0.0273154, 0.0213095, 0.0084034, -0.0079676, -0.0216904, -0.0283366, -0.0278925, -0.0232619, -0.0165373, -0.0073048, 0.0052663, 0.0192286, 0.0295872, 0.0317421, 0.0252157, 0.0137895, 0.0019906, -0.0082841, -0.0175799, -0.0258276, -0.0303363, -0.0274944, -0.0165271, -0.0011622, 0.01281, 0.0216963, 0.0255977, 0.0262531, 0.0238652, 0.0167034, 0.0039463, -0.0116066, -0.0243472, -0.0297442, -0.0274341, -0.0204717, -0.011822, -0.0020396, 0.009485, 0.0214963, 0.0299275, 0.0305611, 0.0226921, 0.0099117, -0.0028522, -0.0129359, -0.0205487, -0.0262023, -0.0283169, -0.0240428, -0.0125574, 0.0028794, 0.0166305, 0.0246261, 0.0266117, 0.0246637, 0.0200026, 0.01184, -0.0005068, -0.0148574, -0.0262567, -0.030272, -0.0262628, -0.0173109, -0.007061, 0.0030497, 0.0133488, 0.0231968, 0.0295041, 0.0285926, 0.0196315, 0.0059392, -0.007369, -0.0170267, -0.0228994, -0.0259652, -0.0256979, -0.0200838, -0.0083554, 0.0067186, 0.0198369, 0.0267506, 0.0268952, 0.0225621, 0.0158434, 0.0068667, -0.0048331, -0.0176586, -0.0274136, -0.0299467, -0.0244253, -0.0138572, -0.002371, 0.0078307, 0.01676, 0.0243076, 0.0283693, 0.0259284, 0.0161291, 0.001961, -0.0114707, -0.0204716, -0.0245826, -0.0251379, -0.0225761, -0.0157509, -0.004041, 0.0102776, 0.0223876, 0.0280514, 0.0264589, 0.020009, 0.0115047, 0.0019262, -0.008918, -0.0199611, -0.0278335, -0.0288206, -0.0219898, -0.0101966, 0.0021385, 0.0121865, 0.0196365, 0.0248206, 0.0265918, 0.0226781, 0.0122939, -0.0019329, -0.0150802, -0.0232067, -0.0255693, -0.0237558, -0.0190558, -0.011184, 0.0002627, 0.013482, 0.0242562, 0.0285326, 0.0253334, 0.0170747, 0.0071019, -0.002845, -0.0126535, -0.0217243, -0.0275451, -0.0269632, -0.0190386, -0.0064174, 0.0063674, 0.0160162, 0.0219113, 0.0247378, 0.0242473, 0.0189588, 0.0082397, -0.0056556, -0.0181365, -0.0251904, -0.025849, -0.0218623, -0.0152414, -0.0065188, 0.0044382, 0.016268, 0.0254337, 0.0282201, 0.0235664, 0.0138339, 0.0027426, -0.007326, -0.0159469, -0.0229222, -0.0265927, -0.0244603, -0.0156653, -0.0026073, 0.0102313, 0.0192426, 0.0235394, 0.0240773, 0.0214222, 0.0148945, 0.0040827, -0.0091281, -0.0205972, -0.0264069, -0.0254346, -0.0195153, -0.01124, -0.0018873, 0.008375, 0.0185837, 0.0259296, 0.0271629, 0.0212258, 0.0103703, -0.0014703, -0.0114087, -0.0187209, -0.0235424, -0.0250351, -0.0214136, -0.0119762, 0.001143, 0.0136553, 0.021807, 0.0244948, 0.0228695, 0.0182101, 0.0106145, -5.84e-05, -0.0122743, -0.0224332, -0.0268601, -0.0243574, -0.016781, -0.0071578, 0.0025852, 0.0119705, 0.0203853, 0.0257655, 0.0254253, 0.0183921, 0.0067705, -0.0054435, -0.0150008, -0.0209129, -0.0235835, -0.0229389, -0.0179309, -0.008081, 0.0047479, 0.01658, 0.0236715, 0.0247693, 0.0211551, 0.0147052, 0.0062433, -0.0040715, -0.0150296, -0.0236319, -0.0265745, -0.0226655, -0.0137321, -0.0030943, 0.0067872, 0.0151378, 0.0216445, 0.0249789, 0.0230873, 0.0151566, 0.003121, -0.0090957, -0.0180314, -0.022482, -0.0230606, -0.0203809, -0.0141271, -0.0040916, 0.0081272, 0.0189621, 0.024822, 0.0243783, 0.0189887, 0.0110065, 0.0019034, -0.0078512, -0.0173395, -0.0241978, -0.0255958, -0.0204257, -0.0104501, 0.0008535, 0.0106188, 0.0178034, 0.022346, 0.02362, 0.0202422, 0.0116221, -0.0004885, -0.0123545, -0.0204457, -0.0234045, -0.0220007, -0.0174456, -0.0101215, -0.0001218, 0.0112048, 0.0207698, 0.0252615, 0.0233525, 0.0164337, 0.0072117, -0.0022898, -0.0113003, -0.019159, -0.0241463, -0.0239824, -0.0177157, -0.0070192, 0.0045974, 0.0139948, 0.0199095, 0.0224859, 0.0217468, 0.0169896, 0.0078932, -0.0039734, -0.0151616, -0.0222092, -0.023675, -0.0204415, -0.0142189, -0.0060288, 0.0037223, 0.0139155, 0.021988, 0.0250131, 0.0217398, 0.0135602, 0.0034139, -0.0062335, -0.0143371, -0.0204581, -0.0235086, -0.0218087, -0.014624, -0.0035245, 0.0080595, 0.0168498, 0.0214186, 0.0220766, 0.019429, 0.0134358, 0.0040778, -0.0072536, -0.0174726, -0.0233084, -0.0233094, -0.0184356, -0.0107925, -0.0019625, 0.0073401, 0.016204, 0.0226163, 0.0241182, 0.0196052, 0.0104475, -0.0002952, -0.0098344, -0.016892, -0.0212194, -0.0223275, -0.0191605, -0.0112493, -5.44e-05, 0.011169, 0.019132, 0.0223088, 0.0211427, 0.0167424, 0.0096906, 0.000284, -0.0102534, -0.019254, -0.0237451, -0.0223379, -0.0160428, -0.0072565, 0.0019706, 0.0106398, 0.0180242, 0.0226648, 0.0226301, 0.0170231, 0.0071772, -0.0038319, -0.0130142, -0.0189116, -0.0214372, -0.0206543, -0.016129, -0.0076911, 0.0033084, 0.0138687, 0.0208095, 0.0225769, 0.0197198, 0.0137661, 0.00586, -0.003388, -0.0129083, -0.020488, -0.0235404, -0.0208076, -0.0133315, -0.0036986, 0.0056756, 0.0135447, 0.0193458, 0.0221597, 0.0206175, 0.0140791, 0.0038332, -0.0071211, -0.0157111, -0.0203613, -0.0211225, -0.0185521, -0.0128131, -0.0040528, 0.0064844, 0.0161135, 0.0218688, 0.022239, 0.0178578, 0.0105848, 0.0020496, -0.0068423, -0.0151621, -0.02117, -0.0227315, -0.0187812, -0.0103785, -0.000206, 0.0090649, 0.0159893, 0.0201492, 0.0211374, 0.0181589, 0.0108668, 0.0005013, -0.0100937, -0.0178766, -0.0212212, -0.0202969, -0.016089, -0.0093135, -0.0004363, 0.0093989, 0.0178684, 0.0223105, 0.0213244, 0.0156138, 0.007283, -0.0016423, -0.0099926, -0.0169692, -0.0213064, -0.0213672, -0.0163295, -0.0072625, 0.0031419, 0.0120659, 0.0179238, 0.0204283, 0.0196432, 0.0153381, 0.007481, -0.0027375, -0.0126936, -0.0194803, -0.0214891, -0.0189947, -0.0133383, -0.0057285, 0.0030632, 0.0119885, 0.0191135, 0.0221534, 0.019879, 0.013054, 0.0039431, -0.0051267, -0.0127668, -0.0182987, -0.0209185, -0.0195103, -0.0135347, -0.0040654, 0.0062722, 0.0146203, 0.0193162, 0.0201923, 0.0177339, 0.0122468, 0.00402, -0.005805, -0.0148752, -0.0205085, -0.0211811, -0.0172633, -0.0103792, -0.0021563, 0.0063549, 0.0141972, 0.019841, 0.0214306, 0.0179623, 0.0102526, 0.0006481, -0.0083218, -0.0151031, -0.0191304, -0.0200371, -0.0172324, -0.0104853, -0.0008708, 0.0091177, 0.0166819, 0.020148, 0.0194609, 0.0154722, 0.0089777, 0.0005798, -0.0086284, -0.0166022, -0.0209602, -0.0203253, -0.015157, -0.0072899, 0.0013118, 0.009358, 0.0159801, 0.0200533, 0.0201863, 0.0156419, 0.0072854, -0.0025262, -0.0111597, -0.0169553, -0.0194571, -0.0187025, -0.0146111, -0.0072712, 0.0022428, 0.0116236, 0.0182217, 0.0204183, 0.0182667, 0.0129256, 0.0056221, -0.0027492, -0.0111455, -0.0178534, -0.0208523, -0.0189661, -0.0127399, -0.0041493, 0.0045924, 0.0120045, 0.0173062, 0.0197681, 0.0184782, 0.012996, 0.0042322, -0.005509, -0.0135849, -0.0182931, -0.0192871, -0.0169667, -0.0117309, -0.0039856, 0.0051988, 0.0137433, 0.0192251, 0.0201421, 0.0166547, 0.0101681, 0.0022717, -0.0058814, -0.0133008, -0.0186175, -0.0202137, -0.0171596, -0.0100832, -0.0010362, 0.0076089, 0.0142356, 0.0181546, 0.0190114, 0.0163715, 0.0101081, 0.0011736, -0.0082353, -0.0155531, -0.0190996, -0.0186383, -0.0148864, -0.0086774, -0.0007191, 0.0079267, 0.0154405, 0.0196902, 0.0193464, 0.0146769, 0.0072731, -0.0009891, -0.008741, -0.0150507, -0.0188948, -0.0190845, -0.0149697, -0.00726, 0.0019773, 0.0102974, 0.0160087, 0.0185182, 0.0178194, 0.0139377, 0.0070632, -0.0018142, -0.010651, -0.0170367, -0.0193743, -0.0175418, -0.012525, -0.0055357, 0.0024433, 0.0103655, 0.0166923, 0.0196311, 0.0180734, 0.012395, 0.0043156, -0.0040814, -0.0112635, -0.0163642, -0.0186991, -0.0175172, -0.0124708, -0.0043478, 0.0048221, 0.0126047, 0.0172954, 0.018404, 0.0162393, 0.0112551, 0.0039495, -0.0046568, -0.0127096, -0.0180199, -0.0191312, -0.0160402, -0.0099514, -0.0023911, 0.0054203, 0.0124613, 0.0174851, 0.0190736, 0.0163761, 0.0098773, 0.0013707, -0.0069332, -0.0133938, -0.0172205, -0.0180525, -0.0155716, -0.0097414, -0.0014237, 0.0074353, 0.0144886, 0.018079, 0.0178282, 0.0143231, 0.0084028, 0.0008522, -0.0072861, -0.0143744, -0.0184999, -0.018396, -0.0141832, -0.0072347, 0.0006772, 0.008141, 0.0141715, 0.017817, 0.0180533, 0.014315, 0.0071938, -0.0014926, -0.0094843, -0.0150917, -0.0176125, -0.0169878, -0.0133134, -0.0068618, 0.0014386, 0.009764, 0.0159218, 0.01836, 0.0168208, 0.0121301, 0.0054601, -0.0021485, -0.0096428, -0.0156217, -0.0184884, -0.0172089, -0.01203, -0.0044466, 0.0035952, 0.0105443, 0.0154657, 0.017699, 0.0166187, 0.0119602, 0.0044192, -0.0042073, -0.0116835, -0.0163301, -0.0175456, -0.0155479, -0.0108154, -0.0039153, 0.0041668, 0.0117614, 0.016888, 0.018151, 0.015422, 0.009725, 0.0025069, -0.0049758, -0.0116745, -0.0164357, -0.0180076, -0.015619, -0.0096457, -0.0016582, 0.0062948, 0.0125781, 0.016323, 0.0171496, 0.0148238, 0.0093851, 0.0016277, -0.0067123, -0.0134902, -0.0170935, -0.0170352, -0.0137805, -0.0081503, -0.0009817, 0.0066957, 0.0133914, 0.0173831, 0.0174762, 0.0136789, 0.0071728, -0.000383, -0.007563, -0.0133401, -0.0168132, -0.01709, -0.0136836, -0.0070976, 0.0010639, 0.008719, 0.014205, 0.0167363, 0.0161985, 0.0127295, 0.0066658, -0.0011098, -0.0089561, -0.0148777, -0.0173824, -0.0161098, -0.0117415, -0.0053928, 0.001863, 0.008968, 0.0146291, 0.017417, 0.0163734, 0.0116487, 0.0045421, -0.0031394, -0.0098523, -0.0146097, -0.016762, -0.0157793, -0.0114687, -0.0044576, 0.0036548, 0.0108182, 0.0153984, 0.0167102, 0.014885, 0.0104033, 0.0038807, -0.0037239, -0.0108923, -0.0158287, -0.0172077, -0.0148071, -0.0094913, -0.0026174, 0.0045465, 0.0109326, 0.0154578, 0.0170081, 0.0148883, 0.0093929, 0.0019004, -0.0056978, -0.0117943, -0.0154626, -0.0162982, -0.0141248, -0.009043, -0.0017961, 0.0060562, 0.0125537, 0.0161439, 0.0162588, 0.013253, 0.0079125, 0.0011045, -0.0061518, -0.012485, -0.0163385, -0.0165924, -0.0131722, -0.0070918, 0.0001067, 0.0070062, 0.0125501, 0.0158724, 0.016186, 0.0130748, 0.0069759, -0.0006883, -0.0080041, -0.0133538, -0.0158919, -0.0154489, -0.0121832, -0.0064781, 0.0008183, 0.0082169, 0.0138989, 0.0164415, 0.0154094, 0.0113553, 0.0053272, -0.0015906, -0.0083382, -0.013709, -0.0164144, -0.0155716, -0.0112594, -0.0046079, 0.0027126, 0.0091866, 0.0137912, 0.0158787, 0.0149906, 0.0109949, 0.0044669, -0.0031614, -0.0100105, -0.0145053, -0.0159011, -0.0142494, -0.0100167, -0.0038481, 0.0033189, 0.0100915, 0.0148358, 0.0163009, 0.014197, 0.0092483, 0.0027171, -0.0041366, -0.0102339, -0.014546, -0.0160723, -0.0141879, -0.0091271, -0.0021043, 0.0051397, 0.0110418, 0.0146357, 0.0154904, 0.0134666, 0.0087128, 0.0019327, -0.0054624, -0.0116795, -0.0152346, -0.0155035, -0.0127412, -0.0076878, -0.0012227, 0.0056462, 0.011645, 0.0153589, 0.0157436, 0.0126647, 0.0069907, 0.0001475, -0.0064745, -0.0118011, -0.0149905, -0.015339, -0.0124919, -0.0068371, 0.000358, 0.0073361, 0.0125377, 0.0150768, 0.0147321, 0.0116669, 0.0062962, -0.0005608, -0.0075417, -0.0129848, -0.0155416, -0.0147246, -0.0109736, -0.0052629, 0.0013294, 0.0077465, 0.012851, 0.0154735, 0.0148023, 0.0108643, 0.0046446, -0.0023184, -0.0085516, -0.0130109, -0.015046, -0.0142504, -0.0105417, -0.0044553, 0.0027182, 0.0092556, 0.0136497, 0.0151168, 0.0136364, 0.0096488, 0.0038143, -0.0029497, -0.0093546, -0.013908, -0.0154346, -0.0135974, -0.0089993, -0.0028068, 0.0037444, 0.0095724, 0.0136913, 0.015193, 0.0135162, 0.0088508, 0.0022712, -0.0046228, -0.0103242, -0.0138439, -0.0147243, -0.0128475, -0.0083971, -0.0020452, 0.0049222, 0.0108621, 0.0143646, 0.0147684, 0.0122414, 0.0074707, 0.001333, -0.0051774, -0.0108666, -0.0144427, -0.0149335, -0.0121628, -0.0068744, -0.000381, 0.0059665, 0.0110879, 0.0141592, 0.0145417, 0.0119329, 0.0066836, -7.06e-05, -0.0067163, -0.0117602, -0.0142933, -0.0140479, -0.0111794, -0.0061221, 0.0003301, 0.0069214, 0.0121293, 0.0146811, 0.0140554, 0.0105938, 0.005195, -0.001083, -0.0071919, -0.0120516, -0.0145924, -0.0140684, -0.0104699, -0.004658, 0.0019537, 0.0079455, 0.0122647, 0.0142566, 0.0135517, 0.0101064, 0.0044248, -0.0023224, -0.0085539, -0.0128353, -0.0143604, -0.0130461, -0.0092992, -0.0037814, 0.0026096, 0.0086725, 0.0130386, 0.0146066, 0.0130086, 0.0087433, 0.0028824, -0.0033735, -0.0089481, -0.0128906, -0.0143682, -0.0128754, -0.0085706, -0.0024079, 0.0041432, 0.0096401, 0.0130841, 0.0139935, 0.0122609, 0.008093, 0.0021352, -0.0044323, -0.010101, -0.0135368, -0.0140573, -0.0117553, -0.0072614, -0.0014371, 0.0047394, 0.0101419, 0.0135829, 0.0141593, 0.0116669, 0.0067427, 0.0005914, -0.0054858, -0.0104115, -0.0133764, -0.0137922, -0.0113997, -0.0065215, -0.0001812, 0.0061396, 0.0110194, 0.0135391, 0.0133908, 0.0107149, 0.0059527, -0.0001251, -0.006353, -0.0113314, -0.0138627, -0.013406, -0.0102191, -0.0051248, 0.0008495, 0.0066693, 0.0113027, 0.0137639, 0.0133669, 0.0100768, 0.0046486, -0.0016207, -0.0073717, -0.0115541, -0.0135093, -0.0128931, -0.0096911, -0.0043817, 0.0019665, 0.0078999, 0.0120596, 0.0136303, 0.0124751, 0.0089625, 0.0037462, -0.002298, -0.0080425, -0.0122263, -0.0138193, -0.0124353, -0.008484, -0.0029461, 0.0030218, 0.0083565, 0.0121365, 0.0135909, 0.0122629, 0.008287, 0.0025157, -0.0037022, -0.0089922, -0.0123583, -0.0132982, -0.0117059, -0.0078022, -0.0022086, 0.0039852, 0.0093904, 0.0127486, 0.0133688, 0.0112803, 0.0070554, 0.001532, -0.0043319, -0.0094677, -0.0127782, -0.0134234, -0.0111821, -0.0066003, -0.0007812, 0.0050297, 0.0097675, 0.0126354, 0.0130838, 0.0108892, 0.0063515, 0.0003989, -0.0056069, -0.0103176, -0.0128167, -0.0127618, -0.010273, -0.0057899, -6e-05, 0.0058289, 0.010585, 0.0130836, 0.0127755, 0.0098475, 0.0050485, -0.0006319, -0.0061789, -0.0106022, -0.0129868, -0.0127, -0.0096899, -0.0046217, 0.0013155, 0.0068274, 0.0108755, 0.0127983, 0.0122684, 0.0092924, 0.0043262, -0.0016485, -0.0072935, -0.0113249, -0.0129293, -0.0119247, -0.0086396, -0.0037105, 0.0020094, 0.0074568, 0.0114648, 0.0130696, 0.0118767, 0.0082203, 0.0029954, -0.0026922, -0.0077981, -0.0114276, -0.0128601, -0.0116801, -0.008005, -0.0026003, 0.0032951, 0.0083779, 0.0116633, 0.0126331, 0.0111769, 0.0075215, 0.0022657, -0.0035788, -0.0087295, -0.012002, -0.0127066, -0.0108187, -0.0068542, -0.0016196, 0.0039501, 0.0088372, 0.0120218, 0.0127223, 0.0107076, 0.0064468, 0.0009488, -0.0046012, -0.0091574, -0.0119355, -0.0124159, -0.0104032, -0.0061787, -0.0005888, 0.0051129, 0.0096521, 0.0121233, 0.0121568, 0.0098488, 0.0056303, 0.0002248, -0.0053469, -0.0098888, -0.0123453, -0.0121675, -0.0094823, -0.0049685, 0.000428, 0.0057162, 0.0099436, 0.0122545, 0.012064, 0.0093089, 0.0045778, -0.0010394, -0.0063152, -0.0102307, -0.0121237, -0.0116773, -0.008912, -0.0042634, 0.0013621, 0.0067292, 0.010628, 0.0122551, 0.0113918, 0.0083261, 0.0036713, -0.0017444, -0.0069141, -0.0107529, -0.012359, -0.0113367, -0.0079564, -0.0030328, 0.0023822, 0.007269, 0.0107579, 0.0121695, 0.0111237, 0.0077243, 0.0026625, -0.0029225, -0.0077991, -0.0110012, -0.0119991, -0.0106742, -0.0072528, -0.0023111, 0.0032065, 0.0081126, 0.0112934, 0.0120683, 0.0103681, 0.0066541, 0.0016972, -0.0035948, -0.0082492, -0.011313, -0.0120573, -0.0102475, -0.006287, -0.0010976, 0.0041974, 0.0085775, 0.0112712, 0.0117825, 0.0099378, 0.0060023, 0.0007519, -0.0046577, -0.0090246, -0.0114615, -0.0115772, -0.0094433, -0.0054759, -0.0003742, 0.0049008, 0.0092371, 0.0116444, 0.01158, 0.0091217, 0.0048819, -0.0002403, -0.0052824, -0.009326, -0.0115664, -0.0114602, -0.0089377, -0.0045216, 0.0007881, 0.005832, 0.0096159, 0.0114805, 0.0111145, 0.0085462, 0.0041925, -0.0011063, -0.0062068, -0.0099703, -0.0116104, -0.0108786, -0.0080234, -0.0036308, 0.0014985, 0.006408, 0.0100846, 0.0116838, 0.0108135, 0.007691, 0.0030564, -0.0020941, -0.0067705, -0.0101269, -0.0115187, -0.0105947, -0.0074487, -0.0027077, 0.00258, 0.0072526, 0.0103689, 0.0113921, 0.0101931, 0.0069925, 0.002344, -0.0028673, -0.007539, -0.0106244, -0.0114567, -0.0099313, -0.0064576, -0.001767, 0.0032617, 0.0076975, 0.0106456, 0.0114247, 0.0098, 0.0061197, 0.0012261, -0.0038203, -0.008029, -0.0106426, -0.0111837, -0.0094947, -0.0058265, -0.0008935, 0.0042363, 0.0084316, 0.0108281, 0.0110196, 0.0090523, 0.0053236, 0.0005069, -0.0044898, -0.0086292, -0.0109818, -0.0110161, -0.0087689, -0.0047914, 6.64e-05, 0.0048734, 0.0087436, 0.0109166, 0.010885, 0.0085747, 0.0044531, -0.0005628, -0.0053796, -0.0090333, -0.0108696, -0.0105804, -0.008197, -0.0041175, 0.0008752, 0.0057207, 0.0093483, 0.0109923, 0.0103819, 0.0077281, 0.0035859, -0.0012729, -0.0059379, -0.0094593, -0.0110451, -0.0103103, -0.0074279, -0.0030696, 0.001825, 0.0062987, 0.0095296, 0.0109022, 0.0100896, 0.0071769, 0.0027356, -0.0022678, -0.0067397, -0.0097684, -0.0108132, -0.0097345, -0.0067426, -0.0023686, 0.0025558, 0.0070035, 0.0099909, 0.0108695, 0.0095059, 0.0062613, 0.0018265, -0.002952, -0.0071819, -0.0100193, -0.0108255, -0.0093685, -0.0059496, -0.0013382, 0.0034667, 0.0075084, 0.0100453, 0.0106145, 0.0090702, 0.0056498, 0.0010139, -0.0038488, -0.0078743, -0.0102255, -0.0104858, -0.0086771, -0.0051755, -0.000627, 0.0041083, 0.0080596, 0.0103538, 0.0104736, 0.0084224, 0.0046948, 9.16e-05, -0.0044904, -0.0081968, -0.010305, -0.0103393, -0.0082235, -0.0043764, 0.0003589, 0.0049543, 0.0084791, 0.0102868, 0.0100704, 0.0078607, 0.0040371, -0.0006689, -0.0052711, -0.0087628, -0.0104033, -0.0099045, -0.0074422, -0.0035392, 0.0010636, 0.0054983, 0.0088714, 0.0104388, 0.0098249, 0.0071658, 0.0030707, -0.0015768, -0.0058551, -0.0089662, -0.0103202, -0.0096097, -0.0069122, -0.0027511, 0.0019813, 0.0062571, 0.009196, 0.0102589, 0.0092942, 0.0064998, 0.0023832, -0.0022718, -0.0065056, -0.0093939, -0.0103089, -0.0090947, -0.0060682, -0.0018784, 0.0026619, 0.0066972, 0.0094287, 0.0102557, 0.0089508, 0.0057753, 0.0014328, -0.0031384, -0.0070174, -0.0094798, -0.0100752, -0.0086657, -0.0054756, -0.0011178, 0.0034903, 0.0073487, 0.0096502, 0.0099723, 0.0083144, 0.0050285, 0.0007331, -0.0037562, -0.0075279, -0.0097611, -0.0099547, -0.0080849, -0.0045952, -0.0002365, 0.0041299, 0.0076805, 0.0097264, 0.0098194, 0.0078818, 0.0042911, -0.0001774, -0.0045578, -0.0079551, -0.0097332, -0.0095855, -0.007539, -0.0039546, 0.0004823, 0.0048527, 0.0082101, 0.0098401, 0.0094433, 0.0071627, 0.0034878, -0.0008721, -0.0050895, -0.0083209, -0.0098658, -0.0093598, -0.0069079, -0.003063, 0.0013463, 0.0054358, 0.0084323, 0.0097677, 0.0091511, 0.0066528, 0.0027536, -0.001721, -0.0058056, -0.0086539, -0.0097306, -0.0088736, -0.006266, -0.0023916, 0.00201, 0.0060402, 0.0088294, 0.0097719, 0.0086952, 0.0058756, 0.0019202, -0.0023929, -0.0062436, -0.0088738, -0.0097165, -0.0085497, -0.0056008, -0.0015135, 0.0028319, 0.0065522, 0.0089419, 0.0095614, 0.0082778, 0.0053022, 0.0012048, -0.0031611, -0.0068561, -0.0091044, -0.0094813, -0.0079658, -0.004885, -0.0008287, 0.0034285, 0.0070292, 0.0091997, 0.0094568, 0.0077548, 0.0044903, 0.0003664, -0.0037931, -0.0071952, -0.0091811, -0.0093263, -0.0075525, -0.0042008, 1.42e-05, 0.0041864, 0.0074577, 0.009205, 0.0091217, 0.0072287, 0.0038685, -0.000316, -0.0044657, -0.0076909, -0.0093049, -0.0090007, -0.006892, -0.0034345, 0.000695, 0.0047065, 0.0078027, 0.009322, 0.0089124, 0.006653, 0.003045, -0.0011355, -0.0050426, -0.0079286, -0.0092454, -0.0087153, -0.0064014, -0.0027472, 0.0014826, 0.0053815, 0.0081383, 0.0092249, 0.0084691, 0.0060383, 0.0023918, -0.0017713, -0.0056076, -0.0082982, -0.0092608, -0.0083102, -0.0056862, -0.0019548, 0.0021415, 0.0058167, 0.0083498, 0.0092037, 0.0081624, 0.0054245, 0.0015792, -0.0025487, -0.0061145, -0.0084329, -0.0090741, -0.0079079, -0.0051324, -0.0012794, 0.0028565, 0.0063922, 0.0085845, 0.0090097, 0.0076283, 0.0047421, 0.0009122, -0.0031259, -0.0065634, -0.0086706, -0.0089819, -0.0074348, -0.0043834, -0.0004843, 0.0034766, 0.0067366, 0.0086645, 0.0088563, 0.0072331, 0.0041047, 0.0001301, -0.0038413, -0.0069886, -0.0087036, -0.00868, -0.0069317, -0.0037816, 0.0001655, 0.0041054, 0.0072012, 0.0087943, 0.0085738, 0.0066271, 0.0033767, -0.0005336, -0.0043502, -0.0073169, -0.0088086, -0.008485, -0.0064038, -0.0030202, 0.0009406, 0.0046717, 0.0074509, 0.0087489, 0.0082984, 0.0061558, 0.0027308, -0.0012667, -0.0049858, -0.007651, -0.0087434, -0.0080825, -0.0058186, -0.0023872, 0.001551, 0.0052028, 0.0077959, 0.0087723, 0.0079371, 0.0054976, 0.0019801, -0.001909, -0.0054172, -0.0078568, -0.0087184, -0.0077916, -0.0052499, -0.0016336, 0.002285, 0.0057006, 0.0079486, 0.0086091, 0.0075526, 0.0049641, 0.0013403, -0.0025772, -0.0059583, -0.0080922, -0.0085595, -0.0073038, -0.0046024, -0.0009867, 0.0028441, 0.0061259, 0.0081693, 0.0085272, 0.0071229, 0.0042727, 0.0005885, -0.0031819, -0.0063057, -0.0081773, -0.0084106, -0.0069264, -0.0040063, -0.0002592, 0.0035186, 0.0065441, 0.0082253, 0.0082569, 0.0066445, 0.0036919, -3.16e-05, -0.0037724, -0.0067419, -0.0083101, -0.008165, -0.0063705, -0.0033171, 0.0003846, 0.004016, 0.0068592, 0.0083214, 0.0080745, 0.0061587, 0.0029869, -0.0007635, -0.0043246, -0.0070006, -0.0082791, -0.007902, -0.0059187, -0.0027083, 0.0010691, 0.0046146, 0.0071884, 0.0082829, 0.0077102, 0.0056043, 0.0023757, -0.0013503, -0.0048263, -0.0073239, -0.0083082, -0.0075786, -0.0053127, -0.001999, 0.0016922, 0.005041, 0.0073909, 0.0082568, 0.0074342, 0.0050752, 0.0016756, -0.0020424, -0.0053121, -0.0074908, -0.0081677, -0.0072136, -0.0048001, -0.0013915, 0.0023187, 0.0055501, 0.007624, 0.0081275, 0.0069896, 0.0044631, 0.0010506, -0.002584, -0.0057173, -0.0076972, -0.0080944, -0.0068216, -0.0041611, -0.000682, 0.0029055, 0.0058985, 0.007715, 0.0079852, 0.0066295, 0.0039039, 0.0003721, -0.0032196, -0.0061258, -0.0077719, -0.0078541, -0.0063694, -0.0036025, -8.94e-05, 0.003462, 0.0063087, 0.0078487, 0.0077714, 0.0061198, 0.0032535, -0.0002497, -0.0037051, -0.0064299, -0.0078614, -0.0076833, -0.0059205, -0.0029486, 0.0006006, 0.0039978, 0.0065736, 0.0078321, 0.0075227, 0.0056876, 0.0026783, -0.0008906, -0.004269, -0.0067521, -0.007845, -0.0073544, -0.0053973, -0.0023605, 0.0011649, 0.0044737, 0.0068778, 0.0078652, 0.007232, 0.0051292, 0.0020095, -0.0014926, -0.004689, -0.0069524, -0.00782, -0.0070928, -0.0049037, -0.0017086, 0.0018172, 0.0049453, 0.0070554, 0.0077463, 0.0068877, 0.004638, 0.0014318, -0.002082, -0.0051689, -0.0071818, -0.0077157, -0.0066876, -0.0043268, -0.001107, 0.0023414, 0.0053331, 0.0072501, 0.0076804, 0.0065289, 0.0040467, 0.0007632, -0.0026489, -0.0055159, -0.0072788, -0.007582, -0.0063447, -0.0038009, -0.0004727, 0.0029404, 0.00573, 0.0073396, 0.0074679, 0.006103, 0.0035111, 0.0001963, -0.0031753, -0.0059029, -0.0074121, -0.0073953, -0.0058774, -0.0031885, 0.0001259, 0.0034136, 0.0060248, 0.0074246, 0.0073079, 0.0056871, 0.0029036, -0.0004536, -0.0036929, -0.0061712, -0.007409, -0.0071619, -0.0054649, -0.0026441, 0.0007274, 0.0039452, 0.0063385, 0.0074265, 0.0070117, 0.0051951, 0.0023395, -0.0009964, -0.0041459, -0.0064588, -0.0074451, -0.0068996, -0.00495, -0.0020145, 0.0013069, 0.0043575, 0.0065374, 0.0074042, 0.0067642, 0.0047333, 0.0017312, -0.001611, -0.0046017, -0.0066441, -0.0073464, -0.0065767, -0.0044805, -0.0014646, 0.0018629, 0.0048105, 0.0067616, 0.007321, 0.0063952, 0.0041909, 0.0011539, -0.0021178, -0.0049741, -0.006829, -0.0072873, -0.0062472, -0.0039327, -0.0008353, 0.0024086, 0.0051544, 0.0068648, 0.007197, 0.0060694, 0.0036955, 0.0005597, -0.0026826, -0.0053582, -0.0069302, -0.0071002, -0.0058478, -0.0034205, -0.000293, 0.0029078, 0.0055203, 0.0069966, 0.0070337, 0.0056407, 0.0031199, -1.45e-05, -0.0031426, -0.005645, -0.0070124, -0.0069506, -0.0054613, -0.0028551, 0.000319, 0.0034062, 0.0057899, 0.007006, 0.0068161, 0.005248, 0.002604, -0.0005807, -0.0036442, -0.005949, -0.0070293, -0.0066842, -0.0049999, -0.0023155, 0.0008407, 0.0038387, 0.0060626, 0.0070446, 0.0065788, 0.0047727, 0.0020121, -0.0011367, -0.0040478, -0.006147, -0.0070109, -0.0064505, -0.004567, -0.0017469, 0.0014198, 0.0042777, 0.0062534, 0.0069642, 0.0062773, 0.0043252, 0.0014883, -0.0016625, -0.004476, -0.0063652, -0.0069455, -0.0061146, -0.0040582, -0.0011946, 0.0019093, 0.0046364, 0.0064301, 0.0069114, 0.0059738, 0.0038168, 0.0008963, -0.0021863, -0.0048152, -0.006474, -0.0068319, -0.0058059, -0.0035908, -0.0006367, 0.002442, 0.0050068, 0.0065402, 0.0067477, 0.0056007, 0.0033284, 0.0003777, -0.0026608, -0.0051621, -0.0066038, -0.0066887, -0.0054123, -0.0030506, -8.73e-05, 0.0028883, 0.0052865, 0.0066209, 0.0066081, 0.0052407, 0.0028013, -0.0001985, -0.0031393, -0.0054309, -0.0066248, -0.0064874, -0.0050393, -0.0025615, 0.0004466, 0.0033623, 0.0055802, 0.0066499, 0.0063689, 0.0048089, 0.0022866, -0.0006994, -0.003553, -0.0056908, -0.0066652, -0.006272, -0.0046, -0.0020053, 0.0009787, 0.0037559, 0.0057772, 0.0066363, 0.0061488, 0.0044026, 0.001754, -0.0012454, -0.0039749, -0.0058845, -0.0066016, -0.0059917, -0.0041746, -0.0015064, 0.0014769, 0.0041615, 0.0059891, 0.006586, 0.005843, 0.003926, 0.001227, -0.0017173, -0.004321, -0.0060546, -0.0065547, -0.0057116, -0.0037023, -0.0009499, 0.0019784, 0.0044948, 0.0061029, 0.0064832, 0.0055512, 0.0034848, 0.0007023, -0.0022203, -0.0046774, -0.0061713, -0.0064123, -0.0053638, -0.0032376, -0.0004539, 0.0024303, 0.0048245, 0.0062302, 0.0063574, 0.0051895, 0.0029783, 0.0001777, -0.0026524, -0.0049502, -0.0062515, -0.0062826, -0.005028, -0.0027455, 8.85e-05, 0.0028887, 0.0050908, 0.0062616, 0.0061721, 0.0048363, 0.0025142, -0.0003265, -0.0031008, -0.0052334, -0.0062902, -0.0060679, -0.0046248, -0.0022554, 0.0005692, 0.0032852, 0.0053393, 0.0063037, 0.0059762, 0.0044298, 0.0019922, -0.0008347, -0.0034837, -0.0054294, -0.006282, -0.0058613, -0.004243, -0.001756, 0.001084, 0.0036898, 0.0055342, 0.0062552, 0.0057166, 0.0040263, 0.0015171, -0.0013075, -0.0038685, -0.0056348, -0.0062444, -0.0055827, -0.0037972, -0.0012541, 0.0015385, 0.004024, 0.0056988, 0.006214, 0.0054575, 0.0035868, 0.0009937, -0.0017867, -0.0041945, -0.0057528, -0.0061524, -0.0053077, -0.0033804, -0.0007598, 0.0020136, 0.0043663, 0.0058202, 0.0060906, 0.0051342, 0.0031458, 0.00052, -0.0022178, -0.0045086, -0.0058776, -0.006042, -0.0049751, -0.0029058, -0.0002601, 0.0024312, 0.0046326, 0.0059004, 0.0059705, 0.0048207, 0.0026855, 9.2e-06, -0.002656, -0.0047711, -0.005918, -0.0058724, -0.0046412, -0.0024657, 0.0002168, 0.0028558, 0.0049052, 0.0059469, 0.0057782, 0.0044447, 0.00222, -0.0004516, -0.0030364, -0.0050094, -0.0059617, -0.0056938, -0.0042646, -0.0019757, 0.0007013, 0.0032272, 0.0050999, 0.0059444, 0.0055847, 0.0040858, 0.0017509, -0.0009372, -0.0034238, -0.0052041, -0.0059266, -0.0054542, -0.0038828, -0.0015235, 0.0011506, 0.003593, 0.0052986, 0.0059175, 0.005331, 0.0036691, 0.0012739, -0.0013742, -0.0037467, -0.0053641, -0.0058911, -0.0052143, -0.0034733, -0.0010314, 0.0016078, 0.0039108, 0.0054202, 0.0058364, 0.0050724, 0.0032754, 0.0008078, -0.0018234, -0.0040751, -0.0054884, -0.0057849, -0.0049144, -0.0030556, -0.0005791, 0.0020193, 0.0042107, 0.0055424, 0.0057393, 0.0047662, 0.002831, 0.0003326, -0.0022262, -0.0043348, -0.0055692, -0.0056741, -0.0046213, -0.0026248, -9.8e-05, 0.0024377, 0.0044684, 0.0055907, 0.0055848, 0.0044514, 0.0024135, -0.0001193, -0.0026288, -0.0045972, -0.005622, -0.0055019, -0.0042712, -0.0021833, 0.0003432, 0.002803, 0.0046974, 0.0056359, 0.005422, 0.0041021, 0.0019537, -0.0005804, -0.0029885, -0.0047901, -0.0056251, -0.0053214, -0.0039335, -0.0017421, 0.0008017, 0.0031735, 0.0048907, 0.0056126, 0.0052013, 0.0037415, 0.0015235, -0.0010078, -0.0033364, -0.0049824, -0.0056076, -0.0050901, -0.0035446, -0.0012895, 0.0012213, 0.0034857, 0.0050466, 0.0055827, 0.0049791, 0.0033596, 0.0010608, -0.0014433, -0.0036454, -0.0051066, -0.0055368, -0.0048475, -0.0031726, -0.0008492, 0.0016464, 0.0038002, 0.0051727, 0.0054917, 0.0047013, 0.0029646, 0.0006294, -0.0018364, -0.0039322, -0.0052263, -0.0054516, -0.0045655, -0.0027566, -0.000398, 0.0020341, 0.0040535, 0.0052545, 0.0053902, 0.0044273, 0.0025607, 0.0001761, -0.0022357, -0.0041842, -0.0052813, -0.0053115, -0.0042694, -0.0023609, 3.03e-05, 0.0024163, 0.0043057, 0.0053121, 0.005236, 0.0041015, 0.0021429, -0.0002459, -0.0025864, -0.004405, -0.0053279, -0.0051628, -0.0039451, -0.0019294, 0.0004687, 0.0027636, 0.0044965, 0.0053209, 0.0050681, 0.0037841, 0.0017276, -0.0006791, -0.0029404, -0.0045958, -0.0053152, -0.0049603, -0.0036051, -0.0015204, 0.0008755, 0.0030953, 0.0046824, 0.0053111, 0.0048573, 0.0034209, 0.0012987, -0.0010812, -0.003242, -0.0047481, -0.0052905, -0.0047543, -0.0032486, -0.0010853, 0.00129, 0.0033948, 0.0048085, 0.0052502, 0.0046303, 0.0030699, 0.0008827, -0.0014839, -0.0035433, -0.0048748, -0.0052133, -0.0044973, -0.0028757, -0.0006741, 0.0016657, 0.0036695, 0.0049258, 0.0051756, 0.0043702, 0.0026804, 0.0004547, -0.0018566, -0.00379, -0.0049577, -0.0051206, -0.0042411, -0.0024968, -0.0002466, 0.0020461, 0.0039152, 0.0049865, 0.0050492, 0.0040922, 0.0023055, 4.83e-05, -0.0022197, -0.0040325, -0.0050191, -0.0049828, -0.0039383, -0.0021018, 0.0001564, 0.0023831, 0.0041283, 0.0050346, 0.0049136, 0.0037909, 0.0019006, -0.0003681, -0.0025545, -0.0042207, -0.0050334, -0.0048271, -0.0036398, -0.0017104, 0.0005659, 0.0027211, 0.0043159, 0.005031, 0.004728, 0.003471, 0.0015121, -0.0007554, -0.0028709, -0.0044005, -0.0050304, -0.0046349, -0.003301, -0.0013045, 0.0009509, 0.0030125, 0.004465, 0.0050116, 0.0045371, 0.0031378, 0.0011027, -0.0011495, -0.0031608, -0.0045279, -0.0049789, -0.004423, -0.0029697, -0.0009112, 0.0013325, 0.0033008, 0.0045916, 0.0049464, 0.0042998, 0.0027863, 0.0007112, -0.0015088, -0.0034242, -0.0046427, -0.0049136, -0.0041831, -0.0026052, -0.0005056, 0.0016901, 0.003541, 0.0046754, 0.0048623, 0.0040601, 0.0024306, 0.000308, -0.0018711, -0.0036629, -0.0047082, -0.0048002, -0.0039223, -0.0022502, -0.0001198, 0.0020355, 0.0037739, 0.0047398, 0.0047393, 0.0037788, 0.0020576, -7.67e-05, -0.0021948, -0.0038691, -0.0047575, -0.0046763, -0.0036423, -0.00187, 0.0002753, 0.0023578, 0.0039593, 0.0047591, 0.0045954, 0.0034983, 0.0016886, -0.000464, -0.0025173, -0.0040528, -0.0047619, -0.0045067, -0.0033417, -0.0015013, 0.0006442, 0.0026599, 0.0041332, 0.0047619, 0.00442, 0.0031824, 0.0013049, -0.0008321, -0.0027987, -0.0041989, -0.0047475, -0.0043298, -0.0030302, -0.0011162, 0.0010188, 0.0029398, 0.004261, 0.0047193, 0.0042228, 0.0028698, 0.0009327, -0.0011941, -0.0030746, -0.0043247, -0.0046932, -0.0041107, -0.0026991, -0.0007437, 0.0013622, 0.0031927, 0.0043737, 0.0046625, 0.0040011, 0.0025287, 0.0005486, -0.0015367, -0.0033081, -0.0044094, -0.0046175, -0.0038868, -0.0023649, -0.0003632, 0.0017072, 0.0034243, 0.0044428, 0.0045611, 0.0037572, 0.0021929, 0.0001824, -0.0018655]}
\ No newline at end of file
packages/core/test/fixtures/fem_off_32.jsonadded+187−0View file
@@ -0,0 +1,187 @@
1+{
2+ "model": "Q4 Mindlin-Reissner FEM; sparse eigensolve; coupled Hunt-Crossley/Hertz impact",
3+ "config": {
4+ "width": 0.03,
5+ "height": 0.03,
6+ "thickness": 0.001,
7+ "density": 2700.0,
8+ "young": 68900000000.0,
9+ "poisson": 0.33,
10+ "material_q": 1400.0,
11+ "shear_correction": 0.8333333333333334,
12+ "mesh_x": 32,
13+ "mesh_y": 32,
14+ "hole_diameter": 0.0008,
15+ "hole_edge_offset": 0.0015,
16+ "thread_length": 0.2,
17+ "thread_dashpot": 0.00075,
18+ "hit_x": 0.006,
19+ "hit_y": -0.004,
20+ "hammer_mass": 0.01,
21+ "hammer_tip_radius": 0.002,
22+ "hammer_speed": 0.45,
23+ "hammer_young": 200000000000.0,
24+ "hammer_poisson": 0.29,
25+ "restitution": 0.62,
26+ "mic_distance": 0.35,
27+ "air_density": 1.204,
28+ "sound_speed": 343.0,
29+ "sample_rate": 96000,
30+ "duration": 4.0,
31+ "modes": 72,
32+ "normalize_dbfs": -1.0
33+ },
34+ "derived": {
35+ "plate_mass_kg": 0.002428642831973649,
36+ "fem_degrees_of_freedom": 3267,
37+ "retained_mode_count": 17,
38+ "low_frequency_momentum_modes": 3,
39+ "contact_duration_s": 7.928533294911626e-05,
40+ "impact_impulse_Ns": 0.0007650716967034133,
41+ "peak_contact_force_N": 15.556634955147148,
42+ "hammer_exit_velocity_m_s_positive_into_plate": 0.37349280323841183,
43+ "plate_contact_point_exit_velocity_m_s_positive_into_plate": 0.4359411428246839,
44+ "relative_separation_velocity_m_s": -0.06244833958627205,
45+ "effective_relative_restitution": 0.13877408796949345,
46+ "critical_frequency_hz": 12120.88938013311,
47+ "unnormalized_peak_pressure_pa_approx": 8.395916704400946e-08
48+ },
49+ "modes": [
50+ {
51+ "mode": 1,
52+ "frequency_hz": 3582.2869998473116,
53+ "q_total": 410.6918254530768,
54+ "hit_participation": 7.11451277412826,
55+ "support_participation": -6.065554441715668e-08,
56+ "radiation_participation": 6.645634834535202e-14
57+ },
58+ {
59+ "mode": 2,
60+ "frequency_hz": 6068.32451758857,
61+ "q_total": 335.06232166335684,
62+ "hit_participation": -4.989584956140311,
63+ "support_participation": 39.558657210801634,
64+ "radiation_participation": 1.2001453536424003e-09
65+ },
66+ {
67+ "mode": 3,
68+ "frequency_hz": 7312.682049880727,
69+ "q_total": 315.1810182134881,
70+ "hit_participation": -20.382034137931853,
71+ "support_participation": 2.8562978499150242,
72+ "radiation_participation": 5.968840035234103e-11
73+ },
74+ {
75+ "mode": 4,
76+ "frequency_hz": 9767.810886271465,
77+ "q_total": 281.25338656475304,
78+ "hit_participation": -12.078386225195494,
79+ "support_participation": -0.026688312099777546,
80+ "radiation_participation": -2.858240259192646e-13
81+ },
82+ {
83+ "mode": 5,
84+ "frequency_hz": 9767.81112324192,
85+ "q_total": 279.7596498232715,
86+ "hit_participation": -4.084890436262233,
87+ "support_participation": 39.414248289184,
88+ "radiation_participation": 4.615343292855916e-10
89+ },
90+ {
91+ "mode": 6,
92+ "frequency_hz": 17406.91418302896,
93+ "q_total": 221.33714881015712,
94+ "hit_participation": 10.214637205443747,
95+ "support_participation": -39.75251653879094,
96+ "radiation_participation": -1.4657146340141562e-10
97+ },
98+ {
99+ "mode": 7,
100+ "frequency_hz": 18481.958797033793,
101+ "q_total": 216.32877400782587,
102+ "hit_participation": 22.564469992789796,
103+ "support_participation": -3.222335236301978,
104+ "radiation_participation": -1.0539110071304016e-11
105+ },
106+ {
107+ "mode": 8,
108+ "frequency_hz": 18481.958819191677,
109+ "q_total": 216.2265684790528,
110+ "hit_participation": -27.413256721479193,
111+ "support_participation": -18.673384065112508,
112+ "radiation_participation": -6.107267757176336e-11
113+ },
114+ {
115+ "mode": 9,
116+ "frequency_hz": 20110.349699859195,
117+ "q_total": 208.72189043718055,
118+ "hit_participation": 2.8264429923151697,
119+ "support_participation": 1.948897079927276e-08,
120+ "radiation_participation": 7.940784314819367e-16
121+ },
122+ {
123+ "mode": 10,
124+ "frequency_hz": 22351.70563879573,
125+ "q_total": 199.51125777492905,
126+ "hit_participation": 23.009002478534985,
127+ "support_participation": -1.725669654289366e-08,
128+ "radiation_participation": 5.33883172588491e-15
129+ },
130+ {
131+ "mode": 11,
132+ "frequency_hz": 29180.373325145683,
133+ "q_total": 177.7748909720117,
134+ "hit_participation": -24.980147053078895,
135+ "support_participation": 0.14958047305946864,
136+ "radiation_participation": 1.9859185842563236e-13
137+ },
138+ {
139+ "mode": 12,
140+ "frequency_hz": 29180.373407187442,
141+ "q_total": 177.58090301980624,
142+ "hit_participation": 13.582522279981529,
143+ "support_participation": 38.75803531589733,
144+ "radiation_participation": 5.0851365151582777e-11
145+ },
146+ {
147+ "mode": 13,
148+ "frequency_hz": 35231.08989592263,
149+ "q_total": 163.6412349054538,
150+ "hit_participation": -12.123921973111656,
151+ "support_participation": 13.990843991001825,
152+ "radiation_participation": 1.259567339578077e-11
153+ },
154+ {
155+ "mode": 14,
156+ "frequency_hz": 36669.40588630956,
157+ "q_total": 160.69777756943697,
158+ "hit_participation": 9.15006318921439,
159+ "support_participation": -33.02953834499242,
160+ "radiation_participation": -2.7443202299125193e-11
161+ },
162+ {
163+ "mode": 15,
164+ "frequency_hz": 38503.734715938794,
165+ "q_total": 157.3484854697542,
166+ "hit_participation": -3.61599665525642,
167+ "support_participation": 0.17932309476301006,
168+ "radiation_participation": 1.3476882201087902e-13
169+ },
170+ {
171+ "mode": 16,
172+ "frequency_hz": 38503.7347953747,
173+ "q_total": 157.22596406338678,
174+ "hit_participation": -8.669739835725732,
175+ "support_participation": -39.969380487593284,
176+ "radiation_participation": -3.011751477483243e-11
177+ },
178+ {
179+ "mode": 17,
180+ "frequency_hz": 41631.68337710871,
181+ "q_total": 151.97618432408356,
182+ "hit_participation": -31.810274407905133,
183+ "support_participation": -2.3085488794960596e-08,
184+ "radiation_participation": -6.981245551269943e-16
185+ }
186+ ]
187+}
packages/core/test/fixtures/fem_off_32_wav.jsonadded+1−0View file
@@ -0,0 +1 @@
1+{"fs": 96000, "samples": [-2e-07, -0.5262365, -0.4347152, -0.1396727, 0.120004, 0.4846677, 0.7476686, 0.8085673, 0.4909169, 0.2305252, 0.0463964, -0.3927426, -0.5794191, -0.5163928, -0.4842011, -0.3450481, -0.2402485, -0.2055686, -0.2237457, -0.0658298, 0.2969319, 0.3782541, 0.4590064, 0.6155502, 0.5180979, 0.4617124, 0.3392164, -0.0750532, -0.5517171, -0.7783133, -0.6981141, -0.5922621, -0.3525861, -0.0549384, 0.0577381, 0.3012925, 0.5706033, 0.5361579, 0.3014899, 0.115729, 0.0690782, 0.0415373, 0.1415647, 0.1785873, -0.0941374, -0.2395811, -0.2361558, -0.3643156, -0.4833009, -0.5151336, -0.4579781, -0.2198072, 0.2416429, 0.6428179, 0.7023687, 0.6436846, 0.5845141, 0.3484457, 0.0642748, -0.1517819, -0.4779201, -0.6841995, -0.5277956, -0.362883, -0.2769827, -0.164773, -0.0937437, -0.0043049, 0.139509, 0.3744698, 0.4905104, 0.4568078, 0.5850926, 0.55137, 0.2900838, 0.07471, -0.3240842, -0.7338401, -0.891251, -0.851094, -0.6485771, -0.3534615, 0.1063895, 0.4969937, 0.6713171, 0.8422513, 0.7749542, 0.4596841, 0.2002452, -0.0610864, -0.3206476, -0.4259408, -0.3790416, -0.3774419, -0.4127013, -0.3285677, -0.2617606, -0.1976714, 0.001321, 0.1896483, 0.2922573, 0.4513275, 0.6501106, 0.6455954, 0.4521839, 0.1936233, -0.1924101, -0.5194317, -0.5904773, -0.5794575, -0.5695189, -0.4004137, -0.1008731, 0.1495353, 0.3574218, 0.4442368, 0.3050591, 0.1528697, 0.1655376, 0.2305876, 0.1900953, 0.1371198, 0.0906344, -0.0567428, -0.114004, -0.1813053, -0.4590177, -0.6422336, -0.5990906, -0.3746069, -0.0334842, 0.2859815, 0.5240043, 0.5808393, 0.6218092, 0.6502792, 0.3725591, 0.016432, -0.2499767, -0.4719449, -0.4858401, -0.3818986, -0.3464625, -0.3666987, -0.3024967, -0.1142213, 0.0285913, 0.1597015, 0.3196587, 0.4008794, 0.5167325, 0.6710164, 0.6037337, 0.2971843, -0.0526805, -0.4060683, -0.7080572, -0.812337, -0.7627829, -0.6273993, -0.3266126, 0.1210921, 0.4956378, 0.6811225, 0.7474094, 0.6313651, 0.3711018, 0.1744187, -0.0046101, -0.2118058, -0.3273893, -0.3470432, -0.3714332, -0.386951, -0.319199, -0.2770143, -0.2435874, -0.0725844, 0.1332964, 0.3253801, 0.5324925, 0.6532977, 0.585709, 0.3650917, 0.1359416, -0.14305, -0.4291931, -0.5421712, -0.5906972, -0.5456116, -0.2964027, -0.0241876, 0.1532397, 0.2270545, 0.2406326, 0.2213445, 0.1950228, 0.2474099, 0.2454737, 0.1590321, 0.1693077, 0.1772942, 0.0719308, -0.0924885, -0.3313493, -0.5779246, -0.64608, -0.495626, -0.2737362, -0.056913, 0.2036677, 0.4648094, 0.6288199, 0.6896586, 0.5756802, 0.2469983, -0.0503064, -0.1900926, -0.2940536, -0.3633851, -0.4012201, -0.4351792, -0.3956044, -0.2535398, -0.0979582, -0.0342599, 0.0532153, 0.2631526, 0.4740596, 0.6490424, 0.7043934, 0.5172812, 0.2052683, -0.0877167, -0.3630006, -0.6406263, -0.8065685, -0.7774943, -0.5905976, -0.2209399, 0.2035048, 0.4640587, 0.5755424, 0.6030117, 0.5404969, 0.3843502, 0.196347, 0.010774, -0.18476, -0.2706576, -0.2598876, -0.3072874, -0.3614103, -0.3715833, -0.3589954, -0.2652387, -0.0687966, 0.1579265, 0.3435883, 0.50354, 0.6052936, 0.5445682, 0.3651929, 0.137347, -0.1588069, -0.4188769, -0.5190818, -0.5122181, -0.4191264, -0.2279148, -0.0503515, 0.05457, 0.1284845, 0.1874221, 0.2020664, 0.1837633, 0.2059822, 0.2308781, 0.2417177, 0.2916465, 0.2466806, 0.0479443, -0.1835331, -0.3989531, -0.5467874, -0.5726469, -0.4809825, -0.3171419, -0.0886592, 0.2382418, 0.5236911, 0.6296308, 0.5933018, 0.4322585, 0.2114063, 0.0437421, -0.0843996, -0.2414135, -0.3847774, -0.4198107, -0.383835, -0.3320396, -0.2629334, -0.2168186, -0.1445127, 0.0599331, 0.3452923, 0.5608271, 0.6425305, 0.6119523, 0.4627666, 0.2271699, -0.0427574, -0.3754495, -0.6942888, -0.8137564, -0.7002732, -0.4462058, -0.1289643, 0.160864, 0.3563081, 0.489053, 0.5682383, 0.520147, 0.3410972, 0.1449777, 0.0038183, -0.0948217, -0.1425363, -0.1942234, -0.3174658, -0.409169, -0.4045413, -0.3491612, -0.2399508, -0.0775486, 0.1184942, 0.3220611, 0.5073058, 0.6081962, 0.5234646, 0.3150399, 0.0807819, -0.171134, -0.3552787, -0.4277615, -0.4329622, -0.3656431, -0.2265232, -0.0744615, 0.0240119, 0.0795815, 0.1169181, 0.1196835, 0.1436188, 0.2254449, 0.2990748, 0.3339914, 0.3249603, 0.2210461, 0.0216681, -0.1913653, -0.3814589, -0.5398102, -0.5934324, -0.5016939, -0.2986057, -0.0168214, 0.2815848, 0.4800475, 0.5336149, 0.5040434, 0.4147478, 0.2601669, 0.0853562, -0.0860102, -0.2431257, -0.3289307, -0.3330101, -0.3394232, -0.3741792, -0.3672358, -0.2859707, -0.1173848, 0.1308915, 0.3671467, 0.5191972, 0.5971515, 0.610253, 0.4989902, 0.2337573, -0.1094309, -0.452427, -0.6835146, -0.701576, -0.5742934, -0.3883012, -0.1540781, 0.1042009, 0.3368487, 0.4838659, 0.5111458, 0.4122469, 0.2544545, 0.1544842, 0.0966027, 0.0099549, -0.0981836, -0.2204756, -0.331913, -0.3798762, -0.3733758, -0.3514516, -0.2855846, -0.1176882, 0.1262998, 0.3622308, 0.5234956, 0.5513232, 0.4380503, 0.2647974, 0.0820981, -0.1212304, -0.2992454, -0.3897471, -0.3904182, -0.3090086, -0.1796648, -0.0781221, -0.037434, -0.0124291, 0.0338558, 0.0888805, 0.1639112, 0.2594947, 0.3283316, 0.3584755, 0.3426856, 0.2357753, 0.0330312, -0.2037583, -0.4159034, -0.5627137, -0.5792113, -0.4525114, -0.2454733, 0.0028459, 0.2461931, 0.416383, 0.4923801, 0.4860553, 0.3998328, 0.2419002, 0.0738721, -0.0568177, -0.1654111, -0.2463079, -0.3094405, -0.385619, -0.430053, -0.3879809, -0.2667206, -0.093366, 0.1149986, 0.3327073, 0.5160245, 0.6328163, 0.6329116, 0.4570216, 0.1506123, -0.1696323, -0.4313411, -0.5886195, -0.6174519, -0.5441827, -0.3834802, -0.1353259, 0.1354582, 0.3279118, 0.4004269, 0.3850378, 0.3283255, 0.2697474, 0.2245654, 0.1465109, 0.0188359, -0.0998566, -0.1903553, -0.2734111, -0.3500131, -0.4075622, -0.408133, -0.298551, -0.0753756, 0.1732103, 0.3556511, 0.4564146, 0.473161, 0.4061264, 0.2762677, 0.0900395, -0.1243017, -0.2850032, -0.3322165, -0.2951198, -0.2331014, -0.1749577, -0.1401305, -0.1139643, -0.059832, 0.0102119, 0.0744933, 0.1515483, 0.2564236, 0.3588319, 0.4113179, 0.3780807, 0.2304971, -0.0027907, -0.2366586, -0.4217856, -0.5342537, -0.5373231, -0.4272723, -0.2361179, 0.0020303, 0.2344889, 0.3935379, 0.4525858, 0.4325761, 0.3535704, 0.2353651, 0.1155646, 0.0046808, -0.1147192, -0.2285385, -0.3215773, -0.3969001, -0.4273992, -0.3882844, -0.2846178, -0.1173679, 0.1101428, 0.35776, 0.5503318, 0.6321097, 0.5770508, 0.3832667, 0.1144719, -0.1539288, -0.3895971, -0.5505882, -0.5906206, -0.5023108, -0.31438, -0.0818575, 0.1212138, 0.2418065, 0.2972396, 0.3260408, 0.3259558, 0.290632, 0.2262742, 0.1379748, 0.0451833, -0.0368056, -0.1330691, -0.2658308, -0.3916866, -0.4440579, -0.3932568, -0.2446124, -0.0433765, 0.1469186, 0.3006235, 0.4158491, 0.4607788, 0.3975356, 0.2407906, 0.0465473, -0.1212882, -0.2114853, -0.2292426, -0.2273178, -0.2268938, -0.2092055, -0.1730494, -0.1310635, -0.0871022, -0.0419022, 0.0254977, 0.1456583, 0.2995897, 0.416209, 0.4380268, 0.3582758, 0.1946186, -0.0161904, -0.2246247, -0.405248, -0.5243536, -0.5272594, -0.4048849, -0.2010418, 0.0241168, 0.2147624, 0.3365734, 0.3891293, 0.3926511, 0.3481073, 0.2555894, 0.1438219, 0.0287685, -0.0892714, -0.2007653, -0.304485, -0.3971829, -0.4467586, -0.4156907, -0.2960724, -0.1015024, 0.1408357, 0.3740161, 0.5353661, 0.5926628, 0.5315915, 0.3544461, 0.1034792, -0.1603809, -0.385261, -0.5173842, -0.522179, -0.4202912, -0.2612591, -0.0876823, 0.0654463, 0.1786412, 0.256038, 0.298549, 0.2961767, 0.2621708, 0.2224785, 0.1769728, 0.1085708, 0.003561, -0.1405707, -0.2942444, -0.397302, -0.4137291, -0.3539625, -0.2333961, -0.0658925, 0.1212895, 0.2956325, 0.4122185, 0.4247259, 0.3310171, 0.1856531, 0.0490732, -0.0569267, -0.1313802, -0.1810603, -0.2141723, -0.2191407, -0.1956108, -0.1747802, -0.1710239, -0.1580936, -0.0979873, 0.0247914, 0.1888807, 0.339643, 0.420675, 0.4181611, 0.3445621, 0.1999486, -0.0059396, -0.2321919, -0.4223596, -0.5170426, -0.4832962, -0.3484657, -0.1723821, 0.0047932, 0.1646083, 0.2892692, 0.3630264, 0.3777417, 0.332673, 0.248657, 0.1592299, 0.0680431, -0.0437256, -0.1770443, -0.3134723, -0.4212263, -0.4604589, -0.4095974, -0.277969, -0.0854853, 0.1447364, 0.366596, 0.5224199, 0.5723941, 0.4976712, 0.3116439, 0.0681186, -0.1688689, -0.3526388, -0.4514521, -0.4518328, -0.3723768, -0.2451891, -0.0969672, 0.0404069, 0.1422277, 0.2072518, 0.2430122, 0.257886, 0.2623782, 0.2558668, 0.2170066, 0.1285106, 0.0001118, -0.1459896, -0.2807951, -0.3700018, -0.39606, -0.3538578, -0.2367759, -0.0581024, 0.1353424, 0.2879964, 0.3630298, 0.3525935, 0.2803277, 0.1841707, 0.0839337, -0.0161902, -0.1002811, -0.1509843, -0.1715546, -0.1781637, -0.1914696, -0.2202577, -0.2372448, -0.2001342, -0.0964674, 0.0494469, 0.202321, 0.3318122, 0.4129767, 0.4281754, 0.3588946, 0.1933729, -0.0359387, -0.2577167, -0.4098843, -0.4643899, -0.4271161, -0.3253042, -0.184312, -0.0188367, 0.1463436, 0.2703795, 0.3299781, 0.3343964, 0.3059775, 0.2627189, 0.2036313, 0.1087455, -0.0299836, -0.186745, -0.3239844, -0.4168423, -0.4473316, -0.4024193, -0.2786315, -0.0815801, 0.1581732, 0.3772758, 0.5116459, 0.5310238, 0.4400707, 0.2668603, 0.0554736, -0.14982, -0.3136474, -0.4036167, -0.4030607, -0.3289799, -0.2163229, -0.0964353, 0.0072438, 0.0873456, 0.152539, 0.2084118, 0.2506526, 0.2735325, 0.2693999, 0.2256731, 0.13705, 0.013263, -0.1303222, -0.2691092, -0.3659962, -0.3922585, -0.3373977, -0.2088138, -0.0391491, 0.1240899, 0.2444822, 0.3073106, 0.3109551, 0.2633603, 0.1831744, 0.0900546, 0.0038265, -0.0562759, -0.0917677, -0.1249216, -0.1711529, -0.2248529, -0.2643298, -0.262461, -0.2039906, -0.0969006, 0.0412445, 0.1956969, 0.3408308, 0.4344684, 0.4381134, 0.3375327, 0.1540843, -0.0579867, -0.241627, -0.3671177, -0.4249094, -0.4084549, -0.3195229, -0.1764658, -0.013044, 0.1292648, 0.2235339, 0.2755788, 0.3055487, 0.3167011, 0.294771, 0.2264768, 0.1117644, -0.0317322, -0.1777429, -0.3084309, -0.4099547, -0.454078, -0.4084951, -0.2651922, -0.0504531, 0.1810272, 0.3675068, 0.4701151, 0.4795734, 0.4006274, 0.2465258, 0.0491078, -0.1438826, -0.2871057, -0.3519237, -0.3400385, -0.2809553, -0.2030204, -0.1186218, -0.0333338, 0.0483233, 0.1228684, 0.1864659, 0.2357099, 0.2688598, 0.277421, 0.242665, 0.1537064, 0.0203561, -0.1321539, -0.2682555, -0.3522455, -0.3651243, -0.3071691, -0.191277, -0.0435396, 0.1008358, 0.2119257, 0.2720478, 0.2755678, 0.2331569, 0.168701, 0.1032312, 0.0466275, -0.00116, -0.0504255, -0.1117078, -0.1807877, -0.2419766, -0.2791152, -0.2768693, -0.2224533, -0.112972, 0.0414499, 0.2146074, 0.3613296, 0.4346157, 0.4125015, 0.3039371, 0.1377478, -0.0487461, -0.2200374, -0.347493, -0.4064285, -0.3824422, -0.2880715, -0.1585651, -0.0282846, 0.0836862, 0.1752945, 0.2514352, 0.3071047, 0.3259273, 0.2973805, 0.2261997, 0.1214441, -0.0110788, -0.1622342, -0.3120616, -0.4243179, -0.4555353, -0.3844752, -0.2281601, -0.027324, 0.1741381, 0.3386127, 0.4371791, 0.4497288, 0.3688972, 0.2128465, 0.0286216, -0.1317781, -0.2384802, -0.2873303, -0.2898022, -0.2601571, -0.2057038, -0.1323929, -0.053312, 0.0197569, 0.0871513, 0.1554376, 0.2238065, 0.277987, 0.2936633, 0.2505074, 0.1500098, 0.0146697, -0.1276241, -0.2499346, -0.3273163, -0.3424195, -0.2899595, -0.1802305, -0.0419047, 0.0872102, 0.1796417, 0.2264563, 0.2320745, 0.2094192, 0.1728736, 0.1302896, 0.083754, 0.0328864, -0.0262935, -0.0980641, -0.1780883, -0.252212, -0.3007338, -0.3006626, -0.2354316, -0.1078822, 0.0587345, 0.2275266, 0.3570892, 0.4154739, 0.3908736, 0.2897746, 0.1318895, -0.0497217, -0.2143574, -0.3272018, -0.3689818, -0.3415084, -0.2659222, -0.1667656, -0.0582987, 0.0518787, 0.1548402, 0.239497, 0.2951872, 0.3152265, 0.2983419, 0.2427987, 0.1420859, -0.0035622, -0.1723738, -0.3246514, -0.4189209, -0.4285423, -0.3523504, -0.2092607, -0.0255721, 0.1666682, 0.3266659, 0.4142849, 0.4084957, 0.3179964, 0.1773942, 0.0296139, -0.0961847, -0.189288, -0.2453494, -0.2612428, -0.2400888, -0.1935108, -0.1371073, -0.0809022, -0.0210468, 0.0538592, 0.1437115, 0.2293355, 0.284393, 0.2899071, 0.241253, 0.1471605, 0.0226731, -0.1139839, -0.2365579, -0.3129908, -0.3215168, -0.2631572, -0.1598097, -0.043281, 0.0597329, 0.1368472, 0.1853312, 0.204952, 0.1993144, 0.1772012, 0.1471185, 0.1117723, 0.0664104, 0.0016239, -0.0863363, -0.185542, -0.2717009, -0.319089, -0.3080036, -0.2313562, -0.0990731, 0.0648371, 0.2270768, 0.3502869, 0.4036625, 0.3741353, 0.2695184, 0.115189, -0.051963, -0.1954916, -0.2916149, -0.3312689, -0.3172156, -0.2606989, -0.1751662, -0.0729573, 0.0332459, 0.1318249, 0.215742, 0.2802154, 0.3167664, 0.3125866, 0.255348, 0.1415551, -0.0138863, -0.1786193, -0.3164224, -0.3984675, -0.406601, -0.3354794, -0.1951211, -0.0122263, 0.1712408, 0.3091789, 0.37138, 0.3549937, 0.2780395, 0.1661536, 0.043523, -0.0703067, -0.1589785, -0.2102099, -0.2232783, -0.211079, -0.1883364, -0.1588617, -0.1149491, -0.0471001, 0.0436666, 0.1411332, 0.2230786, 0.2731687, 0.2817901, 0.2421507, 0.1529955, 0.0255334, -0.1134339, -0.2276633, -0.2876977, -0.2858498, -0.2346182, -0.1533522, -0.0598284, 0.0310128, 0.1064866, 0.1567526, 0.1794182, 0.1827728, 0.1786098, 0.1696692, 0.1465397, 0.0967049, 0.0153096, -0.0888538, -0.1951723, -0.2801979, -0.3237831, -0.3098463, -0.230683, -0.0945102, 0.0723751, 0.2314469, 0.3436993, 0.383585, 0.3467604, 0.2467102, 0.1074797, -0.0415653, -0.1720926, -0.2633206, -0.3047318, -0.2969208, -0.2500551, -0.1773204, -0.0890068, 0.0077605, 0.107012, 0.2014184, 0.2787301, 0.321831, 0.3151443, 0.2514285, 0.1352017, -0.0158887, -0.173901, -0.3072456, -0.3861908, -0.388895, -0.3105208, -0.1684777, 0.0023931, 0.1614074, 0.2751514, 0.3268984, 0.3164028, 0.2537214, 0.156158, 0.0468377, -0.0511218, -0.1227126, -0.1655859, -0.1878926, -0.1988014, -0.1983053, -0.1778791, -0.1305272, -0.0574552, 0.0325123, 0.1265299, 0.2105692, 0.2688603, 0.2833858, 0.2408806, 0.1446188, 0.0176276, -0.1074615, -0.2025607, -0.2530226, -0.25756, -0.2216398, -0.1540373, -0.069395, 0.0129452, 0.0776615, 0.1210961, 0.1503984, 0.1745138, 0.1935359, 0.1964239, 0.170176, 0.1099767, 0.0206162, -0.0867237, -0.1958494, -0.2856961, -0.3317889, -0.3129111, -0.2233119, -0.0801172, 0.0825797, 0.2273551, 0.324637, 0.3579851, 0.3245435, 0.2332483, 0.1040484, -0.0344569, -0.1537712, -0.2355299, -0.2745293, -0.2749708, -0.2441392, -0.1872899, -0.1076712, -0.0106888, 0.094049, 0.1939001, 0.273875, 0.3177907, 0.3118412, 0.2488638, 0.132612, -0.0188701, -0.1750748, -0.3012327, -0.3680426, -0.3597817, -0.2795784, -0.148118, 0.003921, 0.1441368, 0.2454409, 0.291986, 0.281737, 0.2253347, 0.1425243, 0.0551469, -0.0216646, -0.0832477, -0.1315813, -0.1689987, -0.193996, -0.2009926, -0.1842596, -0.1414255, -0.0731487, 0.0166068, 0.1170206, 0.2086149, 0.2674957, 0.274658, 0.225591, 0.133149, 0.0204916, -0.0890962, -0.1768431, -0.2289646, -0.2382132, -0.2065624, -0.1464313, -0.076727, -0.0129019, 0.0409068, 0.0893331, 0.1362873, 0.1785385, 0.2066076, 0.2101903, 0.182959, 0.1228908, 0.0311792, -0.0841822, -0.2032219, -0.2961491, -0.3342299, -0.3025198, -0.2065603, -0.0688117, 0.081371, 0.2152034, 0.3072162, 0.3389178, 0.3048455, 0.2160242, 0.0967832, -0.0249146, -0.1283896, -0.204212, -0.2496364, -0.263229, -0.2441478, -0.1939019, -0.1174857, -0.0227929, 0.0810375, 0.1831736, 0.2681606, 0.3162125, 0.3098328, 0.2422664, 0.1223332, -0.026549, -0.1729455, -0.2861581, -0.3428083, -0.331258, -0.2551468, -0.1333609, 0.0052134, 0.1297652, 0.2162178, 0.2536133, 0.2447014, 0.2014103, 0.1389754, 0.0707963, 0.0051306, -0.0546446, -0.1072537, -0.1514063, -0.1844009, -0.2008673, -0.1934263, -0.1554842, -0.0850691, 0.0108864, 0.114857, 0.2032252, 0.2548944, 0.2581852, 0.2132627, 0.1306715, 0.0276243, -0.0754627, -0.1581075, -0.2051254, -0.2124794, -0.1878618, -0.1447828, -0.0946647, -0.0421666, 0.0132147, 0.0714099, 0.1283956, 0.1775841, 0.2116783, 0.2222184, 0.1995404, 0.1362158, 0.0337338, -0.091869, -0.2120235, -0.2962964, -0.3238041, -0.2880828, -0.1960337, -0.0650293, 0.0796608, 0.207984, 0.2918011, 0.3148326, 0.2787066, 0.1994782, 0.0981358, -0.0072823, -0.1039311, -0.1820576, -0.2332477, -0.2521662, -0.2387379, -0.1963316, -0.1281805, -0.037223, 0.0699728, 0.178854, 0.2671373, 0.311909, 0.2990951, 0.2283377, 0.1119437, -0.0284108, -0.1652999, -0.2702419, -0.3200223, -0.3040425, -0.2291363, -0.1171228, 0.0040212, 0.1093677, 0.1825767, 0.2173425, 0.2157814, 0.1857014, 0.1379061, 0.082783, 0.0267634, -0.0284253, -0.0830032, -0.1353661, -0.1794733, -0.2049946, -0.2010877, -0.1615797, -0.0884246, 0.0073875, 0.1076498, 0.1915443, 0.2411272, 0.2455603, 0.2039135, 0.1260789, 0.030287, -0.0623686, -0.1342794, -0.1763455, -0.1889805, -0.1782763, -0.1508769, -0.1114447, -0.0627542, -0.0067426, 0.0543971, 0.1173859, 0.1764683, 0.221288, 0.2372242, 0.2109046, 0.1380764, 0.0282017, -0.0971789, -0.2111664, -0.2892329, -0.314087, -0.2781209, -0.1860792, -0.0565966, 0.0812615, 0.1968211, 0.2679203, 0.2866937, 0.2578758, 0.1926035, 0.1039233, 0.0058835, -0.0871099, -0.1628427, -0.2140408, -0.2385093, -0.2358001, -0.2036224, -0.1390691, -0.0445581, 0.0676417, 0.1768602, 0.2602806, 0.2997245, 0.2854848, 0.2172548, 0.104879, -0.0307223, -0.1602216, -0.2541217, -0.2927564, -0.272347, -0.2040936, -0.1076946, -0.0043228, 0.0873824, 0.153513, 0.187241, 0.1900981, 0.1700753, 0.1367971, 0.0963899, 0.0496769, -0.004721, -0.0654695, -0.1261836, -0.1765163, -0.2049984, -0.2022714, -0.163862, -0.0922276, 0.0019112, 0.1003342, 0.1817057, 0.2281043, 0.2304378, 0.1907352, 0.1206545, 0.0369074, -0.0439762, -0.1094037, -0.1525932, -0.1725013, -0.1718741, -0.1547076, -0.1242858, -0.0819548, -0.0274012, 0.0388457, 0.1120433, 0.18133, 0.2308938, 0.2446847, 0.2128127, 0.1359277, 0.0256647, -0.0977224, -0.2092113, -0.2841392, -0.3042291, -0.2633703, -0.1707895, -0.048633, 0.0757974, 0.1785308, 0.2438461, 0.2650799, 0.243306, 0.1859272, 0.1053845, 0.016404, -0.068352, -0.141019, -0.197065, -0.2315722, -0.2374453, -0.2078419, -0.1413467, -0.045465, 0.0645263, 0.1695489, 0.2501165, 0.2888686, 0.2741248, 0.2046916, 0.0933012, -0.0350679, -0.1510811, -0.2309294, -0.2622603, -0.2445056, -0.1862887, -0.1025287, -0.0115969, 0.0685846, 0.1256842, 0.1565056, 0.1655206, 0.1592521, 0.1412459, 0.1111434, 0.0674859, 0.0112473, -0.0527657, -0.1167172, -0.1706671, -0.2031777, -0.203479, -0.1657759, -0.093447, -8.85e-05, 0.0941026, 0.1690486, 0.2106807, 0.2134323, 0.1799842, 0.1196723, 0.0459193, -0.0271421, -0.0882317, -0.1314922, -0.1565105, -0.1655265, -0.1600163, -0.1391188, -0.1004948, -0.0429682, 0.0304352, 0.1108916, 0.18449, 0.2348781, 0.2473197, 0.2133106, 0.1341666, 0.022282, -0.1004797, -0.2075852, -0.2750168, -0.2881459, -0.2455321, -0.1584704, -0.046802, 0.0668641, 0.1620674, 0.2238618, 0.2450869, 0.2270873, 0.178002, 0.1090429, 0.0306053, -0.0495049, -0.1247068, -0.1870622, -0.2267906, -0.2347567, -0.2058597, -0.1411575, -0.0482037, 0.0595673, 0.1636766, 0.2427167, 0.2774417, 0.2577069, 0.1871103, 0.0821581, -0.0334609, -0.1360735, -0.2072336, -0.2361472, -0.2209128, -0.1691263, -0.0960702, -0.0195098, 0.0466768, 0.0962272, 0.1293873, 0.1484561, 0.1541419, 0.1453017, 0.1207026, 0.0802925, 0.0255677, -0.0397331, -0.1080886, -0.1672676, -0.2024646, -0.201873, -0.1623392, -0.0911304, -0.0031792, 0.0837832, 0.153534, 0.1940725, 0.1994465, 0.1712131, 0.1182594, 0.0536213, -0.0104829, -0.0663194, -0.1109814, -0.1438699, -0.1637088, -0.1674901, -0.1515752, -0.1134659, -0.0534036, 0.0240873, 0.1090429, 0.1862873, 0.2379959, 0.2486543, 0.2107293, 0.128237, 0.0165075, -0.1015072, -0.2010043, -0.2614588, -0.271329, -0.2303177, -0.1489451, -0.0454743, 0.0590268, 0.1458502, 0.202502, 0.2242546, 0.2130201, 0.1744763, 0.1154283, 0.0427469, -0.0362438, -0.1129319, -0.1773334, -0.2192489, -0.2300087, -0.204183, -0.1415079, -0.0489526, 0.0584395, 0.1595365, 0.2322898, 0.2603941, 0.2382149, 0.1717085, 0.0759, -0.0290639, -0.1221504, -0.1858492, -0.2103489, -0.1960479, -0.1525301, -0.0937581, -0.0322518, 0.0241379, 0.0718144, 0.1092105, 0.1350739, 0.1480684, 0.1467794, 0.1292989, 0.0933457, 0.038385, -0.0310402, -0.10372, -0.1640533, -0.1973764, -0.1951804, -0.157319, -0.0910521, -0.0089247, 0.0733976, 0.139997, 0.1786616, 0.1845463, 0.1611835, 0.1177282, 0.0641655, 0.0078985, -0.0464946, -0.0955922, -0.1357442, -0.1628202, -0.1725562, -0.160843, -0.1243355, -0.0624228, 0.019763, 0.109642, 0.1889354, 0.2386523, 0.245022, 0.2037399, 0.1211705, 0.0129012, -0.0994265, -0.1926936, -0.2478345, -0.2547568, -0.2147082, -0.1390817, -0.0452318, 0.048698, 0.1277518, 0.1820733, 0.2071178, 0.202774, 0.1720054, 0.1196503, 0.0517099, -0.0247812, -0.101374, -0.1678761, -0.2129696, -0.2261968, -0.2010054, -0.1378981, -0.0461274, 0.0574073, 0.1519718, 0.2181219, 0.2424506, 0.2206536, 0.1583655, 0.0698521, -0.0253484, -0.1075264, -0.1621872, -0.1834218, -0.1738161, -0.1413908, -0.0956029, -0.0445767, 0.0057651, 0.0515741, 0.0907058, 0.1218527, 0.1430228, 0.1502838, 0.1382601, 0.1029674, 0.0455202, -0.0260621, -0.0984782, -0.1570405, -0.1895284, -0.1887301, -0.1538161, -0.0909774, -0.0128703, 0.0642113, 0.1252247, 0.160923, 0.1693869, 0.1541629, 0.1210088, 0.0756389, 0.0232614, -0.0311409, -0.082907, -0.127819, -0.1613826, -0.1777594, -0.1700635, -0.133035, -0.0670035, 0.0195634, 0.111014, 0.1885213, 0.2349167, 0.238638, 0.1964339, 0.1147999, 0.009553, -0.0973146, -0.1833678, -0.2319969, -0.2363457, -0.1995209, -0.1319909, -0.0480284, 0.0374779, 0.1114767, 0.1645724, 0.1919438, 0.1928629, 0.1691182, 0.1236323, 0.0603404, -0.0147645, -0.092547, -0.1609352, -0.2069499, -0.2198618, -0.1942812, -0.13227, -0.0438461, 0.054505, 0.1431754, 0.20397, 0.2247244, 0.2025293, 0.1443272, 0.0646259, -0.0188603, -0.0901017, -0.1384128, -0.1597867, -0.156068, -0.1328807, -0.0972092, -0.0553117, -0.0114536, 0.032187, 0.0742751, 0.1122706, 0.141043, 0.1535189, 0.1433138, 0.1078785, 0.0504137, -0.0202916, -0.0916549, -0.1498056, -0.1824485, -0.1820201, -0.1482963, -0.0889521, -0.0172311, 0.052419, 0.1084916, 0.1443563, 0.1578679, 0.1500794, 0.124194, 0.0847149, 0.0364023, -0.0166487, -0.0708947, -0.1218532, -0.1624834, -0.1834877, -0.1762178, -0.1366202, -0.0678547, 0.0196435, 0.1102082, 0.1859546, 0.2302325, 0.2315917, 0.1875966, 0.1066177, 0.0060176, -0.0928832, -0.1707614, -0.2145998, -0.2192856, -0.1871999, -0.1268522, -0.0507728, 0.0274216, 0.0961032, 0.1475208, 0.1776692, 0.1847378, 0.1678558, 0.1273924, 0.0664122, -0.0081762, -0.0857911, -0.1536612, -0.1990556, -0.2117944, -0.1868352, -0.1265001, -0.0412602, 0.0519797, 0.1339865, 0.1882725, 0.2052561, 0.1841458, 0.1322658, 0.0624766, -0.010373, -0.0730785, -0.1168713, -0.1385796, -0.1399323, -0.1254561, -0.0999595, -0.0667983, -0.0276781, 0.0161507, 0.0621642, 0.1053953, 0.138644, 0.1541168, 0.1457864, 0.1116011, 0.054911, -0.0153509, -0.0860511, -0.1427741, -0.1736602, -0.1728201, -0.1417921, -0.0884242, -0.0240035, 0.0398631, 0.0935241, 0.130481, 0.1477807, 0.1457275, 0.1267766, 0.093908, 0.0495591, -0.0039165, -0.0625316, -0.1191769, -0.1639017, -0.1862437, -0.1784086, -0.1378014, -0.0683142, 0.0195215, 0.1096482, 0.1833554, 0.2238865, 0.2214525, 0.1763774, 0.098787, 0.0052532, -0.0857009, -0.1575475, -0.1985981, -0.2038137, -0.1753983, -0.1216362, -0.0539674, 0.0164251, 0.0807033, 0.1324196, 0.1664841, 0.1786177, 0.1660122, 0.1286138, 0.069981, -0.0026792, -0.0789769, -0.1462205, -0.1912241, -0.2033479, -0.1780085, -0.1189755, -0.0380018, 0.048245, 0.1225364, 0.1710752, 0.1862228, 0.1677144, 0.1222515, 0.0614125, -0.0017542, -0.0563492, -0.0959615, -0.1188473, -0.1263526, -0.1208123, -0.1040706, -0.0771219, -0.0407208, 0.0035287, 0.0520907, 0.0988999, 0.135773, 0.1540759, 0.1472875, 0.1135584, 0.0571827, -0.0118057, -0.0799432, -0.1337656, -0.1631648, -0.1636174, -0.136724, -0.0891663, -0.0306541, 0.0285051, 0.0796205, 0.1170092, 0.138139, 0.1426311, 0.1308495, 0.1031289, 0.0602788, 0.0049876, -0.0569882, -0.1167767, -0.1636368, -0.1870766, -0.1793311, -0.1378132, -0.067013, 0.0211058, 0.109119, 0.1784201, 0.2142467, 0.2094555, 0.1658274, 0.0929961, 0.0058969, -0.0785036, -0.144812, -0.1825388, -0.1880484, -0.1642535, -0.1183354, -0.0589352, 0.0056397, 0.067778, 0.1203324, 0.1567863, 0.1719662, 0.1628089, 0.1289072, 0.0730506, 0.0019188, -0.0736318, -0.1397543, -0.1826931, -0.1927393, -0.1672185, -0.1112443, -0.0362608, 0.0427326, 0.1104188, 0.1544188, 0.1680966, 0.1519911, 0.1130237, 0.0616406, 0.008255, -0.0393457, -0.0767666, -0.1023089, -0.1157792, -0.1174743, -0.107519, -0.0856706, -0.0518392, -0.0073401, 0.0437027, 0.093824, 0.1333319, 0.1528801, 0.1464073, 0.1132164, 0.0585098, -0.0076647, -0.0726715, -0.1241722, -0.1529582, -0.1549673, -0.1318212, -0.0897285, -0.0372824, 0.0169139, 0.0659058, 0.10489, 0.1307537, 0.1412964, 0.1347923, 0.1102374, 0.068205, 0.0118821, -0.0522517, -0.1145295, -0.1632798, -0.1871627, -0.1782716, -0.1350323, -0.0636766, 0.0225688, 0.1064979, 0.1710992, 0.2035092, 0.1977664, 0.1559298, 0.0874231, 0.0066634, -0.0705633, -0.1309186, -0.1661495, -0.1736971, -0.1555828, -0.116698, -0.0634728, -0.0031276, 0.0568314, 0.1091434, 0.1472484, 0.165568, 0.1599442, 0.128721, 0.0743175, 0.0042859, -0.0692877, -0.132305, -0.1721658, -0.1808454, -0.1566487, -0.1045451, -0.0352244, 0.0371376, 0.0982494, 0.1373661, 0.1498225, 0.1373173, 0.1060581, 0.0640276, 0.0187361, -0.0239741, -0.060264, -0.0880816, -0.1064866, -0.1146379, -0.1109958, -0.0935229, -0.0611222, -0.0155263, 0.0376476, 0.0894851, 0.1298057, 0.1497425, 0.1439922, 0.1122828, 0.0598232, -0.0035366, -0.0655193, -0.1144992, -0.1423577, -0.1460229, -0.127269, -0.0912451, -0.0446494, 0.0057406, 0.0539248, 0.0949589, 0.1248131, 0.1400664, 0.1377914, 0.1159892, 0.0746657, 0.0171433, -0.0491662, -0.11323, -0.162331, -0.1850721, -0.1745341, -0.1305412, -0.0602785, 0.0229807, 0.1027496, 0.1630826, 0.192267, 0.1854893, 0.1457094, 0.0824514, 0.0089611, -0.0612042, -0.1169864, -0.1513362, -0.1614326, -0.1480982, -0.1148963, -0.067161, -0.0111349, 0.0465622, 0.0990487, 0.1391866, 0.1600746, 0.156427, 0.1264985, 0.0735589, 0.0059915, -0.0641737, -0.123754, -0.1612017, -0.1691346, -0.146189, -0.0976405, -0.0342447, 0.0306655, 0.0848266, 0.1199538, 0.1329051, 0.125126, 0.1011473, 0.0669156, 0.0283031, -0.0100405, -0.0451742, -0.0753833, -0.0989697, -0.1132158, -0.1144686, -0.0995338, -0.0675836, -0.0214415, 0.0323682, 0.0846819, 0.1254629, 0.1459592, 0.1409505, 0.1104881, 0.0602967, 0.0003763, -0.0577561, -0.1039945, -0.1316238, -0.1379643, -0.1239851, -0.0933877, -0.0514879, -0.0040828, 0.0434008, 0.0861532, 0.1196492, 0.1392718, 0.1405813, 0.1204946, 0.0789402, 0.020121, -0.0473387, -0.1115846, -0.159814, -0.1811547, -0.1694818, -0.1254019, -0.0566852, 0.0232171, 0.0982994, 0.1538515, 0.179845, 0.1728958, 0.1364014, 0.0790553, 0.0123844, -0.0519035, -0.1040937, -0.1377529, -0.1500256, -0.1411323, -0.1134896, -0.0709367, -0.018406, 0.0379841, 0.090857, 0.1320708, 0.1540137, 0.15141, 0.1229848, 0.0723522, 0.0078076, -0.0589871, -0.1152675, -0.1500235, -0.1567525, -0.1351651, -0.0911043, -0.0346539, 0.0227623, 0.0711535, 0.1037964, 0.1179315, 0.1144771, 0.0969703, 0.0700187, 0.0377767, 0.0031437, -0.0319127, -0.0652344, -0.0934892, -0.112149, -0.1165456, -0.1034325, -0.0723575, -0.0264385, 0.0275934, 0.0802296, 0.1210435, 0.1413101, 0.1365148, 0.1076566, 0.0607864, 0.0051229, -0.0493124, -0.0937208, -0.1218896, -0.1308875, -0.1210599, -0.0952987, -0.0578492, -0.0132651, 0.0339271, 0.0788609, 0.1158998, 0.1388902, 0.1423128, 0.1229121, 0.0811331, 0.0218694, -0.0457227, -0.109463, -0.1564512, -0.1761105, -0.1631257, -0.1191915, -0.0528355, 0.0226524, 0.092437, 0.1434932, 0.1672553, 0.1610587, 0.1281587, 0.0764098, 0.0160388, -0.0427563, -0.0916393, -0.1249837, -0.1398215, -0.1353574, -0.1125462, -0.0739848, -0.0240432, 0.0310518, 0.0835139, 0.1248725, 0.1473998, 0.1458354, 0.1189695, 0.0705469, 0.0090336, -0.0539397, -0.1061854, -0.1378889, -0.1439133, -0.1247487, -0.0859205, -0.0362136, 0.0146375, 0.0581497, 0.0886753, 0.1040217, 0.1049843, 0.0940553, 0.0739789, 0.0469068, 0.014513, -0.0212044, -0.0571097, -0.0886958, -0.1105919, -0.1176403, -0.1062794, -0.0759485, -0.0300769, 0.023909, 0.0759236, 0.1156691, 0.1353034, 0.1313082, 0.1050129, 0.0618935, 0.0101131, -0.0412698, -0.084109, -0.1126274, -0.1240896, -0.118492, -0.0976076, -0.0641141, -0.0213536, 0.0263406, 0.0733149, 0.112815, 0.1378805, 0.1426686, 0.1238948, 0.0821399, 0.0226789, -0.0446872, -0.1072077, -0.1520249, -0.1694362, -0.1554257, -0.1126402, -0.0496655, 0.0210152, 0.0858078, 0.1329398, 0.1548851, 0.1496415, 0.1204981, 0.074495, 0.0203116, -0.0335823, -0.0800482, -0.1137107, -0.1310095, -0.1301651, -0.1111946, -0.0760131, -0.0285909, 0.0249741, 0.0767611, 0.1179906, 0.1406795, 0.1396147, 0.1139817, 0.0679994, 0.0102402, -0.0482224, -0.0963318, -0.125539, -0.1316016, -0.1152402, -0.0815232, -0.0382133, 0.0063801, 0.0453488, 0.0743315, 0.0916058, 0.097323, 0.0924401, 0.0779476, 0.0547224, 0.0239499, -0.0122091, -0.0500906, -0.0844597, -0.1091099, -0.118158, -0.1077823, -0.0778068, -0.032413, 0.0204648, 0.0709226, 0.1094197, 0.1289053, 0.1262151, 0.1025319, 0.0628916, 0.0148484, -0.0333645, -0.0745859, -0.1037854, -0.1181988, -0.1168525, -0.1000736, -0.0693834, -0.0277253, 0.0203141, 0.0685753, 0.1097901, 0.1364726, 0.1422718, 0.1236813, 0.0816404, 0.0222641, -0.043897, -0.1040532, -0.1461394, -0.1616148, -0.1473645, -0.1063562, -0.0469657, 0.0189338, 0.0787417, 0.1220031, 0.1424798, 0.1388298, 0.1139525, 0.0735831, 0.0248038, -0.0251521, -0.069738, -0.1036143, -0.1228854, -0.1251576, -0.1096395, -0.0774695, -0.0321959, 0.0200002, 0.0708113, 0.111194, 0.1333254, 0.1325079, 0.1084391, 0.0654839, 0.0118626, -0.0421112, -0.0863643, -0.1133595, -0.1196644, -0.1063104, -0.0779157, -0.0410066, -0.0022042, 0.0331279, 0.0614735, 0.0810205, 0.0910553, 0.0912862, 0.0814601, 0.0614855, 0.0320296, -0.0046979, -0.0444266, -0.0809988, -0.1074082, -0.117491, -0.1077271, -0.0784997, -0.0343524, 0.0168574, 0.0656521, 0.1030216, 0.1223791, 0.1208954, 0.0998536, 0.0639516, 0.0198543, -0.025412, -0.0656586, -0.0960885, -0.1133613, -0.1155056, -0.1019219, -0.0735234, -0.0329478, 0.015255, 0.0646195, 0.1072183, 0.1348244, 0.1407435, 0.1218082, 0.0797626, 0.0214102, -0.0425981, -0.0999831, -0.1394797, -0.1533249, -0.1390392, -0.1000054, -0.0445691, 0.0161346, 0.0709064, 0.110843, 0.1307031, 0.1291855, 0.1084153, 0.0730027, 0.0289402, -0.0174439, -0.0602511, -0.0943713, -0.1155636, -0.1206172, -0.1078686, -0.0779868, -0.0345719, 0.0159118, 0.0650488, 0.1040329, 0.1254828, 0.1251041, 0.1028183, 0.0629991, 0.0135882, -0.0357889, -0.0761541, -0.1011818, -0.1082571, -0.0984155, -0.0754126, -0.044377, -0.0104871, 0.0219836, 0.050046, 0.0717816, 0.08577, 0.0905806, 0.0847298, 0.0672314, 0.0385001, 0.0011362, -0.0398616, -0.0776836, -0.1050248, -0.1157957, -0.1067532, -0.0785593, -0.0359013, 0.0134331, 0.0603032, 0.0962905, 0.1154859, 0.1154992, 0.0974962, 0.0654343, 0.0248944, -0.0180639, -0.0577478, -0.0893161, -0.1089862, -0.1141198, -0.1033566, -0.0769194, -0.0370761, 0.0114666, 0.0616218, 0.1047576, 0.1323186, 0.137841, 0.1187374, 0.077287, 0.020521, -0.0410222, -0.0954597, -0.1321953, -0.1443631, -0.1303528, -0.0939343, -0.0430085, 0.0124891, 0.0628098, 0.1001699, 0.1198181, 0.1203543, 0.1033714, 0.0726129, 0.0329783, -0.0102451, -0.0517466, -0.0863127, -0.1090707, -0.1161105, -0.1053869, -0.0775343, -0.0362202, 0.0121461, 0.0593475, 0.0968311, 0.1175023, 0.1174358, 0.0969417, 0.0605275, 0.0157005, -0.0289609, -0.065791, -0.0895027, -0.0978664, -0.0915969, -0.0736029, -0.0478681, -0.018345, 0.0117279, 0.0398746, 0.0639399, 0.0815938, 0.0902202, 0.08737, 0.0716127, 0.043431, 0.0057943, -0.0358588, -0.0743605, -0.1022222, -0.113408, -0.1049551, -0.0778847, -0.0370636, 0.009937, 0.0545986, 0.0892854, 0.1086735, 0.1104902, 0.0955104, 0.0669515, 0.0295475, -0.0113744, -0.0505675, -0.0832242, -0.1051387, -0.1129115, -0.1043769, -0.0792596, -0.0398308, 0.0087925, 0.0590187, 0.1019235, 0.1289884, 0.1340441, 0.1149123, 0.074273, 0.0194147, -0.0391976, -0.0902382, -0.1240975, -0.1349716, -0.1219007, -0.0885793, -0.0421552, 0.0085437, 0.0548786, 0.0899506, 0.1095262, 0.112212, 0.0990092, 0.0726013, 0.0367566, -0.0039823, -0.0444561, -0.0791649, -0.1028488, -0.1113281, -0.102402, -0.0765728, -0.0373846, 0.0088257, 0.053908, 0.089541, 0.1091426, 0.109436, 0.0911312, 0.0585427, 0.0183659, -0.0219489, -0.0557848, -0.0785666, -0.0883329, -0.085564, -0.0723919, -0.0515883, -0.0258162, 0.0025674, 0.0312356, 0.0574957, 0.0781396, 0.0897164, 0.089198, 0.0748741, 0.047232, 0.0095048, -0.03245, -0.0711105, -0.0989335, -0.1101874, -0.1023735, -0.0768049, -0.0382402, 0.0062442, 0.048805, 0.08242, 0.1021419, 0.1057416, 0.0936592, 0.068432, 0.033935, -0.0052526, -0.0442319, -0.0780183, -0.1018298, -0.1115829, -0.1046125, -0.0804682, -0.0415399, 0.0067763, 0.0566007, 0.0988445, 0.1250569, 0.1294099, 0.1102615, 0.0707839, 0.0184133, -0.0367733, -0.0843084, -0.1156045, -0.1256681, -0.1138789, -0.0837313, -0.0417028, 0.0043784, 0.0469873, 0.0801483, 0.1000419, 0.1050191, 0.0952897, 0.0725743, 0.0398755, 0.0013313, -0.0379981, -0.0725362, -0.0968458, -0.1065074, -0.0990996, -0.0750352, -0.0379174, 0.0058728, 0.0483999, 0.0819086, 0.1005463, 0.101571, 0.0857631, 0.0570128, 0.0212455, -0.0150241, -0.0461236, -0.0682285, -0.0796595, -0.0805034, -0.0718845, -0.0553272, -0.0324876, -0.0052589, 0.0239511, 0.0520063, 0.0750812, 0.089072, 0.0904092, 0.0771316, 0.049867, 0.0122639, -0.0294311, -0.0676161, -0.0950253, -0.1063602, -0.0994367, -0.0756059, -0.0393857, 0.0025989, 0.04309, 0.0756682, 0.0958076, 0.1012956, 0.0920987, 0.0699453, 0.0378971, 2.38e-05, -0.0388603, -0.0735097, -0.0987066, -0.1099196, -0.1041449, -0.0807995, -0.0423878, 0.0053964, 0.0543817, 0.0954219, 0.1203607, 0.1239326, 0.1050691, 0.0672449, 0.0177606, -0.0338684, -0.0780133, -0.1069795, -0.1164823, -0.1062065, -0.0794039, -0.0417717, -3.41e-05, 0.0393529, 0.0710983, 0.0915091, 0.0985673, 0.0918324, 0.072324, 0.042451, 0.0059546, -0.032254, -0.0665065, -0.0911372, -0.1015395, -0.0952811, -0.0729092, -0.0381109, 0.0029103, 0.0427089, 0.0741867, 0.0920817, 0.0939968, 0.0807217, 0.0557804, 0.0243516, -0.0080712, -0.0368321, -0.0587405, -0.072107, -0.0763847, -0.0717272, -0.0587039, -0.0382733, -0.0119801, 0.0177436, 0.0473802, 0.0724715, 0.0882716, 0.0908421, 0.0782604, 0.0514758, 0.014456, -0.0264646, -0.0638598, -0.0907768, -0.1022246, -0.0962339, -0.0742058, -0.0404517, -0.0010813, 0.0373656, 0.0691149, 0.0899145, 0.0973325, 0.0907506, 0.0712099, 0.0412092, 0.0044864, -0.0342202, -0.0694907, -0.0957354, -0.107999, -0.103008, -0.0802056, -0.042375, 0.0044751, 0.0520682, 0.0914743, 0.1150069, 0.1179446, 0.0996477, 0.0637553, 0.0173675, -0.0305932, -0.0713829, -0.0982412, -0.107543, -0.0991054, -0.07573, -0.0422466, -0.0043858, 0.0322317, 0.0628057, 0.0837208, 0.0926565, 0.0886043, 0.0719316, 0.0445124, 0.0098109, -0.0272733, -0.0609485, -0.0854648, -0.0962681, -0.0910711, -0.070519, -0.0382156, -5.3e-05, 0.0370249, 0.0665189, 0.0837396, 0.0866661, 0.0760835, 0.0550207, 0.0277487, -0.001282, -0.0282382, -0.0502782, -0.0655383, -0.0728949, -0.0717098, -0.0617378, -0.043293, -0.0176299, 0.0126687, 0.0435867, 0.0700845, 0.087031, 0.0904219, 0.0785012, 0.0524311, 0.0162947, -0.0236032, -0.0600066, -0.0862614, -0.0977461, -0.0927794, -0.0727681, -0.0416361, -0.0048149, 0.0318526, 0.0630332, 0.0845401, 0.0936835, 0.089387, 0.0721523, 0.0439725, 0.0082537, -0.0302999, -0.0660027, -0.0928669, -0.1056516, -0.1010664, -0.0787655, -0.0417938, 0.0037119, 0.0495645, 0.0871359, 0.1092018, 0.1115707, 0.0940383, 0.060374, 0.0173492, -0.0268754, -0.0645193, -0.089656, -0.0991025, -0.0926259, -0.072541, -0.0429025, -0.0085703, 0.0255865, 0.0552024, 0.076673, 0.0873112, 0.0855522, 0.0712457, 0.045935, 0.0129529, -0.0228246, -0.0556258, -0.0797752, -0.0908429, -0.0866684, -0.0679541, -0.0382073, -0.002996, 0.0312952, 0.058857, 0.0756047, 0.0797974, 0.0720298, 0.0546999, 0.031197, 0.0050982, -0.020401, -0.0427157, -0.0598059, -0.0699886, -0.071843, -0.064369, -0.0474045, -0.0221344, 0.0085824, 0.0403123, 0.0676675, 0.0853493, 0.0893629, 0.0780761, 0.0528125, 0.0177625, -0.0208205, -0.0559566, -0.0814376, -0.0930673, -0.0893394, -0.0714653, -0.0428691, -0.0083699, 0.0267204, 0.0573976, 0.0795463, 0.0902717, 0.08805, 0.0728262, 0.0461383, 0.0112177, -0.0271047, -0.0628651, -0.089852, -0.1027762, -0.0984606, -0.0767436, -0.0408312, 0.0030781, 0.0469164, 0.0824186, 0.1029365, 0.1048893, 0.0884496, 0.0573248, 0.0177649, -0.0228809, -0.0576836, -0.0813878, -0.0911512, -0.0866697, -0.0697732, -0.0437334, -0.0125625, 0.0195121, 0.0483877, 0.0703478, 0.0823634, 0.082474, 0.0701948, 0.0468338, 0.015577, -0.0187826, -0.05054, -0.0741271, -0.0852916, -0.0820724, -0.065264, -0.0382193, -0.0060524, 0.0255191, 0.0513691, 0.0678967, 0.0734931, 0.0684844, 0.0546572, 0.0345908, 0.0110611, -0.0133029, -0.036077, -0.0549378, -0.0675962, -0.0719354, -0.0664063, -0.0505835, -0.0256792, 0.0052241, 0.0374026, 0.0652303, 0.0833076, 0.0877105, 0.0770029, 0.0527028, 0.0190367, -0.0179573, -0.0517203, -0.076505, -0.0884221, -0.0860005, -0.070208, -0.0440196, -0.0117096, 0.0219178, 0.0521823, 0.074992, 0.0871612, 0.0866902, 0.0730724, 0.0475893, 0.013446, -0.0244087, -0.0598716, -0.0866543, -0.099489, -0.0953235, -0.0742005, -0.039521, 0.0024806, 0.0439876, 0.0772741, 0.0963515, 0.0981672, 0.0830891, 0.0546215, 0.0184728, -0.0187581, -0.0509353, -0.0734418, -0.0837232, -0.0813031, -0.0674342, -0.0446204, -0.0161792, 0.0141056, 0.0422834, 0.0645566, 0.0776676, 0.0793601, 0.0688646, 0.0472853, 0.0177088, -0.0151249, -0.0456193, -0.0684329, -0.0796101, -0.0774133, -0.0626407, -0.0383637, -0.0091751, 0.0198475, 0.0441839, 0.0606481, 0.0677199, 0.0654205, 0.054888, 0.037895, 0.0165037, -0.0070608, -0.0303808, -0.0507985, -0.0655017, -0.0718385, -0.0678583, -0.0529559, -0.0283971, 0.0025164, 0.0348071, 0.0627016, 0.0808394, 0.0854906, 0.0754445, 0.0523237, 0.0202476, -0.015057, -0.0474527, -0.0715911, -0.0838363, -0.0827334, -0.0689974, -0.0451284, -0.0148389, 0.0175277, 0.0474992, 0.0708947, 0.0842278, 0.085137, 0.0728268, 0.0484233, 0.0151083, -0.0221019, -0.0570107, -0.0832936, -0.0957779, -0.0916474, -0.0712233, -0.0380579, 0.0017335, 0.0407486, 0.071857, 0.0896638, 0.0915345, 0.0779617, 0.0522218, 0.0194589, -0.014513, -0.044345, -0.0659602, -0.0769316, -0.0764976, -0.0653579, -0.0453912, -0.0193733, 0.0092797, 0.0367712, 0.059244, 0.0732263, 0.0762031, 0.0672141, 0.0472777, 0.0194337, -0.0116931, -0.0407496, -0.0627213, -0.0739486, -0.0728439, -0.0601382, -0.0385993, -0.0123043, 0.0143077, 0.0373192, 0.0539128, 0.0625552, 0.0628606, 0.0553021, 0.0409457, 0.0213062, -0.0016599, -0.0254943, -0.0472368, -0.0636199, -0.0715371, -0.0687349, -0.0545375, -0.0303447, 0.0003293, 0.032361, 0.0599849, 0.077993, 0.0828704, 0.073574, 0.0517561, 0.0213785, -0.0121672, -0.0431802, -0.0667175, -0.0793686, -0.0796267, -0.0678806, -0.0461407, -0.0176328, 0.013638, 0.0433163, 0.067129, 0.0813598, 0.0833734, 0.0721496, 0.0487049, 0.0162386, -0.0201446, -0.0541948, -0.0796682, -0.0916265, -0.0875597, -0.0680229, -0.0365994, 0.0008229, 0.0372965, 0.0662752, 0.0829352, 0.0850355, 0.0731384, 0.0501989, 0.0207163, -0.0102724, -0.038088, -0.059036, -0.0707228, -0.0721153, -0.0634414, -0.0460337, -0.0221803, 0.0050185, 0.0318549, 0.0543758, 0.0689408, 0.0729039, 0.0652443, 0.0469375, 0.0209167, -0.0084051, -0.0359715, -0.0570899, -0.0683749, -0.0683827, -0.0577718, -0.0389669, -0.0154686, 0.0089464, 0.0308968, 0.0478013, 0.0580021, 0.0606905, 0.0557542, 0.0436667, 0.0254869, 0.0029639, -0.0213594, -0.0441992, -0.0618726, -0.0709369, -0.0689901, -0.0553956, -0.03169, -0.0015116, 0.0299841, 0.0571178, 0.0748669, 0.0799378, 0.0714488, 0.0510503, 0.0224836, -0.0092636, -0.0389524, -0.0619978, -0.0751231, -0.0766974, -0.0667812, -0.0469589, -0.0200536, 0.0102086, 0.0395657, 0.0636536, 0.0785452, 0.0813844, 0.071015, 0.0484396, 0.016921, -0.0183903, -0.0513046, -0.0757737, -0.0871495, -0.0832136, -0.0647029, -0.0351782, -0.0002508, 0.0336369, 0.0605623, 0.0762624, 0.0788093, 0.0687146, 0.0485287, 0.0221144, -0.006175, -0.0322189, -0.052634, -0.0650368, -0.0681241, -0.0616682, -0.0465074, -0.0245403, 0.0013374, 0.0274557, 0.0498129, 0.064714, 0.0694821, 0.063067, 0.0463752, 0.0222005, -0.0052718, -0.031296, -0.0515356, -0.0629113, -0.0641089, -0.0556394, -0.0394994, -0.0185922, 0.0038962, 0.025004, 0.0422982, 0.0539752, 0.0588297, 0.0562058, 0.0460396, 0.0290214, 0.0068007, -0.01792, -0.0415535, -0.0601176, -0.0699809, -0.0686921, -0.0556721, -0.0325598, -0.0030692, 0.0276623, 0.0541028, 0.071481, 0.0767582, 0.0691811, 0.0503128, 0.0235928, -0.0064135, -0.0348789, -0.057503, -0.0710956, -0.0738971, -0.0656611, -0.0475774, -0.0221074, 0.0072427, 0.0362457, 0.0604283, 0.0757047, 0.0791023, 0.0694329, 0.0477306, 0.017296, -0.0167439, -0.0483297, -0.0716617, -0.0824154, -0.0786758, -0.0613394, -0.0338786, -0.0015398, 0.029802, 0.0548384, 0.0697886, 0.0729345, 0.0646694, 0.0471295, 0.0235792, -0.0022568, -0.0267543, -0.0467728, -0.0598784, -0.0644786, -0.0599433, -0.0467271, -0.0264505, -0.0018545, 0.0234537, 0.045489, 0.060559, 0.0659895, 0.0607234, 0.0456178, 0.0233263, -0.0022326, -0.0266889, -0.0461004, -0.0576667, -0.0601223, -0.0537565, -0.0401249, -0.0215822, -0.0007958, 0.0196338, 0.0373801, 0.0504584, 0.0572487, 0.056587, 0.0479766, 0.0318731, 0.009917, -0.0150327, -0.0391662, -0.0583046, -0.0687041, -0.0679112, -0.0554334, -0.0330195, -0.0044223, 0.0253232, 0.0509274, 0.0679129, 0.0734627, 0.066875, 0.0495633, 0.0246556, -0.0036783, -0.0309893, -0.0532366, -0.0672913, -0.0712375, -0.064514, -0.0479556, -0.0237467, 0.0047417, 0.0332878, 0.0573512, 0.0727729, 0.0765418, 0.0674814, 0.0466687, 0.0174303, -0.0151633, -0.045244, -0.067333, -0.0774779, -0.0740569, -0.0580551, -0.0327663, -0.0030153, 0.0258894, 0.0492058, 0.0635717, 0.0674228, 0.0609913, 0.0459797, 0.0250682, 0.0014147, -0.0217575, -0.0414623, -0.0551821, -0.0610753, -0.0581946, -0.0466958, -0.0279728, -0.0046268, 0.0198065, 0.041383, 0.0564586, 0.0624197, 0.0582467, 0.044747, 0.024384, 0.0007465, -0.022199, -0.0408778, -0.0527102, -0.0564299, -0.052088, -0.0408074, -0.0244172, -0.0051053, 0.0148226, 0.0330672, 0.0474124, 0.0558462, 0.0567923, 0.0494413, 0.0340993, 0.0124209, -0.0126028, -0.036986, -0.0564116, -0.0671001, -0.0666709, -0.0547581, -0.0331906, -0.0056746, 0.0229455, 0.0476601, 0.0642662, 0.0701246, 0.0645515, 0.0487968, 0.0256678, -0.0010649, -0.0273136, -0.0492476, -0.06374, -0.0686938, -0.0632724, -0.0480394, -0.0249815, 0.0026348, 0.0306094, 0.0543765, 0.0697488, 0.0737264, 0.0651949, 0.0453075, 0.0174044, -0.0135659, -0.0420132, -0.0628357, -0.0724489, -0.0694703, -0.0549096, -0.0318366, -0.0046347, 0.0219501, 0.0437166, 0.0576668, 0.062318, 0.0576836, 0.0450239, 0.0264912, 0.0047697, -0.0172333, -0.0366458, -0.0508738, -0.0578655, -0.0564102, -0.0464234, -0.029127, -0.0070152, 0.0164549, 0.0374323, 0.0523876, 0.0588126, 0.0557243, 0.0438422, 0.025399, 0.0036358, -0.0178756, -0.0359032, -0.0480592, -0.0530448, -0.0506418, -0.0415288, -0.0270411, -0.0089642, 0.0105981, 0.029314, 0.0447398, 0.0545327, 0.0567868, 0.0504552, 0.0357488, 0.0143679, -0.0105655, -0.0349366, -0.0543792, -0.0651703, -0.0650524, -0.0537695, -0.0331706, -0.0068537, 0.0205612, 0.044351, 0.0605824, 0.0667808, 0.0622532, 0.0480474, 0.0266252, 0.0013757, -0.0239146, -0.0455589, -0.0604051, -0.0662028, -0.0618988, -0.047842, -0.025856, 0.0008776, 0.0281771, 0.0514716, 0.066601, 0.0706517, 0.0626248, 0.0437484, 0.0173194, -0.0119083, -0.0386692, -0.0582472, -0.0674056, -0.0649701, -0.0519362, -0.0311048, -0.0063843, 0.0180403, 0.0384547, 0.0521414, 0.0576289, 0.0546919, 0.0441842, 0.0277945, 0.0077993, -0.0131589, -0.0322903, -0.0469177, -0.0548084, -0.0545523, -0.0459004, -0.0299525, -0.0090982, 0.0133253, 0.0336102, 0.0483766, 0.0552284, 0.0532074, 0.0429293, 0.0263782, 0.0064322, -0.0137347, -0.0312133, -0.0437605, -0.0499921, -0.0493935, -0.0422225, -0.0293865, -0.0123495, 0.0069271, 0.0260558, 0.0423789, 0.0532649, 0.0565447, 0.0510125, 0.036852, 0.0158354, -0.0088167, -0.0329381, -0.0521959, -0.062974, -0.0631458, -0.0525432, -0.0330019, -0.0079769, 0.0181692, 0.0410227, 0.0569154, 0.0635009, 0.0600258, 0.0473068, 0.0274762, 0.0035933, -0.0208025, -0.0421412, -0.0572492, -0.0637454, -0.060393, -0.0473684, -0.0263811, -0.0005624, 0.0259293, 0.048572, 0.0633078, 0.067363, 0.0598633, 0.0420789, 0.0172205, -0.0101899, -0.0352379, -0.0536074, -0.0624042, -0.060625, -0.0491889, -0.0305756, -0.0082079, 0.0142452, 0.0334836, 0.0470069, 0.0533234, 0.0519717, 0.0434245, 0.0289522, 0.0104854, -0.0095348, -0.0283628, -0.0432507, -0.0518428, -0.0526012, -0.0451626, -0.0305178, -0.0109357, 0.0103903, 0.0299205, 0.0444446, 0.0516934, 0.0507319, 0.0420486, 0.0273465, 0.009124, -0.0098246, -0.026863, -0.0398377, -0.0472481, -0.0482903, -0.0428395, -0.0314307, -0.0152652, 0.0037867, 0.0232528, 0.040272, 0.0519792, 0.0560265, 0.0511283, 0.0374819, 0.016922, -0.0072793, -0.0309626, -0.0498794, -0.0605548, -0.0610057, -0.0511396, -0.0327421, -0.0090778, 0.0157821, 0.037732, 0.0533341, 0.0603246, 0.0578643, 0.0465437, 0.0281951, 0.005583, -0.0179694, -0.0389855, -0.0542609, -0.0612999, -0.0587279, -0.0466113, -0.0265926, -0.001758, 0.0237954, 0.0456497, 0.059895, 0.0639192, 0.056971, 0.0403482, 0.0171449, -0.0083905, -0.0317327, -0.0489737, -0.057528, -0.0565009, -0.0466809, -0.0302084, -0.010044, 0.0106123, 0.0288246, 0.0422653, 0.0493912, 0.0494956, 0.0426987, 0.0299174, 0.0128115, -0.006328, -0.0247964, -0.0398119, -0.0489459, -0.0505753, -0.0442486, -0.0308613, -0.0125612, 0.0076214, 0.026347, 0.0406039, 0.0482519, 0.0483531, 0.0412325, 0.0282936, 0.0116692, -0.0061874, -0.0228695, -0.0362805, -0.0447875, -0.0473025, -0.0433486, -0.0331456, -0.0177016, 0.0011495, 0.0208395, 0.0383405, 0.0506234, 0.0552323, 0.050851, 0.0377092, 0.0176945, -0.0059036, -0.0289832, -0.0474323, -0.0579515, -0.0587019, -0.0496336, -0.032437, -0.0101534, 0.0134408, 0.0345273, 0.0498688, 0.0572604, 0.0557655, 0.0457522, 0.0287718, 0.0073301, -0.0154238, -0.0360781, -0.0514021, -0.0588248, -0.0568907, -0.0456021, -0.0265516, -0.0027682, 0.0217419, 0.0427007, 0.0563805, 0.0603554, 0.0540022, 0.0386207, 0.0171407, -0.0065075, -0.0282022, -0.0444204, -0.0528369, -0.0526197, -0.0443996, -0.0299739, -0.0118592, 0.0071772, 0.0245096, 0.0379283, 0.0458075, 0.0472082, 0.0419515, 0.0306686, 0.0148016, -0.0034885, -0.0215447, -0.036574, -0.0461087, -0.0484786, -0.0431793, -0.0310245, -0.0140257, 0.0049843, 0.022895, 0.0368955, 0.0449531, 0.0460984, 0.040477, 0.0291976, 0.0140454, -0.0028392, -0.019243, -0.0330898, -0.0425919, -0.0463885, -0.0437007, -0.0345047, -0.0196769, -0.0010428, 0.0187448, 0.0365317, 0.0491778, 0.0541734, 0.0502163, 0.0375899, 0.0182197, -0.0046313, -0.0269754, -0.0448785, -0.0552261, -0.0563034, -0.0480716, -0.0320991, -0.0111917, 0.0111678, 0.0314337, 0.0465444, 0.054327, 0.0537319, 0.0449137, 0.0291789, 0.0088213, -0.0131491, -0.0333791, -0.0486336, -0.0563052, -0.0548968, -0.0443764, -0.0263002, -0.0036336, 0.0197349, 0.0397091, 0.052781, 0.0567252, 0.0510283, 0.0369519, 0.0172223, -0.0045683, -0.0246953, -0.039994, -0.0483638, -0.0489987, -0.042344, -0.0298477, -0.0136076, 0.0039874, 0.0205601, 0.0339766, 0.0425237, 0.0450611, 0.041159, 0.031211, 0.0164784, -0.0009863, -0.018573, -0.0335025, -0.0433099, -0.046321, -0.0419974, -0.0310626, -0.0153664, 0.0024768, 0.0195894, 0.0333501, 0.0418177, 0.0439766, 0.039784, 0.0300512, 0.0162324, 0.0001925, -0.016, -0.0302515, -0.0406164, -0.0454953, -0.0438645, -0.0355135, -0.0212277, -0.0028404, 0.0169189, 0.0348038, 0.0476176, 0.0528559, 0.0492692, 0.0371959, 0.0185685, -0.0034237, -0.0249448, -0.0422564, -0.0524273, -0.0538529, -0.0464842, -0.0317448, -0.0121902, 0.008986, 0.0284855, 0.043388, 0.0515274, 0.0517428, 0.0440024, 0.0294063, 0.0100702, -0.0111164, -0.0308589, -0.0459354, -0.0537341, -0.0527546, -0.0429618, -0.0258849, -0.0044068, 0.0177399, 0.036678, 0.0491375, 0.0530877, 0.0481004, 0.0353688, 0.0173903, -0.0025927, -0.0212462, -0.0357373, -0.0441474, -0.0456542, -0.0404966, -0.0297855, -0.0152423, 0.001067, 0.0169681, 0.0303799, 0.039505, 0.0430261, 0.0403037, 0.0315415, 0.0178593, 0.0012182, -0.0158329, -0.0305637, -0.0405502, -0.0441345, -0.0407467, -0.0310084, -0.0165962, 0.0001014, 0.0164408, 0.0299845, 0.0388669, 0.0420052, 0.0391527, 0.030831, 0.0181962, 0.0028871, -0.0131307, -0.027729, -0.0388169, -0.0445907, -0.0438281, -0.0361801, -0.0223812, -0.0042899, 0.015304, 0.0331081, 0.0459282, 0.0513095, 0.0480703, 0.0365918, 0.0187854, -0.0022641, -0.0228987, -0.0395912, -0.0495941, -0.0513951, -0.0449063, -0.0313835, -0.0131301, 0.006928, 0.025707, 0.0404022, 0.0488476, 0.0497818, 0.0430107, 0.0294577, 0.0110894, -0.0093055, -0.0284904, -0.043281, -0.0510999, -0.0504808, -0.0414029, -0.0253603, -0.0051281, 0.0157465, 0.0336261, 0.0454871, 0.0494855, 0.0452571, 0.0338969, 0.017648, -0.000605, -0.0179002, -0.0316958, -0.040211, -0.0425777, -0.0388252, -0.0297501, -0.0167378, -0.0015756, 0.0137255, 0.0271158, 0.036718, 0.0410686, 0.0393657, 0.0316681, 0.0189807, 0.0031724, -0.0132887, -0.0277475, -0.0378422, -0.0419439, -0.0394535, -0.0308857, -0.0177319, -0.0021453, 0.013464, 0.0268258, 0.0361234, 0.0401859, 0.0385621, 0.0315067, 0.0199176, 0.0052466, -0.0106144, -0.0254924, -0.037161, -0.0436454, -0.0435746, -0.0365131, -0.0231774, -0.005452, 0.0138432, 0.031417, 0.0441191, 0.0495713, 0.0466682, 0.0358245, 0.0189077, -0.0011327, -0.0208427, -0.0369154, -0.0467736, -0.0489695, -0.0433528, -0.0310053, -0.0139905, 0.0050097, 0.0231032, 0.0375842, 0.0462807, 0.0478399, 0.0419292, 0.0293305, 0.0118928, -0.0076846, -0.0262369, -0.0406481, -0.0484086, -0.0481077, -0.0397434, -0.0247655, -0.0058219, 0.0137484, 0.0305674, 0.041863, 0.0459635, 0.0425385, 0.0325537, 0.0179815, 0.001358, -0.0146973, -0.0278953, -0.0365591, -0.0397558, -0.0373055, -0.0297136, -0.01807, -0.0039309, 0.0108194, 0.0241507, 0.0341221, 0.0391607, 0.038343, 0.0316139, 0.0198778, 0.0049092, -0.0109183, -0.0250455, -0.0351923, -0.0397701, -0.0381482, -0.0307215, -0.0187843, -0.0042524, 0.0106825, 0.0238931, 0.0335901, 0.0385053, 0.0379915, 0.0320611, 0.0213878, 0.0072736, -0.0084344, -0.0235093, -0.0356079, -0.0426271, -0.0430985, -0.0365403, -0.0236673, -0.0063803, 0.0124992, 0.0297161, 0.0421994, 0.0476713, 0.0451083, 0.0349442, 0.0189725, -2.05e-05, -0.0187981, -0.0342674, -0.0440009, -0.0465956, -0.0418272, -0.0306042, -0.0147624, 0.0032409, 0.0206817, 0.0349335, 0.0438149, 0.0458989, 0.0407476, 0.0290346, 0.0125098, -0.006218, -0.0240729, -0.0380311, -0.0456741, -0.0456625, -0.0380181, -0.024136, -0.0065141, 0.0117419, 0.0275252, 0.0383074, 0.0425642, 0.0399686, 0.0313374, 0.0183697, 0.0032673, -0.0116643, -0.0243539, -0.0331959, -0.0371747, -0.0359085, -0.0296434, -0.0192193, -0.0060037, 0.0082223, 0.0214479, 0.0316875, 0.0372895, 0.0372407, 0.0313985, 0.0205792, 0.0064601, -0.0086975, -0.0224508, -0.0326148, -0.0376414, -0.0368578, -0.0305291, -0.0197492, -0.0062062, 0.0081096, 0.0211928, 0.0312644, 0.036953, 0.0374242, 0.0324756, 0.0225955, 0.0089751, -0.0065616, -0.021737, -0.0341196, -0.0415189, -0.0424101, -0.036294, -0.0238941, -0.007118, 0.011238, 0.0279913, 0.0401816, 0.0456475, 0.0434412, 0.0339935, 0.0189997, 0.0010672, -0.0167868, -0.031673, -0.0412982, -0.0442884, -0.0403354, -0.0301755, -0.0154324, 0.0016347, 0.0184438, 0.0324361, 0.0414294, 0.0439454, 0.03947, 0.0285903, 0.0129683, -0.0048795, -0.0219803, -0.0354244, -0.0429069, -0.0431738, -0.0362668, -0.0235075, -0.00722, 0.009738, 0.0245304, 0.0348561, 0.039314, 0.0375594, 0.0302447, 0.0187955, 0.0050963, -0.0088284, -0.0210867, -0.030115, -0.0348085, -0.0346015, -0.029516, -0.0201817, -0.0078087, 0.0059086, 0.0189788, 0.0293906, 0.0354425, 0.0360633, 0.0310446, 0.021119, 0.0078558, -0.0066115, -0.0199686, -0.0301287, -0.0355785, -0.0355967, -0.0303135, -0.0206239, -0.0079982, 0.0057553, 0.018731, 0.0291425, 0.0355117, 0.036836, 0.0327314, 0.0235403, 0.010372, -0.0049607, -0.0201391, -0.0326698, -0.0403119, -0.0415205, -0.0358045, -0.0239018, -0.0077102, 0.0100296, 0.0262394, 0.0380902, 0.0435402, 0.041707, 0.0330004, 0.0190007, 0.0021261, -0.0148246, -0.0291549, -0.0386876, -0.0420605, -0.0388753, -0.0297065, -0.0159884, 0.0001923, 0.0163782, 0.0300752, 0.0391107, 0.0419761, 0.0381037, 0.0280148, 0.0132929, -0.0036424, -0.0199392, -0.0328256, -0.0401272, -0.0406773, -0.034526, -0.0229024, -0.0079421, 0.0077516, 0.0216078, 0.031536, 0.0362359, 0.0353214, 0.0292684, 0.019235, 0.0068154, -0.0062103, -0.0180949, -0.0272992, -0.0326309, -0.0333606, -0.0293181, -0.0209571, -0.0093595, 0.0038522, 0.0167127, 0.0272065, 0.0336124, 0.0348251, 0.0305805, 0.0215263, 0.0091158, -0.0046548, -0.0176049, -0.027746, -0.0335959, -0.0343771, -0.0300796, -0.0214033, -0.0096165, 0.0036303, 0.0165072, 0.0272089, 0.0341582, 0.0362064, 0.0328209, 0.0242307, 0.0114874, -0.0035998, -0.0186821, -0.0312333, -0.0389991, -0.040447, -0.0351101, -0.0237364, -0.0081942, 0.0088574, 0.0244669, 0.0359492, 0.0413823, 0.0399382, 0.0319892, 0.0189852, 0.0031494, -0.0129316, -0.0267358, -0.0361835, -0.0399129, -0.0374386, -0.0291879, -0.0164275, -0.0010918, 0.0144733, 0.027837, 0.0368474, 0.0399864, 0.0366563, 0.0273292, 0.0135134, -0.0024799, -0.0179383, -0.0302438, -0.0373598, -0.0382034, -0.0328212, -0.0223357, -0.0086812, 0.0057966, 0.0187823, 0.028375, 0.0333484, 0.0332547, 0.0283906, 0.0196625, 0.0084032, -0.003818, -0.0153723, -0.0247313, -0.0306194, -0.0321645, -0.0290365, -0.0215482, -0.0106765, 0.0020224, 0.0146204, 0.0251204, 0.0318027, 0.0335431, 0.0300292, 0.0218228, 0.0102552, -0.0028214, -0.0153643, -0.0254803, -0.031709, -0.0332073, -0.0298241, -0.0220758, -0.0110503, 0.0017357, 0.0145108, 0.0254456, 0.0328727, 0.0355207, 0.0327398, 0.0246764, 0.0123459, -0.0024437, -0.017332, -0.0297905, -0.0375841, -0.0392152, -0.0342487, -0.0234357, -0.0085977, 0.0077099, 0.0226805, 0.0337815, 0.039206, 0.0381657, 0.0309786, 0.0189546, 0.0041243, -0.0111247, -0.0244286, -0.0337902, -0.0378434, -0.03602, -0.0286154, -0.0167491, -0.0022236, 0.0127162, 0.0257051, 0.0346268, 0.0379758, 0.0351429, 0.0265598, 0.013657, -0.001374, -0.015974, -0.0276898, -0.0346261, -0.0357782, -0.0311761, -0.0218198, -0.0094332, 0.0038925, 0.0160805, 0.0253944, 0.0306584, 0.0313515, 0.0275937, 0.0200583, 0.0098454, -0.0016561, -0.0129108, -0.0223916, -0.0287484, -0.030992, -0.0286653, -0.0219671, -0.0117852, 0.0003909, 0.0126812, 0.0231235, 0.030017, 0.0322315, 0.0294111, 0.0220292, 0.0112878, -0.0011098, -0.0132563, -0.0233446, -0.0299256, -0.0320863, -0.0295394, -0.0226331, -0.012296, 6.79e-05, 0.0127305, 0.0238347, 0.0316352, 0.0347637, 0.032486, 0.0248941, 0.0129794, -0.0014565, -0.0160614, -0.0283318, -0.0360764, -0.03785, -0.033254, -0.0230339, -0.0089453, 0.0065794, 0.0208924, 0.0316135, 0.0370407, 0.0364113, 0.0299773, 0.0189061, 0.0050411, -0.0094153, -0.0222416, -0.0315111, -0.0358489, -0.0346124, -0.0279832, -0.016955, -0.0032145, 0.0110891, 0.0236629, 0.0324423]}
\ No newline at end of file
packages/core/test/fixtures/rr_center.jsonadded+177−0View file
@@ -0,0 +1,177 @@
1+{
2+ "model": "free-edge Kirchhoff-Love Rayleigh-Ritz plate; Hertz impact; compact acoustic radiation",
3+ "plate": {
4+ "width": 0.03,
5+ "height": 0.03,
6+ "thickness": 0.001,
7+ "density": 2700.0,
8+ "young": 68900000000.0,
9+ "poisson": 0.33,
10+ "material_q": 1400.0,
11+ "hole_diameter": 0.0008,
12+ "hole_edge_offset": 0.0015
13+ },
14+ "hammer": {
15+ "mass": 0.01,
16+ "tip_radius": 0.002,
17+ "speed": 0.45,
18+ "young": 200000000000.0,
19+ "poisson": 0.29,
20+ "restitution": 0.62
21+ },
22+ "suspension": {
23+ "thread_length": 0.2,
24+ "dashpot": 0.00075
25+ },
26+ "render": {
27+ "sample_rate": 96000,
28+ "duration": 4.0,
29+ "modes": 72,
30+ "basis_order": 12,
31+ "quadrature_order": 30,
32+ "hit_x": 0.0,
33+ "hit_y": 0.0,
34+ "mic_distance": 0.35,
35+ "air_density": 1.204,
36+ "sound_speed": 343.0,
37+ "normalize_dbfs": -1.0
38+ },
39+ "derived": {
40+ "plate_mass_kg": 0.002428642831973649,
41+ "bending_rigidity_Nm": 6.443347173904911,
42+ "contact_duration_s": 9.437140797643829e-05,
43+ "impact_impulse_Ns": 0.007290000000000001,
44+ "estimated_peak_contact_force_N": 138.83085722430667,
45+ "critical_frequency_hz": 12120.88938013311,
46+ "unnormalized_peak_pressure_pa_approx": 0.022317190943142946,
47+ "retained_mode_count": 18
48+ },
49+ "modes": [
50+ {
51+ "mode": 1,
52+ "frequency_hz": 3604.9596964856946,
53+ "q_total": 409.77691559682415,
54+ "hit_participation": 1.0583634328926802e-09,
55+ "radiation_participation": -1.244794241894155e-15
56+ },
57+ {
58+ "mode": 2,
59+ "frequency_hz": 5266.636133078648,
60+ "q_total": 352.58511359635384,
61+ "hit_participation": 0.03668740173475439,
62+ "radiation_participation": 1.992927445782186e-05
63+ },
64+ {
65+ "mode": 3,
66+ "frequency_hz": 6668.263257152294,
67+ "q_total": 326.58709461223276,
68+ "hit_participation": -34.68101176778089,
69+ "radiation_participation": 1.4105340499224371e-06
70+ },
71+ {
72+ "mode": 4,
73+ "frequency_hz": 9366.202787631966,
74+ "q_total": 286.0010631952906,
75+ "hit_participation": -9.372003226945298e-11,
76+ "radiation_participation": -1.2469848988038964e-14
77+ },
78+ {
79+ "mode": 5,
80+ "frequency_hz": 9376.34030979602,
81+ "q_total": 284.22231555829495,
82+ "hit_participation": -0.03728853036299697,
83+ "radiation_participation": -2.0110600388817583e-05
84+ },
85+ {
86+ "mode": 6,
87+ "frequency_hz": 16649.866412026182,
88+ "q_total": 226.0519036556788,
89+ "hit_participation": -1.5150137349005784e-09,
90+ "radiation_participation": -1.6376937619597606e-14
91+ },
92+ {
93+ "mode": 7,
94+ "frequency_hz": 16653.389527568852,
95+ "q_total": 225.92114832465757,
96+ "hit_participation": -0.5073911517581395,
97+ "radiation_participation": -8.741810799173736e-06
98+ },
99+ {
100+ "mode": 8,
101+ "frequency_hz": 17186.690074496233,
102+ "q_total": 222.49241835759418,
103+ "hit_participation": -33.82266517971492,
104+ "radiation_participation": 2.040628807792887e-05
105+ },
106+ {
107+ "mode": 9,
108+ "frequency_hz": 18640.51955827077,
109+ "q_total": 215.55182577190828,
110+ "hit_participation": -1.7189043387634406e-10,
111+ "radiation_participation": 1.455904212866501e-16
112+ },
113+ {
114+ "mode": 10,
115+ "frequency_hz": 21032.380680531944,
116+ "q_total": 204.772249224193,
117+ "hit_participation": -3.893290840729921e-11,
118+ "radiation_participation": 2.6798947239031933e-17
119+ },
120+ {
121+ "mode": 11,
122+ "frequency_hz": 28498.130528923146,
123+ "q_total": 179.6188655807077,
124+ "hit_participation": 2.9490124156399465e-11,
125+ "radiation_participation": -1.7138782347340778e-15
126+ },
127+ {
128+ "mode": 12,
129+ "frequency_hz": 28529.915562837206,
130+ "q_total": 179.31059579926938,
131+ "hit_participation": 0.2927794327577138,
132+ "radiation_participation": -2.036212334621056e-05
133+ },
134+ {
135+ "mode": 13,
136+ "frequency_hz": 31826.28578815292,
137+ "q_total": 171.12813372083784,
138+ "hit_participation": 0.2653425100307465,
139+ "radiation_participation": -6.725749902454069e-06
140+ },
141+ {
142+ "mode": 14,
143+ "frequency_hz": 33486.01926408711,
144+ "q_total": 167.25840139258062,
145+ "hit_participation": 42.95675271247443,
146+ "radiation_participation": 1.6500481331107334e-05
147+ },
148+ {
149+ "mode": 15,
150+ "frequency_hz": 35733.8715922345,
151+ "q_total": 162.63778906596605,
152+ "hit_participation": -5.497340807279114e-11,
153+ "radiation_participation": -1.1577883183459675e-15
154+ },
155+ {
156+ "mode": 16,
157+ "frequency_hz": 35769.727749465426,
158+ "q_total": 162.43284557542367,
159+ "hit_participation": 0.5544809543476995,
160+ "radiation_participation": 1.951905250464122e-05
161+ },
162+ {
163+ "mode": 17,
164+ "frequency_hz": 41352.50867671962,
165+ "q_total": 152.43256220897143,
166+ "hit_participation": 5.650912980193709e-12,
167+ "radiation_participation": 6.913228805605423e-17
168+ },
169+ {
170+ "mode": 18,
171+ "frequency_hz": 43780.89637646587,
172+ "q_total": 148.47296961212075,
173+ "hit_participation": 0.46833976406010963,
174+ "radiation_participation": 2.308969641442734e-05
175+ }
176+ ]
177+}
packages/core/test/fixtures/rr_center_o8.jsonadded+177−0View file
@@ -0,0 +1,177 @@
1+{
2+ "model": "free-edge Kirchhoff-Love Rayleigh-Ritz plate; Hertz impact; compact acoustic radiation",
3+ "plate": {
4+ "width": 0.03,
5+ "height": 0.03,
6+ "thickness": 0.001,
7+ "density": 2700.0,
8+ "young": 68900000000.0,
9+ "poisson": 0.33,
10+ "material_q": 1400.0,
11+ "hole_diameter": 0.0008,
12+ "hole_edge_offset": 0.0015
13+ },
14+ "hammer": {
15+ "mass": 0.01,
16+ "tip_radius": 0.002,
17+ "speed": 0.45,
18+ "young": 200000000000.0,
19+ "poisson": 0.29,
20+ "restitution": 0.62
21+ },
22+ "suspension": {
23+ "thread_length": 0.2,
24+ "dashpot": 0.00075
25+ },
26+ "render": {
27+ "sample_rate": 96000,
28+ "duration": 4.0,
29+ "modes": 72,
30+ "basis_order": 8,
31+ "quadrature_order": 30,
32+ "hit_x": 0.0,
33+ "hit_y": 0.0,
34+ "mic_distance": 0.35,
35+ "air_density": 1.204,
36+ "sound_speed": 343.0,
37+ "normalize_dbfs": -1.0
38+ },
39+ "derived": {
40+ "plate_mass_kg": 0.002428642831973649,
41+ "bending_rigidity_Nm": 6.443347173904911,
42+ "contact_duration_s": 9.437140797643829e-05,
43+ "impact_impulse_Ns": 0.007290000000000001,
44+ "estimated_peak_contact_force_N": 138.83085722430667,
45+ "critical_frequency_hz": 12120.88938013311,
46+ "unnormalized_peak_pressure_pa_approx": 0.022050006808515085,
47+ "retained_mode_count": 18
48+ },
49+ "modes": [
50+ {
51+ "mode": 1,
52+ "frequency_hz": 3604.9693185188185,
53+ "q_total": 409.77652879524476,
54+ "hit_participation": -1.7222218139786974e-11,
55+ "radiation_participation": 5.581137548809426e-17
56+ },
57+ {
58+ "mode": 2,
59+ "frequency_hz": 5266.636501309742,
60+ "q_total": 352.5847810082564,
61+ "hit_participation": 0.03658700016993266,
62+ "radiation_participation": 1.9930003103148852e-05
63+ },
64+ {
65+ "mode": 3,
66+ "frequency_hz": 6668.264792483605,
67+ "q_total": 326.5870538267108,
68+ "hit_participation": -34.68066328304039,
69+ "radiation_participation": 1.411095213048475e-06
70+ },
71+ {
72+ "mode": 4,
73+ "frequency_hz": 9366.248780434324,
74+ "q_total": 286.00050444298705,
75+ "hit_participation": 4.6314924846925354e-11,
76+ "radiation_participation": 1.7427995390247446e-16
77+ },
78+ {
79+ "mode": 5,
80+ "frequency_hz": 9376.385569557819,
81+ "q_total": 284.2217185265718,
82+ "hit_participation": 0.03695144706033362,
83+ "radiation_participation": 2.011098059434318e-05
84+ },
85+ {
86+ "mode": 6,
87+ "frequency_hz": 16654.1480240132,
88+ "q_total": 226.02753560152746,
89+ "hit_participation": -1.8074131247639446e-10,
90+ "radiation_participation": -2.590747525443318e-15
91+ },
92+ {
93+ "mode": 7,
94+ "frequency_hz": 16657.698035959937,
95+ "q_total": 225.8958597175366,
96+ "hit_participation": -0.51288280745123,
97+ "radiation_participation": -8.774436508815421e-06
98+ },
99+ {
100+ "mode": 8,
101+ "frequency_hz": 17186.803715953065,
102+ "q_total": 222.4917353819469,
103+ "hit_participation": -33.812028123825804,
104+ "radiation_participation": 2.040754706277647e-05
105+ },
106+ {
107+ "mode": 9,
108+ "frequency_hz": 18643.94941216507,
109+ "q_total": 215.53505034551338,
110+ "hit_participation": 4.5685499906562445e-12,
111+ "radiation_participation": 2.5283745782252536e-18
112+ },
113+ {
114+ "mode": 10,
115+ "frequency_hz": 21041.063329621906,
116+ "q_total": 204.7361742340462,
117+ "hit_participation": 2.0006111909413135e-12,
118+ "radiation_participation": 2.711339834266098e-19
119+ },
120+ {
121+ "mode": 11,
122+ "frequency_hz": 28503.812105513523,
123+ "q_total": 179.60325990711405,
124+ "hit_participation": -2.7458511148202387e-12,
125+ "radiation_participation": 7.435773878593382e-17
126+ },
127+ {
128+ "mode": 12,
129+ "frequency_hz": 28535.646237367713,
130+ "q_total": 179.2943367116017,
131+ "hit_participation": -0.2909508538856467,
132+ "radiation_participation": 2.0390967781615517e-05
133+ },
134+ {
135+ "mode": 13,
136+ "frequency_hz": 31872.558631649914,
137+ "q_total": 171.02057766206494,
138+ "hit_participation": 0.2490295909015145,
139+ "radiation_participation": -6.465414110299166e-06
140+ },
141+ {
142+ "mode": 14,
143+ "frequency_hz": 33550.15544074365,
144+ "q_total": 167.11943322508685,
145+ "hit_participation": 42.18760398817165,
146+ "radiation_participation": 1.637847286175347e-05
147+ },
148+ {
149+ "mode": 15,
150+ "frequency_hz": 35803.62635975656,
151+ "q_total": 162.4976794263023,
152+ "hit_participation": 1.1620239736165928e-11,
153+ "radiation_participation": 6.053997358921389e-17
154+ },
155+ {
156+ "mode": 16,
157+ "frequency_hz": 35837.893771465744,
158+ "q_total": 162.3025058567845,
159+ "hit_participation": 0.5157015685804621,
160+ "radiation_participation": 1.9077503254014464e-05
161+ },
162+ {
163+ "mode": 17,
164+ "frequency_hz": 41366.48111026191,
165+ "q_total": 152.4096191200859,
166+ "hit_participation": -2.1750879010967896e-12,
167+ "radiation_participation": -1.3688052427629493e-18
168+ },
169+ {
170+ "mode": 18,
171+ "frequency_hz": 43840.374132755,
172+ "q_total": 148.38406652925917,
173+ "hit_participation": 0.39965784279916045,
174+ "radiation_participation": 2.3006420269506197e-05
175+ }
176+ ]
177+}
packages/core/test/fixtures/rr_center_wav.jsonadded+1−0View file
@@ -0,0 +1 @@
1+{"fs": 96000, "samples": [0.0, -0.1435658, -0.6702253, -0.2622192, 0.7295125, 0.7243446, 0.3965215, -0.0872593, -0.6614562, -0.1475301, 0.2364484, 0.0818658, 0.601355, -0.037127, -0.891251, -0.2257115, -0.0364306, 0.0477865, 0.4995931, -0.3332537, -0.5598657, 0.3138445, 0.3040148, 0.4890454, 0.4964191, -0.5408182, -0.3920906, 0.2513639, -0.0026052, 0.2085504, -0.1243701, -0.885905, -0.1682428, 0.3507078, 0.1917464, 0.4620244, -0.1115286, -0.4974583, 0.3874445, 0.4358236, 0.1828306, 0.180097, -0.6353838, -0.5775122, 0.2782572, 0.0747528, 0.0198225, -0.0468743, -0.6397752, 0.0030423, 0.6697732, 0.2938855, 0.2777627, -0.0945515, -0.5272463, 0.24594, 0.3973821, -0.1167212, -0.1301719, -0.5889061, -0.5205151, 0.4114067, 0.3187178, 0.00572, 0.0520271, -0.3410082, 0.0863738, 0.7399548, 0.2175132, -0.0920922, -0.2464818, -0.5687619, 0.0785564, 0.392141, -0.2132899, -0.2456184, -0.3228963, -0.2615799, 0.5799394, 0.5266803, -0.0532752, -0.0115417, -0.209557, -0.0031916, 0.5821599, 0.0423659, -0.4362631, -0.3390787, -0.4475279, 0.088675, 0.4969786, -0.1138913, -0.2211018, -0.0339378, -0.0029521, 0.5966198, 0.5158214, -0.2404392, -0.2465776, -0.217919, -0.1179462, 0.3693958, -0.0401206, -0.5555067, -0.2566034, -0.1167648, 0.2517309, 0.6069624, 0.0102094, -0.2487891, 0.0659962, 0.0874148, 0.3835943, 0.305803, -0.4581043, -0.463334, -0.1560811, -0.0644578, 0.3002713, 0.050893, -0.4605679, -0.0854225, 0.2183745, 0.3489567, 0.5435268, -0.029747, -0.4030688, -0.0120908, 0.0596461, 0.1287279, 0.0949172, -0.5273847, -0.4790181, 0.0577464, 0.1609601, 0.3490447, 0.1746037, -0.3235225, -0.0038484, 0.3559589, 0.2658243, 0.2617393, -0.2123627, -0.5718772, -0.103732, 0.1012237, 0.0422086, 0.0405462, -0.3687251, -0.3067829, 0.3112209, 0.3809828, 0.3065591, 0.1328956, -0.3176206, -0.0968827, 0.3022576, 0.100438, -0.0490664, -0.3244617, -0.5653748, -0.0435391, 0.2872572, 0.1176787, 0.0817731, -0.1542778, -0.1359774, 0.421266, 0.4199164, 0.0942227, -0.0836871, -0.4108474, -0.2277539, 0.221053, 0.0256898, -0.1861301, -0.2547118, -0.3380239, 0.153025, 0.4966372, 0.1950172, 0.039483, -0.0950861, -0.1162628, 0.3134801, 0.2803496, -0.1682415, -0.2995209, -0.4027495, -0.1951019, 0.2712452, 0.1212706, -0.1519528, -0.1028245, -0.0708861, 0.2797453, 0.537032, 0.1216233, -0.1606083, -0.1840134, -0.1810678, 0.1438071, 0.1480191, -0.2997508, -0.3488489, -0.209485, 0.0113696, 0.4116388, 0.2617776, -0.1079751, -0.0402546, 0.0454265, 0.2209142, 0.360763, -0.0827036, -0.3984838, -0.2636521, -0.1502568, 0.114666, 0.165275, -0.2253633, -0.2337389, 0.0463532, 0.2213332, 0.444442, 0.2437667, -0.1973429, -0.143714, 0.0106499, 0.0816794, 0.1385679, -0.2205693, -0.4759547, -0.1929925, 0.0464582, 0.2242898, 0.2569475, -0.0905446, -0.129985, 0.182478, 0.2757781, 0.2857243, 0.0517843, -0.3733813, -0.2920713, -0.018563, 0.031541, 0.0528539, -0.1741635, -0.3450182, 0.0081488, 0.2914095, 0.3137726, 0.2453447, -0.0896055, -0.1776901, 0.1284409, 0.177528, 0.0525727, -0.1435099, -0.4419581, -0.286826, 0.0949329, 0.1421672, 0.100808, -0.0458909, -0.1686526, 0.1475966, 0.3899216, 0.2489039, 0.0609499, -0.2204379, -0.2968843, 0.018526, 0.096713, -0.0743882, -0.1931015, -0.3156726, -0.1086743, 0.297061, 0.2993634, 0.1299167, -0.00746, -0.116656, 0.1068967, 0.2901714, 0.0548561, -0.1821534, -0.3261638, -0.3073143, 0.0384276, 0.1676024, -0.0256263, -0.1033331, -0.1085045, 0.074692, 0.3961552, 0.3079785, 0.0093757, -0.1251686, -0.1848426, -0.0125057, 0.1497159, -0.0838088, -0.2922442, -0.2754786, -0.143515, 0.1875347, 0.3090809, 0.0689135, -0.036693, 0.0027255, 0.1182621, 0.3043567, 0.1462829, -0.2058714, -0.2875278, -0.2232275, -0.040802, 0.1301921, -0.0478423, -0.2170704, -0.0955836, 0.0858718, 0.3079698, 0.3406437, 0.0329014, -0.1252453, -0.0488356, 0.0337609, 0.1280213, -0.0203279, -0.3249237, -0.3077823, -0.1040242, 0.0850202, 0.2216188, 0.0653267, -0.1072897, 0.0331156, 0.1962748, 0.271866, 0.1972561, -0.1316732, -0.2871158, -0.1388087, -0.0220974, 0.0398852, -0.0530534, -0.2594111, -0.1612337, 0.1158983, 0.2555536, 0.2776189, 0.0859969, -0.1170468, -0.0054023, 0.1334657, 0.1085956, -0.0148771, -0.2687505, -0.3410485, -0.0966172, 0.0773803, 0.1039389, 0.0327516, -0.1117659, -0.0100226, 0.2482642, 0.2896011, 0.1726529, -0.0441821, -0.2325406, -0.1171661, 0.0442929, -0.0072665, -0.1188504, -0.2505626, -0.2234431, 0.0742433, 0.2533952, 0.1975674, 0.0846984, -0.0476291, 0.0097697, 0.2042078, 0.1620736, -0.0390217, -0.2176295, -0.3079656, -0.1353642, 0.0773053, 0.0384761, -0.0612589, -0.1072455, -0.0359026, 0.2187022, 0.3248264, 0.156571, -0.0203202, -0.1281819, -0.0815309, 0.0813134, 0.0301086, -0.1710382, -0.2604559, -0.2206601, -0.0035152, 0.2138483, 0.1542885, 0.0167321, -0.0086616, 0.05256, 0.2119808, 0.2280379, -0.0146263, -0.2052987, -0.2331296, -0.1356155, 0.0395606, 0.0259105, -0.1360334, -0.1465862, -0.0225832, 0.1713424, 0.3098605, 0.1757416, -0.0302756, -0.0644664, -0.0098082, 0.0837689, 0.0630983, -0.1628719, -0.2911697, -0.2012248, -0.0334711, 0.1357749, 0.1265444, -0.0292517, -0.0295887, 0.1041705, 0.2210421, 0.2442943, 0.049158, -0.1805274, -0.186033, -0.0850461, -0.0007972, -0.0091624, -0.1648175, -0.2141538, -0.0381639, 0.1519622, 0.2516067, 0.185723, -0.0040703, -0.0388413, 0.0724897, 0.120225, 0.065649, -0.1207855, -0.2902916, -0.2120288, -0.0310329, 0.0650874, 0.0606157, -0.0501218, -0.0714951, 0.1054387, 0.2439436, 0.229609, 0.0883639, -0.1140467, -0.1486411, -0.0240889, 0.0173497, -0.0484288, -0.1715256, -0.2421708, -0.09048, 0.1284734, 0.1939998, 0.138655, 0.0213206, -0.0174426, 0.1097673, 0.1793485, 0.0753435, -0.0951862, -0.2425075, -0.2156217, -0.0372961, 0.0390039, -0.0130421, -0.0782435, -0.0815249, 0.0748258, 0.2506283, 0.2278976, 0.0843918, -0.0535718, -0.0977273, 0.0035657, 0.0542238, -0.0623744, -0.1920001, -0.2342662, -0.1277409, 0.0819033, 0.1579606, 0.0795266, 0.0139072, 0.0193969, 0.1264312, 0.2202833, 0.116662, -0.0771612, -0.1893505, -0.1816701, -0.0578157, 0.0162951, -0.0666515, -0.1387962, -0.0946641, 0.0478844, 0.2194169, 0.2325977, 0.0856301, -0.0184634, -0.0231576, 0.0400977, 0.0801253, -0.038572, -0.2042573, -0.2306004, -0.1311065, 0.0255798, 0.1082325, 0.0338063, -0.0298256, 0.0306919, 0.142183, 0.2228459, 0.1544882, -0.0402267, -0.1467897, -0.117246, -0.0432383, -0.0037551, -0.0850765, -0.1854941, -0.1291279, 0.0290836, 0.169115, 0.202186, 0.0890004, -0.0074887, 0.0287087, 0.0933523, 0.0963913, -0.0118793, -0.1856876, -0.2317628, -0.1228393, -0.0027784, 0.0498302, 0.0002708, -0.0612027, 0.0156633, 0.1583144, 0.2201133, 0.1613989, 0.001739, -0.1105402, -0.0717691, -0.005574, -0.0165883, -0.1018341, -0.2010509, -0.1672445, 0.0020822, 0.13178, 0.14962, 0.0794425, 0.0082197, 0.0560497, 0.1481444, 0.1316376, 0.0102198, -0.1450179, -0.2139226, -0.1249701, -0.0169923, -0.0067314, -0.0544181, -0.0891237, -0.0150067, 0.1481672, 0.2233938, 0.1581805, 0.0345354, -0.05636, -0.0305116, 0.0374708, 0.0004714, -0.1132147, -0.2009148, -0.1829335, -0.0365258, 0.0950364, 0.0964198, 0.0383179, 0.0101283, 0.064979, 0.1689748, 0.1691652, 0.0363822, -0.1041684, -0.1638848, -0.1090491, -0.0193983, -0.0304946, -0.1043235, -0.1211136, -0.0407916, 0.1120945, 0.2077514, 0.1516313, 0.042925, -0.009127, 0.0139109, 0.0662519, 0.0284398, -0.1086319, -0.2020273, -0.1792706, -0.0637872, 0.0543122, 0.0600044, -0.0010852, -0.0005332, 0.0763552, 0.1729261, 0.1883227, 0.0666643, -0.0736883, -0.1162899, -0.0770615, -0.0225635, -0.0430108, -0.1340057, -0.1586274, -0.0655716, 0.0743544, 0.1683983, 0.139758, 0.0471709, 0.0227264, 0.0673711, 0.1023377, 0.0587243, -0.0790376, -0.1917029, -0.1740109, -0.0780531, 0.0049411, 0.0121034, -0.0400739, -0.0310336, 0.073061, 0.17365, 0.189631, 0.0953071, -0.0340598, -0.0719966, -0.0303104, -0.0067816, -0.0479924, -0.1399971, -0.1805599, -0.0927389, 0.0417889, 0.1171415, 0.1032682, 0.0389332, 0.0296086, 0.0976888, 0.1369404, 0.083312, -0.0420814, -0.1552698, -0.1551455, -0.0725299, -0.021191, -0.0344253, -0.0745188, -0.0611667, 0.0501258, 0.1650318, 0.1803303, 0.1040639, 0.0036409, -0.031911, 0.0080612, 0.0192186, -0.0488155, -0.1430944, -0.1850121, -0.1150817, 0.0131826, 0.0785401, 0.0620242, 0.0261447, 0.0359207, 0.1114, 0.1633305, 0.1069507, -0.0144677, -0.1129848, -0.1257159, -0.0643509, -0.0323004, -0.0718629, -0.113397, -0.0898591, 0.0167542, 0.1384387, 0.1656636, 0.1014259, 0.0332985, 0.0169169, 0.050545, 0.0561893, -0.0280378, -0.1380048, -0.1806362, -0.1282884, -0.0230619, 0.0358475, 0.0152507, -0.005918, 0.0313734, 0.114161, 0.1736036, 0.1303302, 0.0133789, -0.0715515, -0.0807425, -0.0426955, -0.0290555, -0.084415, -0.1407769, -0.1142222, -0.0124176, 0.0979132, 0.1341959, 0.083879, 0.0364983, 0.0482482, 0.0862324, 0.0869479, 0.0040205, -0.1149577, -0.1641908, -0.1211651, -0.0441825, -0.0023068, -0.0234253, -0.0415466, 0.0137006, 0.1103933, 0.1711923, 0.141992, 0.039973, -0.0378759, -0.0402335, -0.0156335, -0.0252061, -0.0895845, -0.1557265, -0.1362986, -0.0354245, 0.063471, 0.0985121, 0.066319, 0.0348988, 0.0655533, 0.1170013, 0.1137067, 0.0331998, -0.0815488, -0.1401067, -0.1079903, -0.0522307, -0.0366141, -0.0628275, -0.0782417, -0.0189656, 0.0909881, 0.159033, 0.1411052, 0.0646273, 0.001873, 0.0038747, 0.025111, -0.0045941, -0.0840827, -0.1565345, -0.1520019, -0.0631775, 0.0267144, 0.0538853, 0.0344482, 0.0237989, 0.0671876, 0.1324939, 0.1359068, 0.0581709, -0.0448719, -0.1024423, -0.0837671, -0.0432285, -0.0494098, -0.0897848, -0.1044327, -0.0490439, 0.0586129, 0.1328794, 0.1226626, 0.0669035, 0.0285715, 0.0380533, 0.0600473, 0.0250675, -0.0664851, -0.1436987, -0.149131, -0.0810725, -0.0045978, 0.0151609, -0.0012414, 0.0070349, 0.0637, 0.1359338, 0.1504626, 0.0798161, -0.0145396, -0.0647777, -0.0575303, -0.034848, -0.0552676, -0.1092712, -0.12685, -0.0720791, 0.0274298, 0.1023662, 0.1024099, 0.0617701, 0.0463879, 0.0690403, 0.0904133, 0.0559666, -0.0387129, -0.1223666, -0.1350724, -0.0876911, -0.0343465, -0.0242482, -0.0422838, -0.0246414, 0.0469537, 0.1263782, 0.1521338, 0.0994986, 0.0187873, -0.0210092, -0.016449, -0.0127019, -0.0489086, -0.1145942, -0.1432303, -0.0942375, -0.0044971, 0.0622125, 0.0704109, 0.0452482, 0.0475061, 0.0869365, 0.1126622, 0.0800022, -0.0079871, -0.0912151, -0.1092965, -0.0752947, -0.0452728, -0.0503432, -0.0720207, -0.0550847, 0.0214274, 0.10541, 0.1358853, 0.1003254, 0.0396423, 0.0121942, 0.0215997, 0.0163313, -0.0342109, -0.1075398, -0.1457396, -0.1088672, -0.0298026, 0.0259417, 0.0352867, 0.0257282, 0.0433583, 0.096252, 0.1307919, 0.1001241, 0.018394, -0.0594169, -0.083468, -0.0616417, -0.0495006, -0.0716825, -0.097775, -0.0811315, -0.0059901, 0.0807672, 0.1157905, 0.092487, 0.0534326, 0.0413858, 0.0567654, 0.0506041, -0.0089415, -0.0899637, -0.1353431, -0.1143608, -0.0527875, -0.0105563, -0.0074943, -0.008005, 0.0251554, 0.0900766, 0.1372222, 0.1176664, 0.0459571, -0.0209556, -0.0450637, -0.035887, -0.0396148, -0.0787742, -0.1159771, -0.1031629, -0.0342215, 0.0481997, 0.0867449, 0.0727129, 0.0511477, 0.0570457, 0.080176, 0.0773331, 0.0187588, -0.0648208, -0.1130492, -0.1023998, -0.0595992, -0.0326483, -0.0386555, -0.0401113, 0.0009214, 0.072785, 0.1269766, 0.1204078, 0.0625783, 0.0085437, -0.0086991, -0.0066017, -0.0234556, -0.0746378, -0.1236512, -0.1189625, -0.0574451, 0.0165913, 0.0557075, 0.0518395, 0.0447311, 0.0670381, 0.0999899, 0.0999102, 0.0447438, -0.0378139, -0.0898701, -0.0878131, -0.0616795, -0.0513604, -0.0653392, -0.0695297, -0.0264486, 0.05037, 0.1091455, 0.113688, 0.0733851, 0.0344223, 0.0269332, 0.0290198, 0.0028157, -0.0577227, -0.1166508, -0.1252811, -0.077257, -0.016659, 0.0154918, 0.0181724, 0.0244434, 0.0614482, 0.1081926, 0.1169863, 0.0700663, -0.0040379, -0.0555815, -0.0615732, -0.0499579, -0.0573956, -0.0828245, -0.0918995, -0.0527788, 0.0226699, 0.0830325, 0.0942345, 0.0694733, 0.0471282, 0.0499671, 0.0566739, 0.0287931, -0.0356226, -0.0977339, -0.1153174, -0.0820044, -0.0361434, -0.0152355, -0.0137335, 0.0006427, 0.0465638, 0.1025181, 0.1215472, 0.0850194, 0.0228696, -0.0218514, -0.0326403, -0.032997, -0.0552588, -0.09347, -0.109059, -0.0752342, -0.005133, 0.0552369, 0.0728163, 0.0616305, 0.0565797, 0.07065, 0.0815358, 0.0544995, -0.011865, -0.076696, -0.1007693, -0.0818089, -0.0519575, -0.0425124, -0.0455434, -0.0272492, 0.025404, 0.0873764, 0.1160714, 0.0929688, 0.0454694, 0.0129044, 0.0038699, -0.0049531, -0.0389206, -0.0896664, -0.1164959, -0.0928189, -0.0342559, 0.0189158, 0.0392791, 0.0391781, 0.0506826, 0.079972, 0.0995342, 0.0784692, 0.0167232, -0.047275, -0.0753146, -0.0681677, -0.0552859, -0.0590998, -0.0692998, -0.052946, 0.0015484, 0.0654972, 0.0990011, 0.0874187, 0.0543231, 0.0346071, 0.0324221, 0.0215219, -0.0183414, -0.075212, -0.1102758, -0.0968808, -0.0507202, -0.009158, 0.008022, 0.0139824, 0.0363513, 0.0780922, 0.1075854, 0.0942588, 0.0408583, -0.017996, -0.0479431, -0.0499988, -0.0523723, -0.0706292, -0.0891272, -0.0769894, -0.0237652, 0.0416353, 0.0795165, 0.0786911, 0.0607961, 0.0538613, 0.0591023, 0.0489961, 0.0050172, -0.0564878, -0.0982105, -0.0959355, -0.0630863, -0.0342121, -0.0240001, -0.0158666, 0.0137833, 0.0646253, 0.1045809, 0.1021302, 0.0609579, 0.0132172, -0.0132219, -0.0205746, -0.0346227, -0.0673635, -0.0979085, -0.0951079, -0.0503794, 0.010054, 0.048902, 0.0562193, 0.0531946, 0.0614705, 0.0771881, 0.0731371, 0.0311968, -0.0309552, -0.0760273, -0.0830343, -0.0639153, -0.0480909, -0.0469705, -0.041371, -0.00921, 0.0452528, 0.0908738, 0.0976025, 0.0680653, 0.03256, 0.0137061, 0.0058417, -0.0139589, -0.0549733, -0.0946467, -0.100812, -0.066356, -0.0154257, 0.0199736, 0.0314171, 0.0390292, 0.0606803, 0.0870994, 0.0906921, 0.0547383, -0.0044912, -0.0510184, -0.0657507, -0.0604931, -0.0588324, -0.0674588, -0.0663788, -0.0339524, 0.0230967, 0.0732973, 0.0893325, 0.0727707, 0.0500708, 0.0401042, 0.0338466, 0.0099691, -0.0368969, -0.0840377, -0.0998147, -0.0770159, -0.0384829, -0.0108562, 0.0010456, 0.0155358, 0.0476985, 0.0851185, 0.0987482, 0.0734196, 0.0237922, -0.0184857, -0.0365354, -0.0422965, -0.0553712, -0.0765692, -0.0845959, -0.0585213, -0.0048218, 0.0455853, 0.0679114, 0.0642341, 0.0561258, 0.0576912, 0.057506, 0.0342467, -0.0144054, -0.0649591, -0.08801, -0.0762535, -0.0502691, -0.032694, -0.0240874, -0.0069258, 0.0304757, 0.0744127, 0.0955578, 0.0796632, 0.0410848, 0.0067859, -0.0101588, -0.0215018, -0.0445356, -0.07594, -0.0924034, -0.0744197, -0.0278891, 0.0189456, 0.0437269, 0.0498601, 0.0555672, 0.0692213, 0.0770518, 0.0576599, 0.0096885, -0.0425963, -0.0718855, -0.0717613, -0.0594103, -0.0532196, -0.05017, -0.0320762, 0.0095063, 0.0591101, 0.0881949, 0.0826801, 0.0560592, 0.031778, 0.0183914, 0.0038892, -0.0264342, -0.066837, -0.092327, -0.0840999, -0.048215, -0.0093877, 0.013512, 0.0250531, 0.0419146, 0.067922, 0.0863248, 0.0759188, 0.0349878, -0.0135038, -0.0443643, -0.0530852, -0.0544591, -0.0618409, -0.0688833, -0.0559971, -0.0156984, 0.0351977, 0.0694678, 0.0738421, 0.0598316, 0.0475169, 0.0413586, 0.0278252, -0.0054514, -0.0508622, -0.083079, -0.0833939, -0.0579199, -0.0286866, -0.0105007, 0.0023084, 0.0250927, 0.0591892, 0.0855628, 0.0833132, 0.0509892, 0.009235, -0.0194585, -0.0323696, -0.0435027, -0.0621629, -0.0788915, -0.0734104, -0.0379365, 0.0109675, 0.0475067, 0.0601024, 0.0585018, 0.0587335, 0.061776, 0.0521176, 0.0182911, -0.0304247, -0.0690706, -0.0791795, -0.065559, -0.0475289, -0.0359495, -0.0234396, 0.0036417, 0.0446557, 0.0792801, 0.0862937, 0.064382, 0.0319119, 0.0078633, -0.006493, -0.0244862, -0.0528112, -0.0792855, -0.0832903, -0.0569196, -0.0149722, 0.0190672, 0.03569, 0.0441468, 0.0567406, 0.0711275, 0.0702422, 0.0421042, -0.0039388, -0.0441653, -0.0616663, -0.059955, -0.054929, -0.0537046, -0.046414, -0.0200231, 0.0232787, 0.0631284, 0.0784516, 0.0673105, 0.0458264, 0.0291196, 0.0164468, -0.004519, -0.0385805, -0.071862, -0.0834812, -0.0660012, -0.0325066, -0.0034376, 0.0136002, 0.0278218, 0.0489938, 0.071908, 0.0788686, 0.0578855, 0.017391, -0.0207161, -0.0417917, -0.0492586, -0.0557534, -0.0649254, -0.0648077, -0.0420312, 0.0004717, 0.0429499, 0.0655774, 0.0656713, 0.0565928, 0.0495721, 0.0410109, 0.01902, -0.0193458, -0.0592389, -0.0794937, -0.0725246, -0.0497059, -0.0278668, -0.0122387, 0.0060779, 0.0347921, 0.0661918, 0.0817645, 0.0699045, 0.0379145, 0.0051934, -0.0159551, -0.0296129, -0.04605, -0.0656262, -0.0748104, -0.0597993, -0.0231766, 0.0165989, 0.0420083, 0.0508488, 0.0539393, 0.0588638, 0.0589975, 0.0415961, 0.004578, -0.0372305, -0.0634985, -0.0665097, -0.0557988, -0.0444931, -0.0344883, -0.0164998, 0.0156648, 0.0527284, 0.0754342, 0.0723674, 0.0499165, 0.0246073, 0.0061893, -0.0100799, -0.0329425, -0.0600571, -0.0765761, -0.068857, -0.039062, -0.0041085, 0.0207249, 0.03449, 0.0463831, 0.0609346, 0.0693377, 0.0579631, 0.0248293, -0.0156864, -0.0449195, -0.0558137, -0.0562446, -0.0561109, -0.0539444, -0.0391488, -0.0063331, 0.0345104, 0.0640222, 0.0706039, 0.0594375, 0.0439546, 0.0306891, 0.0139408, -0.0139262, -0.0485238, -0.0734844, -0.0749252, -0.0543233, -0.0264717, -0.0043597, 0.0122284, 0.0314565, 0.0551716, 0.0726345, 0.0695852, 0.0435875, 0.0080097, -0.0203955, -0.0359546, -0.0455, -0.056236, -0.064027, -0.0566645, -0.0285469, 0.0105916, 0.0425502, 0.056307, 0.0559536, 0.0521405, 0.0479651, 0.0359056, 0.0085458, -0.0289959, -0.0598257, -0.0697901, -0.0595039, -0.0412322, -0.0250558, -0.0093889, 0.0134227, 0.043366, 0.0680668, 0.0728304, 0.0546893, 0.0255013, 8.89e-05, -0.0172379, -0.0327355, -0.0513494, -0.0670029, -0.0665789, -0.0441208, -0.008783, 0.0227392, 0.0406762, 0.0484507, 0.0544469, 0.0590917, 0.053108, 0.028862, -0.00851, -0.0427144, -0.0599221, -0.0599102, -0.052472, -0.0444198, -0.0322793, -0.0084723, 0.0259964, 0.0577324, 0.0715132, 0.0635578, 0.0435203, 0.023562, 0.0064625, -0.0137054, -0.0399102, -0.0641202, -0.072396, -0.0581788, -0.0294106, -0.0009933, 0.0188227, 0.0331225, 0.0481442, 0.0620647, 0.0642151, 0.0464648, 0.0135036, -0.0197256, -0.0406921, -0.0488682, -0.0520011, -0.0539855, -0.0491805, -0.0294994, 0.0039928, 0.0382155, 0.0582945, 0.0600081, 0.0508751, 0.039829, 0.0274071, 0.0073316, -0.0223733, -0.0524236, -0.0683305, -0.0630339, -0.0428228, -0.0202129, -0.0016145, 0.0161845, 0.037648, 0.0588931, 0.0682272, 0.0571264, 0.0296675, -0.0006142, -0.0227971, -0.0364692, -0.0476488, -0.057902, -0.0598855, -0.0450507, -0.0144563, 0.0195975, 0.0433203, 0.0525664, 0.053265, 0.051481, 0.0453267, 0.0278071, -0.0027133, -0.0365714, -0.0592107, -0.0632862, -0.0534065, -0.0391768, -0.0245838, -0.0056947, 0.0208846, 0.0494912, 0.0673297, 0.0652469, 0.0461029, 0.0214853, 0.0003958, -0.0172809, -0.0360473, -0.0551548, -0.0655927, -0.0580655, -0.0332297, -0.0023707, 0.0222056, 0.0368372, 0.046216, 0.054041, 0.0561768, 0.0446304, 0.0176423, -0.0155494, -0.041172, -0.0521944, -0.0521383, -0.0479859, -0.0410215, -0.0259808, 0.000508, 0.0321741, 0.0558304, 0.0620605, 0.0525087, 0.036384, 0.0202618, 0.0028042, -0.0198711, -0.0453025, -0.0631114, -0.0632322, -0.0454899, -0.0198128, 0.0031765, 0.0206238, 0.0362686, 0.051801, 0.0612466, 0.0556599, 0.0330324, 0.0020696, -0.0244914, -0.0401593, -0.0477084, -0.0521275, -0.0524505, -0.0421816, -0.0177485, 0.014618, 0.0419762, 0.0550435, 0.0546255, 0.0477177, 0.0383656, 0.0237069, -0.0002648, -0.0302513, -0.0550079, -0.0638346, -0.0554012, -0.0376529, -0.0190352, -0.0011046, 0.0194479, 0.0426086, 0.0606903, 0.0635568, 0.0482882, 0.0224568, -0.0026416, -0.021319, -0.0356566, -0.0487907, -0.0577093, -0.0545858, -0.0353366, -0.0056844, 0.0222642, 0.0397501, 0.0470567, 0.0493772, 0.0484561, 0.0398718, 0.0190644, -0.0107747, -0.0385262, -0.0535853, -0.0539293, -0.0455456, -0.0344398, -0.0204178, 0.0002517, 0.0268605, 0.050833, 0.0613529, 0.054506, 0.0362064, 0.0157536, -0.0025479, -0.0206119, -0.0401105, -0.0563618, -0.0603432, -0.0472339, -0.0219251, 0.0046707, 0.0245949, 0.0377029, 0.0475905, 0.0540255, 0.0512531, 0.0342853, 0.0060675, -0.022805, -0.0421773, -0.0496817, -0.0497171, -0.0460779, -0.0369496, -0.0180483, 0.0097699, 0.0377027, 0.0548323, 0.0566077, 0.0472089, 0.0336115, 0.0182894, -0.0011357, -0.0254715, -0.0488019, -0.0611212, -0.056612, -0.0387315, -0.0165416, 0.0034387, 0.0210532, 0.0384395, 0.0534128, 0.0586866, 0.0484345, 0.0249049, -0.0023326, -0.0239753, -0.0375006, -0.0458698, -0.0507382, -0.0486044, -0.0344838, -0.0090039, 0.0193834, 0.0402215, 0.0488746, 0.0482061, 0.042987, 0.03374, 0.017166, -0.0074772, -0.0339006, -0.0519042, -0.0552145, -0.0459352, -0.031004, -0.0149262, 0.0029633, 0.0241556, 0.0451669, 0.057575, 0.0546763, 0.0376937, 0.0147123, -0.0063784, -0.0233727, -0.0381291, -0.0504152, -0.0550716, -0.0462785, -0.0242684, 0.0030868, 0.0259889, 0.0398802, 0.0465732, 0.0488962, 0.0455931, 0.0325177, 0.0088424, -0.0190697, -0.0411392, -0.051023, -0.0498234, -0.0425563, -0.0317462, -0.0156125, 0.0072696, 0.0327182, 0.0516666, 0.0567578, 0.0480644, 0.0318641, 0.0141313, -0.0038995, -0.0235852, -0.0432344, -0.0561295, -0.0552659, -0.0398923, -0.0166761, 0.0058024, 0.0234187, 0.0371403, 0.0479463, 0.0526453, 0.045879, 0.0262672, -0.0002527, -0.0240715, -0.0389863, -0.0453184, -0.0463367, -0.0426575, -0.031261, -0.0103352, 0.015873, 0.0382695, 0.0493887, 0.0485133, 0.0402759, 0.0286915, 0.0136011, -0.0066272, -0.0297468, -0.0483003, -0.0545456, -0.0467377, -0.030043, -0.0113189, 0.0063866, 0.0238593, 0.040858, 0.0526551, 0.0526642, 0.0386201, 0.0155463, -0.0078795, -0.0258715, -0.0381614, -0.0464616, -0.0495919, -0.0433068, -0.0252688, 0.0004852, 0.0250528, 0.0409933, 0.0469415, 0.0460489, 0.0405878, 0.0291526, 0.0097102, -0.0152975, -0.0381254, -0.0507027, -0.0504953, -0.0412011, -0.027871, -0.0121914, 0.0069182, 0.0285856, 0.0471023, 0.0548909, 0.0486084, 0.0318741, 0.011791, -0.0068784, -0.0237268, -0.0392318, -0.0505163, -0.0518872, -0.0399853, -0.0179606, 0.0061175, 0.025218, 0.0374804, 0.0445681, 0.0468884, 0.041607, 0.025975, 0.0021677, -0.0222455, -0.0392526, -0.0457976, -0.0442294, -0.0378377, -0.0268729, -0.0096349, 0.0129272, 0.0348316, 0.0481962, 0.0490265, 0.0396504, 0.0254214, 0.0096236, -0.0078368, -0.0269464, -0.0438658, -0.0519323, -0.0468569, -0.0306222, -0.0098991, 0.0093286, 0.02525, 0.038465, 0.0477496, 0.0488515, 0.038128, 0.0171319, -0.007148, -0.0270628, -0.0392221, -0.0447017, -0.0450366, -0.0390465, -0.0243675, -0.001894, 0.0222958, 0.0402371, 0.0474972, 0.0452606, 0.0372356, 0.025237, 0.0085163, -0.0127127, -0.0340537, -0.0482833, -0.0504268, -0.0413271, -0.0260429, -0.0090748, 0.0083561, 0.0263293, 0.042414, 0.0510845, 0.0476281, 0.0325207, 0.0115248, -0.0087192, -0.0249952, -0.0374078, -0.0457516, -0.0471556, -0.038136, -0.01896, 0.0047381, 0.0253049, 0.0381123, 0.0432783, 0.0428002, 0.03685, 0.0237224, 0.0034339, -0.0194777, -0.037639, -0.0457355, -0.0437081, -0.0351223, -0.0228999, -0.0073154, 0.0117553, 0.0313587, 0.045326, 0.0482873, 0.0397933, 0.0242407, 0.0068375, -0.0099952, -0.0261135, -0.0402252, -0.0481476, -0.0453143, -0.0310911, -0.0101929, 0.0105443, 0.0267496, 0.037835, 0.0442778, 0.0446062, 0.0359687, 0.0178577, -0.0053632, -0.0263763, -0.0396359, -0.0442276, -0.0422643, -0.0351291, -0.0221013, -0.0028626, 0.0193185, 0.0378476, 0.0468583, 0.0450821, 0.0356307, 0.022267, 0.0064434, -0.011798, -0.030548, -0.0447215, -0.0488494, -0.0413156, -0.0256058, -0.0072804, 0.0100623, 0.0256532, 0.0388713, 0.0467563, 0.0451196, 0.0324113, 0.0121311, -0.0090896, -0.0259221, -0.0368298, -0.0424928, -0.0425865, -0.0350351, -0.0187987, 0.0030898, 0.024045, 0.0379512, 0.0427845, 0.0403414, 0.0328633, 0.0206512, 0.003319, -0.017068, -0.0350311, -0.0446076, -0.0434444, -0.0338795, -0.0201049, -0.0046661, 0.0119737, 0.0287597, 0.0419078, 0.0463507, 0.039628, 0.0241615, 0.0054479, -0.0119475, -0.0264394, -0.0377865, -0.0442995, -0.0426304, -0.0307893, -0.011114, 0.0103191, 0.0275383, 0.0379788, 0.0421768, 0.0408155, 0.0329287, 0.0174975, -0.0035057, -0.0244386, -0.0389857, -0.0440724, -0.0408523, -0.0321181, -0.0193278, -0.0025609, 0.0168688, 0.0346144, 0.0449703, 0.0446984, 0.0351335, 0.0204844, 0.0042957, -0.0121427, -0.0280789, -0.0408337, -0.0459976, -0.0405704, -0.0258304, -0.0067811, 0.0113513, 0.0259742, 0.0366827, 0.0426772, 0.0415424, 0.0311848, 0.0128664, -0.0082436, -0.0259813, -0.0368125, -0.0407051, -0.038856, -0.0313076, -0.0173465, 0.0018878, 0.0218968, 0.0366588, 0.0423381, 0.039227, 0.03017, 0.0175056, 0.0019445, -0.0156449, -0.0320655, -0.0423009, -0.0426822, -0.0335699, -0.018791, -0.0025015, 0.0131756, 0.0274881, 0.0387133, 0.0433904, 0.0384959, 0.0244219, 0.0054612, -0.0129095, -0.0272391, -0.036731, -0.0412117, -0.0393063, -0.0292877, -0.0117611, 0.009012, 0.0270171, 0.0380071, 0.0412684, 0.038206, 0.0297959, 0.0159724, -0.0024697, -0.0219727, -0.0370109, -0.0432813, -0.0402265, -0.0304624, -0.0169945, -0.0013345, 0.0156331, 0.0315129, 0.0420097, 0.0432873, 0.0348456, 0.0199223, 0.0029924, -0.0129964, -0.0269382, -0.0376512, -0.0424988, -0.0386156, -0.0256768, -0.007149, 0.011554, 0.0262843, 0.0356506, 0.0397037, 0.0378665, 0.0288508, 0.0127845, -0.0070056, -0.02494, -0.0363263, -0.0397277, -0.0364313, -0.028042, -0.0151094, 0.001758, 0.0198978, 0.0345044, 0.0411311, 0.0384783, 0.0287231, 0.0152295, 0.0001978, -0.0153545, -0.0297329, -0.039512, -0.041024, -0.0331366, -0.0184212, -0.001405, 0.0143062, 0.0271825, 0.0364473, 0.0403513, 0.036495, 0.0241486, 0.006006, -0.0127783, -0.0275523, -0.0363069, -0.0391582, -0.0362986, -0.0271219, -0.0116315, 0.0076075, 0.0255579, 0.0372739, 0.0406179, 0.0365908, 0.0273076, 0.014081, -0.0023155, -0.0198436, -0.0344212, -0.0416294, -0.0394948, -0.0296018, -0.0154756, -3.39e-05, 0.0152702, 0.0291071, 0.038818, 0.0410351, 0.0341072, 0.0198269, 0.0025231, -0.0136434, -0.0265158, -0.0353675, -0.0391338, -0.0359486, -0.0248224, -0.0076312, 0.0109952, 0.0261277, 0.0350747, 0.0376967, 0.0346743, 0.026063, 0.0118959, -0.0059866, -0.0233164, -0.0352098, -0.0389199, -0.0349333, -0.0255537, -0.0127413, 0.002435, 0.0184419, 0.0320622, 0.0392497, 0.0375987, 0.0280148, 0.0138812, -0.0013679, -0.0157728, -0.0281944, -0.0367649, -0.0387268, -0.0322611, -0.0184625, -0.001237, 0.0149553, 0.027347, 0.0350734, 0.0376612, 0.0339892, 0.0231993, 0.0065877, -0.0118361, -0.0271266, -0.0360158, -0.0379555, -0.0339254, -0.0247138, -0.0107374, 0.0065373, 0.0235511, 0.0356861, 0.039761, 0.0356658, 0.0256572, 0.0122674, -0.0028912, -0.0183959, -0.0316837, -0.0391806, -0.0382495, -0.0291246, -0.014823, 0.0009, 0.0154806, 0.0276116, 0.0359014, 0.0381722, 0.0326129, 0.0197091, 0.0027783, -0.0136869, -0.0263709, -0.0340204, -0.0363995, -0.0329764, -0.0231299, -0.0077127, 0.009968, 0.0252277, 0.0344183, 0.0364809, 0.0323734, 0.0233713, 0.0102844, -0.0056522, -0.0215826, -0.033388, -0.0377315, -0.0339598, -0.0240573, -0.0108492, 0.0035917, 0.0178407, 0.0299153, 0.036868, 0.0361328, 0.0274589, 0.0134043, -0.0022056, -0.0163534, -0.0275059, -0.034636, -0.0362116, -0.0307037, -0.0182604, -0.0016551, 0.0147641, 0.0273128, 0.0343498, 0.0357587, 0.0315796, 0.0216339, 0.0066323, -0.0106608, -0.0258986, -0.0352196, -0.0370998, -0.0323699, -0.0227091, -0.0094373, 0.0061588, 0.0216906, 0.0335095, 0.0382437, 0.0347672, 0.0247082, 0.0110594, -0.0036204, -0.0176777, -0.0294393, -0.0364677, -0.0363289, -0.0283618, -0.0146085, 0.0011848, 0.015606, 0.0267493, 0.0336912, 0.0353684, 0.030533, 0.0190622, 0.0031527, -0.0131502, -0.0259247, -0.0330892, -0.0344282, -0.0303465, -0.0210521, -0.0071645, 0.0091048, 0.0238967, 0.0333223, 0.0354354, 0.0307868, 0.0212497, 0.0085557, -0.0059245, -0.0202501, -0.0313669, -0.0360856, -0.0329385, -0.023131, -0.0096365, 0.004625, 0.0177555, 0.0283439, 0.0345407, 0.0342648, 0.0266519, 0.0132574, -0.0023906, -0.0166237, -0.0271702, -0.0331379, -0.0339503, -0.0288327, -0.0176468, -0.0021372, 0.0140246, 0.0268177, 0.0337539, 0.0344325, 0.0295728, 0.0198979, 0.0062, -0.009655, -0.0242837, -0.0338866, -0.0361565, -0.0312824, -0.0212179, -0.0081457, 0.0062492, 0.020213, 0.0311819, 0.0362217, 0.0336063, 0.0240616, 0.0103891, -0.0041897, -0.0173788, -0.0277532, -0.0338675, -0.0340027, -0.0271823, -0.014462, 0.0010082, 0.0154517, 0.0261854, 0.0321112, 0.0329018, 0.0281898, 0.0178987, 0.0033535, -0.012282, -0.0250945, -0.0322632, -0.0330489, -0.0282346, -0.0189209, -0.0061102, 0.0086098, 0.0224102, 0.0318035, 0.0342976, 0.0296809, 0.0197723, 0.0070174, -0.0066023, -0.0194376, -0.0294266, -0.0340813, -0.0316595, -0.0224921, -0.0090748, 0.0052699, 0.0179223, 0.0273736, 0.0325319, 0.0321673, 0.0254506, 0.0131276, -0.0020535, -0.0163784, -0.0268711, -0.032191, -0.0321713, -0.026896, -0.0165768, -0.0023727, 0.0129704, 0.0257505, 0.0329485, 0.0334939, 0.028131, 0.0183015, 0.0053706, -0.0090858, -0.0226113, -0.032042, -0.0348176, -0.0303685, -0.0202882, -0.0071875, 0.0065957, 0.0192864, 0.0290873, 0.0338758, 0.0319622, 0.0233451, 0.0101522, -0.0043236, -0.0171692, -0.0266359, -0.0317422, -0.0315932, -0.0255141, -0.0140021, 0.0006268, 0.0148575, 0.0255258, 0.0309875, 0.0310307, 0.026, 0.0163386, 0.0030806, -0.0114521, -0.0238917, -0.0311743, -0.0319297, -0.0267288, -0.0171613, -0.0048519, 0.0086261, 0.0211788, 0.0300621, 0.0328198, 0.0286332, 0.01882, 0.0059975, -0.0072656, -0.0190882, -0.0279197, -0.0320656, -0.0300565, -0.021722, -0.0088558, 0.0053786, 0.0178963, 0.0267481, 0.0310416, 0.0302469, 0.0239877, 0.0127043, -0.0016128, -0.0156801, -0.0262429, -0.0314007, -0.0308738, -0.0252632, -0.0153496, -0.0022345, 0.0120261, 0.0243657, 0.0317363, 0.0325123, 0.0270628, 0.0170879, 0.0045267, -0.008881, -0.0212165, -0.0300652, -0.0330926, -0.0292648, -0.019591, -0.0066247, 0.0068353, 0.0186757, 0.0274001, 0.0316082, 0.0300264, 0.022337, 0.0099686, -0.004138, -0.0167855, -0.0257579, -0.0300813, -0.0294303, -0.0236671, -0.013185, 0.0003747, 0.014064, 0.0246496, 0.0299829, 0.0295904, 0.0241641, 0.0147309, 0.0024557, -0.0108807, -0.0226102, -0.0298505, -0.0308031, -0.0255747, -0.0158266, -0.0036983, 0.0089233, 0.0202869, 0.0283775, 0.0311439, 0.0274829, 0.0181134, 0.0054166, -0.0077008, -0.0189295, -0.0268043, -0.0302482, -0.0283278, -0.0207666, -0.0087272, 0.0051187, 0.0175724, 0.0262054, 0.0299336, 0.0286213, 0.0224701, 0.0120242, -0.0012578, -0.0147344, -0.0252757, -0.0305516, -0.0298729, -0.0239699, -0.0141541, -0.0018239, 0.0113102, 0.022875, 0.0301857, 0.0313387, 0.0261711, 0.0162284, 0.0038222, -0.0089029, -0.0201559, -0.0281574, -0.031108, -0.0278871, -0.0189332, -0.0063756, 0.0068558, 0.0182222, 0.0261183, 0.0295944, 0.0279646, 0.0210127, 0.0096593, -0.0037574, -0.0161558, -0.024942, -0.0288184, -0.0276498, -0.0218372, -0.012053, 0.0004004, 0.0132288, 0.0235328, 0.0289142, 0.0284516, 0.0227655, 0.0132918, 0.0015959, -0.0106682, -0.0214366, -0.0283346, -0.0295063, -0.0245861, -0.0149251, -0.0028564, 0.0093156, 0.0197666, 0.0269473, 0.0293902, 0.0261185, 0.0174233, 0.0051852, -0.007751, -0.018725, -0.0260202, -0.0287976, -0.0266602, -0.0195962, -0.0084556, 0.0046759, 0.0168799, 0.0254917, 0.0290446, 0.0273867, 0.0211242, 0.011164, -0.0011709, -0.0137938, -0.024014, -0.0294269, -0.0289265, -0.0230093, -0.0132113, -0.001322, 0.0109061, 0.0215507, 0.0284636, 0.0298519, 0.0251832, 0.0156008, 0.0034142, -0.0089058, -0.0193899, -0.0265495, -0.0291231, -0.0262437, -0.0180693, -0.0062176, 0.0066112, 0.0176728, 0.0250804, 0.0279624, 0.0260659, 0.0195112, 0.0090669, -0.0034518, -0.0153574, -0.0239873, -0.0277058, -0.0262443, -0.0202767, -0.0108485, 0.0007288, 0.0126011, 0.0223564, 0.0276837, 0.0273455, 0.02167, 0.0121802, 0.0007882, -0.0107034, -0.0205405, -0.0268678, -0.0280683, -0.0235407, -0.0142328, -0.0023558, 0.0095446, 0.0194068, 0.0258251, 0.027796, 0.0246762, 0.0166192, 0.0050455, -0.0075146, -0.0183028, -0.0253098, -0.0276434, -0.025226, -0.0184083, -0.0080221, 0.0042769, 0.0160064, 0.0245472, 0.028132, 0.0263765, 0.0200255, 0.0103315, -0.0012668, -0.0130053, -0.0226796, -0.0280793, -0.0278604, -0.0221721, -0.0124986, -0.0008988, 0.0106624, 0.0204455, 0.0267782, 0.0281904, 0.0240206, 0.0150036, 0.0032011, -0.0087901, -0.0187489, -0.0252103, -0.0272931, -0.0245128, -0.0170112, -0.005993, 0.0062469, 0.0170027, 0.0241329, 0.026618, 0.0244138, 0.0180087, 0.0082725, -0.0033138, -0.014539, -0.0229255, -0.0266252, -0.0250871, -0.0190026, -0.0097214, 0.0012351, 0.0122055, 0.0212468, 0.0263604, 0.0261991, 0.0207469, 0.0113636, 0.000144, -0.010846, -0.0198948, -0.0255461, -0.0265788, -0.0224063, -0.0136275, -0.0021256, 0.0095443, 0.0190715, 0.0249476, 0.0264244, 0.0232515, 0.0157002, 0.0048873, -0.0070921, -0.0176389, -0.0245479, -0.0266923, -0.0240538, -0.0172981, -0.0074557, 0.0040121, 0.0150708, 0.0233876, 0.0270866, 0.0254741, 0.0191698, 0.009622, -0.0014812, -0.0124289, -0.0214038, -0.026569, -0.0265973, -0.0213293, -0.0119776, -0.000641, 0.0104804, 0.0195794, 0.0252633, 0.0264636, 0.0226526, 0.0143049, 0.0031012, -0.008498, -0.0181097, -0.0241065, -0.025731, -0.0228417, -0.0157853, -0.0055852, 0.0058857, 0.016199, 0.0231434, 0.0254663, 0.0230703, 0.016668, 0.0073679, -0.0034149, -0.0138448, -0.0217998, -0.0254624, -0.0240332, -0.0180076, -0.0088214, 0.0017672, 0.0120435, 0.0203414, 0.0250465, 0.0249499, 0.0198313, 0.0107596, -0.000236, -0.0109125, -0.0194243, -0.0244701, -0.0251839, -0.0211941, -0.0129602, -0.0020173, 0.0092944, 0.0185879, 0.0241723, 0.0253077, 0.0219976, 0.0147489, 0.0046119, -0.0066661, -0.0168016, -0.0236114, -0.0257599, -0.0230914, -0.0163857, -0.0068934, 0.0039015, 0.0142431, 0.0221519, 0.0258645, 0.0244983, 0.0184331, 0.009094, -0.0016546, -0.0120003, -0.020304, -0.0250754, -0.0251852, -0.0203486, -0.0114832, -0.0005298, 0.0102228, 0.0188271, 0.023961, 0.0248479, 0.021205, 0.0134494, 0.0029472, -0.0081205, -0.0173916, -0.0230869, -0.024397, -0.0213597, -0.0145572, -0.0050224, 0.0056572, 0.0153971, 0.0220997, 0.0243654, 0.0219297, 0.015546, 0.0065185, -0.0036542, -0.0133358, -0.0207497, -0.0242674, -0.0229827, -0.0171553, -0.0081335, 0.0021931, 0.0119711, 0.0196278, 0.0238546, 0.0236921, 0.0188832, 0.0102363, -0.0004251, -0.0108298, -0.0189818, -0.0235691, -0.0239488, -0.020012, -0.0122448, -0.0019269, 0.008904, 0.0179474, 0.0233784, 0.0243391, 0.0209242, 0.0138584, 0.0042702, -0.0062997, -0.0159057, -0.0225406, -0.0247666, -0.0222219, -0.0156347, -0.0064, 0.0038736, 0.0135429, 0.020939, 0.0245373, 0.0234152, 0.0177131, 0.0086855, -0.0017523, -0.0116441, -0.0193658, -0.0236739, -0.0237131, -0.0192386, -0.0109403, -0.0004932, 0.0098819, 0.0181217, 0.022836, 0.023394, 0.019769, 0.0124762, 0.0026906, -0.0077316, -0.0166085, -0.0220951, -0.0232399, -0.0200908, -0.013411, -0.0043639, 0.0055835, 0.0146638, 0.0210288, 0.0232672, 0.020926, 0.0146408, 0.0057988, -0.0039544, -0.0130062, -0.0198314, -0.0230792, -0.0219002, -0.0163704, -0.0076307, 0.0024529, 0.0118977, 0.0190725, 0.0228303, 0.0224805, 0.0178882, 0.0097159, -0.0004793, -0.0105646, -0.0184739, -0.0227838, -0.0229017, -0.0189267, -0.0114932, -0.0017815, 0.0084579, 0.0171541, 0.0224813, 0.0234329, 0.0200291, 0.0130967, 0.0039041, -0.0060442, -0.0150528, -0.0213813, -0.0236508, -0.0213412, -0.0150063, -0.0060325, 0.0038555, 0.0129811, 0.0198468, 0.0231901, 0.022206, 0.0169084, 0.0083236, -0.0017426, -0.0112695, -0.0185545, -0.0224391, -0.0222949, -0.0180352, -0.0102732, -0.0004326, 0.0094682, 0.0173783, 0.0218129, 0.0221341, 0.0184624, 0.01147, 0.0022987, -0.0074366, -0.0158155, -0.0210732, -0.0221567, -0.0190109, -0.0124384, -0.0037265, 0.0056415, 0.014084, 0.0200169, 0.0221548, 0.0199535, 0.0138772, 0.0052539, -0.0041897, -0.0127767, -0.0190852, -0.021995, -0.020817, -0.0155737, -0.007208, 0.0025572, 0.0117191, 0.018561, 0.02196, 0.0214003, 0.0169182, 0.0091636, -0.000509, -0.0101811, -0.0178441, -0.0219924, -0.0219793, -0.0179878, -0.010794, -0.0015936, 0.0080471, 0.0163082, 0.0214816, 0.0224868, 0.0192133, 0.012464, 0.0035949, -0.0058465, -0.0142926, -0.0202401, -0.0224627, -0.0203877, -0.014392, -0.0057478, 0.0037911, 0.0124805, 0.0188775, 0.0219129, 0.0209557, 0.01601, 0.0079186, -0.0016967, -0.0108563, -0.017792, -0.0213357, -0.0209891, -0.0168363, -0.0095201, -0.0002931, 0.0090625, 0.0166136, 0.0208322, 0.0210093, 0.017305, 0.0105174, 0.0018395, -0.0072513, -0.0150839, -0.0200623, -0.0211123, -0.0180525, -0.011623, -0.0031717, 0.0057507, 0.0136352, 0.0191101, 0.0210773, 0.0190004, 0.0131941, 0.004845, -0.0043267, -0.0125691, -0.0184616, -0.0210327, -0.0197799, -0.0147741, -0.0068192, 0.002555, 0.0114279, 0.0180296, 0.0211841, 0.020447, 0.016011, 0.0086002, -0.0005456, -0.0097334, -0.0171088, -0.0211521, -0.0211207, -0.0171734, -0.0101726, -0.0013881, 0.0076913, 0.0154631, 0.0204115, 0.021476, 0.0184189, 0.0119263, 0.0033565, -0.0056778, -0.0136313, -0.0191649, -0.0212457, -0.0193545, -0.0137426, -0.005506, 0.003673, 0.0120066, 0.0180223, 0.020746, 0.0197188, 0.0150325, 0.0074342, -0.0016576, -0.0104053, -0.0170416, -0.0203402, -0.0198252, -0.0157042, -0.0087168, -5.34e-05, 0.0087121, 0.0158422, 0.0198592, 0.0199802, 0.0163063, 0.0096785, 0.0013672, -0.007177, -0.014459, -0.0190908, -0.0200793, -0.0171608, -0.0109475, -0.0027466, 0.0058416, 0.0132916, 0.0183414, 0.0200746, 0.0180557, 0.0125321, 0.004529, -0.0043396, -0.0123081, -0.0179073, -0.0202054, -0.0188375, -0.0139854, -0.0064161, 0.0025097, 0.011031, 0.017415, 0.0204302, 0.0196079, 0.0152129, 0.0080636, -0.0006166, -0.0092949, -0.0163106, -0.0202274, -0.0202473, -0.0164405, -0.0096565, -0.0012169, 0.007389, 0.014684, 0.0193398, 0.0204006, 0.0175765, 0.0114162, 0.0031896, -0.0054836, -0.0130357, -0.0181949, -0.0200784, -0.0182791, -0.0130157, -0.0052328, 0.0035285, 0.011516, 0.0172253, 0.0196946, 0.0185673, 0.0140448, 0.0068679, -0.0016897, -0.0099682, -0.0162849, -0.0193912, -0.0187765, -0.0146878, -0.0079457, 0.0002513, 0.0084571, 0.0151274, 0.0189055, 0.0189946, 0.0154138, 0.0089667, 0.0009563, -0.0071505, -0.0139469, -0.0182152, -0.0190915, -0.0163033, -0.0103478, -0.0024304, 0.005862, 0.0129756, 0.0176804, 0.0191818, 0.0171653, 0.0118856, 0.0042487, -0.0042733, -0.0119716, -0.017349, -0.0194573, -0.0179974, -0.0132535, -0.0060169, 0.0024601, 0.010585, 0.016728, 0.0196466, 0.0188177, 0.0145051, 0.0075864, -0.0006902, -0.0088831, -0.0155053, -0.0192541, -0.0193352, -0.0157278, -0.0092068, -0.0010927, 0.0071063, 0.0139686, 0.0183097, 0.019308, 0.0166875, 0.0108875, 0.0030466, -0.0052686, -0.0124755, -0.0173118, -0.0189863, -0.0172107, -0.0122354, -0.0049043, 0.0034002, 0.0110245, 0.0164646, 0.0187337, 0.0175123, 0.0130918, 0.0062583, -0.0017948, -0.0095768, -0.0155447, -0.0184776, -0.0178171, -0.0137854, -0.0072417, 0.0005778, 0.0082837, 0.0144894, 0.0179942, 0.0180478, 0.0146011, 0.008369, 0.0006295, -0.007129, -0.0135216, -0.0174426, -0.0181676, -0.0154784, -0.0097979, -0.0022018, 0.0057994, 0.0126462, 0.0170921, 0.0183942, 0.0163441, 0.0112573, 0.0039811, -0.0041552, -0.0115584, -0.0167516, -0.018749, -0.0172474, -0.0125923, -0.0056307, 0.0024252, 0.0101239, 0.0159822, 0.0188092, 0.018036, 0.0138676, 0.0071791, -0.000752, -0.0085108, -0.0147316, -0.0182611, -0.0183736, -0.0149957, -0.0087932, -0.0010191, 0.0068225, 0.013317, 0.0173558, 0.0182382, 0.0157589, 0.0103077, 0.0028912, -0.0050367, -0.011925, -0.0164992, -0.0179923, -0.0161964, -0.0114293, -0.0045042, 0.0033258, 0.0105468, 0.015717, 0.017834, 0.0165597, 0.0122198, 0.0056512, -0.001968, -0.0092644, -0.0148478, -0.0175868, -0.0169104, -0.0129828, -0.0066384, 0.0008712, 0.0081657, 0.0139487, 0.0171595, 0.0171418, 0.0138315, 0.007851, 0.0003979, -0.0070601, -0.0131367, -0.0167716, -0.0173391, -0.0147021, -0.0092705, -0.0020154, 0.0056667, 0.0122637, 0.0165184, 0.0176872, 0.0156132, 0.0106753, 0.0037173, -0.0040293, -0.011102, -0.0160981, -0.018025, -0.0165454, -0.0120068, -0.00529, 0.0023946, 0.0096819, 0.0152251, 0.0179288, 0.0172235, 0.0132489, 0.0068263, -0.0007769, -0.0081545, -0.0140064, -0.0172974, -0.0173961, -0.0142269, -0.0083658, -0.0009631, 0.0065265, 0.0126984, 0.0164737, 0.0172292, 0.0148362, 0.0096848, 0.0026873, -0.0048283, -0.0113882, -0.0157276, -0.0170752, -0.0152569, -0.0106461, -0.0040641, 0.0033181, 0.01012, 0.0150001, 0.0169756, 0.0156811, 0.0114319, 0.0050884, -0.0021648, -0.0090214, -0.014221, -0.0167449, -0.0160502, -0.012251, -0.006122, 0.0011063, 0.0080571, 0.0134785, 0.0164113, 0.016301, 0.0131084, 0.0073878, 0.0002371, -0.0069356, -0.0127528, -0.0161638, -0.0165977, -0.013991, -0.0087755, -0.0018548, 0.0054905, 0.0118331, 0.0159304, 0.0170199, 0.0149518, 0.0101472, 0.0034702, -0.0039028, -0.0106269, -0.0154046, -0.0172721, -0.0158571, -0.0114704, -0.0049932, 0.0023572, 0.0092608, 0.0144789, 0.0170287, 0.0163801, 0.0126239, 0.0065001, -0.000774, -0.007807, -0.013328, -0.0163807, -0.016428, -0.0134314, -0.0079089, -0.0008997, 0.0062306, 0.0121074, 0.0156577, 0.0162925, 0.0139449, 0.0090369, 0.0024308, -0.0046619, -0.0108762, -0.014991, -0.0162253, -0.0143987, -0.0099114, -0.0036099, 0.0033704, 0.009756, 0.0143245, 0.0161541, 0.0148639, 0.0107284, 0.0045924, -0.0023547, -0.0088331, -0.0136706, -0.0159631, -0.015233, -0.0115731, -0.0056831, 0.0012683, 0.0079267, 0.0130554, 0.0157487, 0.0155357, 0.0124306, 0.00696, 0.0001277, -0.0067514, -0.0123402, -0.0155879, -0.0159328, -0.0133528, -0.0083165, -0.001704, 0.0052962, 0.0113623, 0.0153061, 0.0163572, 0.0143405, 0.0096779, 0.0032506, -0.0037836, -0.010161, -0.0146923, -0.016482, -0.0151505, -0.0109566, -0.004739, 0.0022992, 0.0088593, 0.0137683, 0.0161409, 0.0155146, 0.0119696, 0.0061687, -0.0007546, -0.0074556, -0.0126854, -0.0155256, -0.0155039, -0.0126326, -0.0074135, -0.000798, 0.0059544, 0.0115353, 0.014888, 0.0154281, 0.0131142, 0.0083989, 0.0021309, -0.0045581, -0.0104126, -0.0142889, -0.0154209, -0.0136088, -0.0092441, -0.0031811, 0.003456, 0.0094603, 0.0137141, 0.0153786, 0.0140911, 0.0100873, 0.004167, -0.0025036, -0.0086619, -0.0131866, -0.0152588, -0.0144756, -0.0109404, -0.0052956, 0.0013698, 0.0077536, 0.0126399, 0.0151461, 0.0148503, 0.011815, 0.0065677, 4.61e-05, -0.0065306, -0.0118934, -0.0150087, -0.0153081, -0.0127777, -0.0079069, -0.0015719, 0.0050983, 0.0108778, 0.0146548, 0.0156767, 0.0137424, 0.0092469, 0.0030642, -0.0036585, -0.0097086, -0.0139864, -0.0156765, -0.0144205, -0.0104361, -0.0045006, 0.0022247, 0.0084658, 0.0130907, 0.0152848, 0.0146545, 0.0112953, 0.005814, -0.0007464, -0.0071131, -0.0120715, -0.0147235, -0.0146337, -0.0118563, -0.006899, -0.0006542, 0.0057189, 0.010998, 0.0141618, 0.0146247, 0.0123458, 0.0077924, 0.0018139, -0.0045067, -0.0100075, -0.0136353, -0.0146631, -0.0128773, -0.0086404, -0.0027936, 0.0035474, 0.0092138, 0.0131683, 0.01466, 0.0133668, 0.0095011, 0.0038046, -0.0026034, -0.0084846, -0.0127457, -0.0146235, -0.0137824, -0.0103562, -0.0049522, 0.0014217, 0.007537, 0.0122136, 0.0145791, 0.0142293, 0.0112596, 0.0062119, -1.44e-05, -0.0062855, -0.0114166, -0.0144132, -0.0147005, -0.0122468, -0.0075407, -0.0014588, 0.0049024, 0.0103928, 0.0139858, 0.0149728, 0.013139, 0.0088359, 0.0029041, -0.0035269, -0.0092741, -0.0133011, -0.014871, -0.0136699, -0.009896, -0.0042608, 0.0021422, 0.0080796, 0.0124478, 0.0144739, 0.0138193, 0.0106113, 0.0054293, -0.000765, -0.0067892, -0.0114844, -0.0139716, -0.013826, -0.0111229, -0.0063829, -0.0004699, 0.0055356, 0.0105045, 0.0134759, 0.0138737, 0.0116413, 0.0072356, 0.0015037, -0.0044957, -0.0096657, -0.0130391, -0.0139505, -0.0121931, -0.0080953, -0.0024606, 0.003618, 0.0089958, 0.0126852, 0.0140067, 0.0126935, 0.0089583, 0.0034924, -0.0026507, -0.0082784, -0.0123218, -0.0140463, -0.0131583, -0.0098252, -0.0046432, 0.0014421, 0.0072845, 0.0117614, 0.0140189, 0.0136523, 0.0107625, 0.0058986, -5.84e-05, -0.0060343, -0.0109257, -0.0137969, -0.0140848, -0.0117346, -0.0072093, -0.0013709, 0.0047054, 0.0099205, 0.0133201, 0.0142535, 0.0125152, 0.0084195, 0.0027556, -0.0033855, -0.0088502, -0.0126438, -0.0140879, -0.0129178, -0.0093344, -0.0039986, 0.0020713, 0.0077039, 0.0118315, 0.0137078, 0.0130266, 0.0099421, 0.0050249, -0.0008224, -0.0065037, -0.0109322, -0.013262, -0.013073, -0.0104411, -0.0058894, -0.0002658, 0.0054032, 0.010069, 0.0128407, 0.0131706, 0.0109893, 0.0067297, 0.0012207, -0.0044982, -0.0093734, -0.0125058, -0.0132942, -0.0115565, -0.007598, -0.0021762, 0.0036554, 0.0087798, 0.0122429, 0.0134151, 0.0120792, 0.008463, 0.0032219, -0.0026568, -0.0080405, -0.0118946, -0.0135021, -0.0125903, -0.0093489, -0.0043726, 0.0014372, 0.0070093, 0.0112886, 0.013453, 0.0130951, 0.0103041, 0.0056224, -8.29e-05, -0.0057794, -0.0104319, -0.0131698, -0.0134568, -0.0112221, -0.0068923, -0.001299, 0.0045066, 0.009461, 0.0126669, 0.0135322, 0.0118758, 0.0079877, 0.0026015, -0.0032462, -0.0084391, -0.0120144, -0.0133349, -0.0121788, -0.0087622, -0.0037124, 0.0020243, 0.0073504, 0.011246, 0.0129865, 0.0122815, 0.0093008, 0.0046146, -0.0009136, -0.0062625, -0.0104237, -0.012598, -0.0123732, -0.0098132, -0.0054299, -5.81e-05, 0.0053103, 0.0096899, 0.0122606, 0.0125166, 0.0103873, 0.0062746, 0.0009747, -0.0044968, -0.0091149, -0.0120286, -0.0126947, -0.0109679, -0.0071449, -0.0019377, 0.0036538, 0.0085504, 0.011824, 0.012875, 0.0115217, 0.0080136, 0.002987, -0.0026289, -0.0077709, -0.011452, -0.012973, -0.0120649, -0.008922, -0.0041378, 0.0014139, 0.0067216, 0.0108002, 0.0128737, 0.0125405, 0.0098685, 0.0053759, -9e-05, -0.0055258, -0.0099465, -0.0125415, -0.0128156, -0.0106964, -0.0065742, -0.0012347, 0.0043067, 0.0090165, 0.012037, 0.0128235, 0.0112282, 0.0075346, 0.0024283, -0.0031184, -0.0080417, -0.0114125, -0.01262, -0.0114695, -0.008194, -0.0034037, 0.0020115, 0.0070302, 0.0106933, 0.0123067, 0.0115862, 0.0087013, 0.0042169, -0.0010289, -0.0060695, -0.0099679, -0.0119824, -0.011721, -0.0092353, -0.005013, 0.0001336, 0.0052391, 0.0093623, 0.0117403, 0.0119154, 0.0098302, 0.0058626, 0.0007681, -0.0044743, -0.008868, -0.0115947, -0.0121527, -0.0104318, -0.0067335, -0.0017358, 0.0036174, 0.0082974, 0.0114075, 0.0123677, 0.0110155, 0.0076132, 0.0027875, -0.0025765, -0.00748, -0.0109924, -0.0124421, -0.0115606, -0.008532, -0.0039376, 0.0013716, 0.0064287, 0.0103089, 0.0122865, 0.0119784, 0.0094354, 0.0051427, -8.44e-05, -0.0052718, -0.0094723, -0.0119239, -0.0121725, -0.010157, -0.0062408, -0.0011613, 0.0041134, 0.0085859, 0.0114304, 0.0121369, 0.0105876, 0.0070688, 0.0022305, -0.0030161, -0.0076687, -0.0108397, -0.0119418, -0.010796, -0.0076444, -0.0030866, 0.0020303, 0.0067517, 0.0101838, 0.0116714, 0.0109384, 0.0081453, 0.0038432, -0.0011517, -0.0059141, -0.0095657, -0.0114217, -0.0111196, -0.0087049, -0.0046375, 0.0003011, 0.0051731, 0.00907, 0.0112733, 0.011369, 0.009321, 0.0054924, 0.0005984, -0.0044261, -0.0086185, -0.0111864, -0.0116562, -0.0099453, -0.0063648, -0.0015687, 0.0035505, 0.0080208, 0.0109839, 0.0118778, 0.0105461, 0.0072544, 0.0026207, -0.0025028, -0.0071734, -0.0105195, -0.0119047, -0.0110641, -0.0081637, -0.0037618, 0.001315, 0.0061343, 0.0098203, 0.0116972, 0.0114083, 0.0089959, 0.0049095, -7.64e-05, -0.0050225, -0.0090133, -0.0113232, -0.0115356, -0.0096083, -0.0058893, -0.0010703, 0.0039353, 0.0081744, 0.0108509, 0.0114797, 0.0099645, 0.0065988, 0.0020102, -0.0029442, -0.0073267, -0.0103003, -0.0113033, -0.0101641, -0.0071242, -0.0027726, 0.0020741, 0.0065156, 0.0097215, 0.0110827, 0.010338, 0.0076355, 0.0035028, -0.0012687, -0.0057852, -0.0092126, -0.0109161, -0.0105684, -0.0082194, -0.0043027, 0.0004376, 0.005098, 0.0087982, 0.0108503, 0.0108746, 0.0088581, 0.0051603, 0.0004622, -0.0043497, -0.0083547, -0.0107878, -0.0111934, -0.0095038, -0.0060371, -0.0014323, 0.0034599, 0.0077233, 0.0105468, 0.0113904, 0.0100993, 0.0069294, 0.0024838, -0.002412, -0.0068597, -0.0100406, -0.0113599, -0.010564, -0.0078019, -0.0035999, 0.0012478, 0.0058413, 0.0093422, 0.0111158, 0.0108352, 0.0085443, 0.0046638, -7.7e-05, -0.0047822, -0.0085701, -0.0107445, -0.010916, -0.0090609, -0.0055215, -0.0009538, 0.0037827, 0.007787, 0.0102988, 0.0108541, 0.0093687, 0.006139, 0.0017762, -0.0029043, -0.0070238, -0.0098004, -0.0107049, -0.0095736, -0.0066394, -0.0024751, 0.0021296, 0.0063169, 0.0093098, 0.0105456, 0.0097853, 0.0071689, 0.0031973, -0.001369, -0.005667, -0.0088968, -0.0104628, -0.0100701, -0.0077791, -0.0040054, 0.0005445, 0.0050062, 0.008531, 0.0104557, 0.010424, 0.0084412, 0.0048668, 0.0003564, -0.0042489, -0.0080743, -0.0103873, -0.0107481, -0.0090953, -0.0057462, -0.0013261, 0.0033486, 0.0074111, 0.0100988, 0.0108997, 0.0096611, 0.0066239, 0.0023687, -0.0023068, -0.0065416, -0.0095621, -0.0108134, -0.0100592, -0.0074364, -0.003438, 0.0011804, 0.0055537, 0.0088766, 0.0105473, 0.0102662, 0.0080845, 0.0044017, -9.61e-05, -0.0045604, -0.0081478, -0.0101899, -0.0103184, -0.0085235, -0.0051459, -0.0008144, 0.0036604, 0.0074314, 0.0097799, 0.0102634, 0.0088047, 0.0056977, 0.0015391, -0.0028894, -0.00676, -0.0093446, -0.0101511, -0.0090268, -0.0061932, -0.0022012, 0.0021861, 0.0061456, 0.0089434, 0.0100602, 0.0092818, 0.0067461, 0.0029281, -0.0014468, -0.0055485, -0.008606, -0.0100524, -0.0096202, -0.0073828, -0.0037446, 0.0006218, 0.0048941, 0.0082591, 0.0100768, 0.0100062, 0.0080636, 0.0046089, 0.0002785, -0.0041256, -0.0077765, -0.0099787, -0.0103097, -0.0087085, -0.0054839, -0.0012447, 0.003221, 0.007089, 0.0096426, 0.0104026, 0.0092227, 0.0063268, 0.002266, -0.0021937, -0.0062248, -0.0090902, -0.0102706, -0.0095507, -0.007062, -0.0032671, 0.001121, 0.0052773, 0.0084281, 0.0099981, 0.0097091, 0.0076216, 0.0041224, -0.0001392, -0.0043634, -0.0077509, -0.009663, -0.0097483, -0.0080051, -0.0047717, -0.0006567, 0.0035697, 0.0071123, 0.0092976, 0.0097096, 0.0082763, 0.0052835, 0.0013099, -0.0028911, -0.0065327, -0.0089347, -0.0096442, -0.0085238, -0.0057862, -0.0019568, 0.0022323, 0.0059897, 0.0086154, 0.0096244, 0.0088272, 0.0063655, 0.0026934, -0.0014999, -0.0054201, -0.0083261, -0.0096736, -0.0092132, -0.0070287, -0.0035181, 0.0006728, 0.0047623, 0.0079761, 0.0097007, 0.0096079, 0.0077169, 0.0043833, 0.0002264, -0.0039846, -0.0074655, -0.0095605, -0.0098689, -0.0083305, -0.0052396, -0.0011828, 0.0030804, 0.0067618, 0.009185, 0.0099026, 0.00878, 0.0060273, 0.0021642, -0.00208, -0.0059124, -0.0086288, -0.0097382, -0.0090448, -0.0066794, -0.0030805, 0.0010798, 0.0050194, 0.008, 0.0094708, 0.0091699, 0.0071646, 0.0038322, -0.0002069, -0.0041974, -0.0073857, -0.0091673, -0.0092082, -0.0075111, -0.0044085, -0.0004907, 0.0035058, 0.0068311, 0.0088568, 0.0091964, 0.0077857, 0.0048999, 0.0010968, -0.002898, -0.006333, -0.0085679, -0.0091856, -0.0080669, -0.0054193, -0.001743, 0.0022627, 0.0058385, 0.0083138, 0.0092303, 0.0084193, 0.0060273, 0.0024927, -0.001528, -0.0052783, -0.0080481, -0.0093135, -0.0088381, -0.0067109, -0.0033243, 0.0006984, 0.0046119, 0.0076809, 0.0093215, 0.0092182, 0.0073897, 0.0041817, 0.0001957, -0.0038279, -0.0071441, -0.0091345, -0.0094235, -0.0079536, -0.0050027, -0.0011309, 0.002933, 0.0064338, 0.0087298, 0.0094035, 0.0083338, 0.0057211, 0.002055, -0.0019746, -0.0056118, -0.0081828, -0.009221, -0.0085465, -0.0062922, -0.0028778, 0.001061, 0.0047865, 0.0075982, 0.00897, 0.0086536, 0.0067197, 0.0035379, -0.000295, -0.0040625, -0.0070558, -0.0087069, -0.008702, -0.0070465, -0.0040634, -0.0003247, 0.0034617, 0.006585, 0.008458, 0.0087259, 0.0073349, 0.0045503, 0.000906, -0.0029014, -0.0061519, -0.008238, -0.0087725, -0.0076554, -0.005092, -0.0015608, 0.0022728, 0.0056834, 0.008028, 0.0088689, 0.0080519, 0.005728, 0.0023239, -0.0015322, -0.0051208, -0.0077647, -0.0089618, -0.0084844, -0.0064218, -0.0031582, 0.0007023, 0.0044456, 0.007374, 0.0089351, 0.0088289, 0.0070721, 0.0039964, 0.0001812, -0.0036606, -0.006817, -0.0087048, -0.0089736, -0.0075725, -0.0047646, -0.0010805, 0.0027851, 0.0061102, 0.008283, 0.0089113, 0.0078874, 0.0054063, 0.0019318, -0.0018851, -0.0053285, -0.0077564, -0.0087236, -0.0080626, -0.0059068, -0.0026621, 0.001067, 0.0045837, 0.0072269, 0.0084988, 0.0081639, 0.0062935, 0.0032481, -0.0003966, -0.0039566, -0.006763, -0.0082853, -0.0082322, -0.0066139, -0.003742, -0.0001681, 0.0034277, 0.0063676, 0.0081002, 0.0082995, 0.0069255, 0.0042357, 0.0007406, -0.0028939, -0.0059791, -0.0079353, -0.0083997, -0.0072875, -0.0048041, -0.0014097, 0.0022619, 0.0055195, 0.0077481, 0.0085287, 0.0077165, 0.0054634, 0.0021849, -0.0015143, -0.0049483, -0.0074734, -0.0086105, -0.0081413, -0.0061516, -0.003014, 0.0006872, 0.004266, 0.0070583, 0.0085422, 0.0084358, 0.0067556, 0.0038171, 0.000175, -0.003487, -0.006488, -0.0082755, -0.0085227, -0.0071871, -0.0045199, -0.0010229, 0.0026449, 0.0057964, 0.0078485, 0.0084304, 0.0074458, 0.0050861, 0.0017934, -0.0018171, -0.0050695, -0.0073549, -0.00825, -0.0075972, -0.0055293, -0.002439, 0.0010947, 0.0044122, 0.0068904, 0.0080616, 0.0077045, 0.0058907, 0.0029691, -0.0005037, -0.0038728, -0.0065047, -0.0079033, -0.0078019, -0.0062164, -0.0034481, -2.65e-05, 0.0033958, 0.0061706, 0.0077771, 0.007915, 0.0065576, 0.0039574, 0.000603, -0.0028713, -0.005807, -0.0076509, -0.0080589, -0.0069578, -0.0045527, -0.0012884, 0.0022295, 0.0053436, 0.0074677, 0.0082005, 0.0074035, 0.0052255, 0.0020713, -0.0014765, -0.0047618, -0.0071729, -0.0082555, -0.0078011, -0.0058912, -0.0028833, 0.0006593, 0.0040772, 0.0067366, 0.0081445, 0.008038, 0.0064354, 0.0036366, 0.0001694, -0.0033138, -0.0061624, -0.0078512, -0.0080748, -0.0067991, -0.0042666, -0.0009531, 0.0025193, 0.0054989, 0.0074319, 0.007966, 0.0070143, 0.0047644, 0.0016409, -0.0017728, -0.0048387, -0.0069827, -0.0078045, -0.0071554, -0.0051655, -0.0022153, 0.0011394, 0.0042701, 0.0065899, 0.0076607, 0.0072784, 0.0055152, 0.0027077, -0.0006082, -0.0038037, -0.006276, -0.0075598, -0.0074112, -0.0058554, -0.0031843, 9.49e-05, 0.003358, 0.0059853, 0.0074819, 0.0075685, 0.0062295, 0.0037147, 0.000493, -0.0028305, -0.0056293, -0.0073754, -0.0077411, -0.0066596, -0.0043337, -0.0011947, 0.002177, 0.0051552, 0.0071825, 0.0078762, 0.0071031, 0.0050067, 0.0019774, -0.0014226, -0.0045643, -0.0068653, -0.0078954, -0.0074583, -0.0056322, -0.0027583, 0.0006245, 0.0038835, 0.0064129, 0.0077459, 0.0076374, 0.0061095, 0.0034491, 0.0001568, -0.0031477, -0.0058453, -0.0074368, -0.0076348, -0.0064129, -0.0040063, -0.0008681, 0.0024135, 0.0052235, 0.0070375, 0.0075225, 0.0065979, 0.0044472, 0.0014789, -0.0017508, -0.0046386, -0.0066439, -0.0073906, -0.0067405, -0.0048202, -0.0019974, 0.0011936, 0.0041525, 0.0063237, 0.0072979, 0.0068883, 0.0051699, 0.0024676, -0.0007037, -0.0037411, -0.0060695, -0.00725, -0.0070596, -0.0055319, -0.0029521, 0.0001937, 0.0033095, 0.0058039, 0.0072057, 0.0072526, 0.0059373, 0.0035058, 0.0004107, -0.002771, -0.0054431, -0.0071025, -0.0074369, -0.0063839, -0.0041409, -0.0011247, 0.0021064, 0.0049549, 0.006891, 0.007551, 0.0068077, 0.0047977, 0.0018958, -0.0013576, -0.0043586, -0.0065521, -0.0075308, -0.0071113, -0.0053699, -0.0026312, 0.0005904, 0.0036907, 0.0060916, 0.0073497, 0.0072368, 0.0057793, 0.0032526, 0.0001322, -0.0029951, -0.0055429, -0.0070372, -0.0072073, -0.0060327, -0.0037421, -0.0007688, 0.0023301, 0.0049747, 0.0066707, 0.0071043, 0.006201, 0.0041394, 0.0013126, -0.0017474, -0.0044677, -0.0063397, -0.0070114, -0.006356, -0.004498, -0.0017911, 0.001251]}
\ No newline at end of file
packages/core/test/fixtures/rr_off.jsonadded+177−0View file
@@ -0,0 +1,177 @@
1+{
2+ "model": "free-edge Kirchhoff-Love Rayleigh-Ritz plate; Hertz impact; compact acoustic radiation",
3+ "plate": {
4+ "width": 0.03,
5+ "height": 0.03,
6+ "thickness": 0.001,
7+ "density": 2700.0,
8+ "young": 68900000000.0,
9+ "poisson": 0.33,
10+ "material_q": 1400.0,
11+ "hole_diameter": 0.0008,
12+ "hole_edge_offset": 0.0015
13+ },
14+ "hammer": {
15+ "mass": 0.01,
16+ "tip_radius": 0.002,
17+ "speed": 0.45,
18+ "young": 200000000000.0,
19+ "poisson": 0.29,
20+ "restitution": 0.62
21+ },
22+ "suspension": {
23+ "thread_length": 0.2,
24+ "dashpot": 0.00075
25+ },
26+ "render": {
27+ "sample_rate": 96000,
28+ "duration": 4.0,
29+ "modes": 72,
30+ "basis_order": 12,
31+ "quadrature_order": 30,
32+ "hit_x": 0.006,
33+ "hit_y": -0.004,
34+ "mic_distance": 0.35,
35+ "air_density": 1.204,
36+ "sound_speed": 343.0,
37+ "normalize_dbfs": -1.0
38+ },
39+ "derived": {
40+ "plate_mass_kg": 0.002428642831973649,
41+ "bending_rigidity_Nm": 6.443347173904911,
42+ "contact_duration_s": 9.437140797643829e-05,
43+ "impact_impulse_Ns": 0.007290000000000001,
44+ "estimated_peak_contact_force_N": 138.83085722430667,
45+ "critical_frequency_hz": 12120.88938013311,
46+ "unnormalized_peak_pressure_pa_approx": 0.01825497961817797,
47+ "retained_mode_count": 18
48+ },
49+ "modes": [
50+ {
51+ "mode": 1,
52+ "frequency_hz": 3604.9596964856946,
53+ "q_total": 409.77691559682415,
54+ "hit_participation": 7.28087668849117,
55+ "radiation_participation": -1.244794241894155e-15
56+ },
57+ {
58+ "mode": 2,
59+ "frequency_hz": 5266.636133078648,
60+ "q_total": 352.58511359635384,
61+ "hit_participation": -4.9360091883056905,
62+ "radiation_participation": 1.992927445782186e-05
63+ },
64+ {
65+ "mode": 3,
66+ "frequency_hz": 6668.263257152294,
67+ "q_total": 326.58709461223276,
68+ "hit_participation": -20.430866989707983,
69+ "radiation_participation": 1.4105340499224371e-06
70+ },
71+ {
72+ "mode": 4,
73+ "frequency_hz": 9366.202787631966,
74+ "q_total": 286.0010631952906,
75+ "hit_participation": -11.661504329933141,
76+ "radiation_participation": -1.2469848988038964e-14
77+ },
78+ {
79+ "mode": 5,
80+ "frequency_hz": 9376.34030979602,
81+ "q_total": 284.22231555829495,
82+ "hit_participation": 3.6471620927482626,
83+ "radiation_participation": -2.0110600388817583e-05
84+ },
85+ {
86+ "mode": 6,
87+ "frequency_hz": 16649.866412026182,
88+ "q_total": 226.0519036556788,
89+ "hit_participation": 27.391489163298825,
90+ "radiation_participation": -1.6376937619597606e-14
91+ },
92+ {
93+ "mode": 7,
94+ "frequency_hz": 16653.389527568852,
95+ "q_total": 225.92114832465757,
96+ "hit_participation": -23.48054472255674,
97+ "radiation_participation": -8.741810799173736e-06
98+ },
99+ {
100+ "mode": 8,
101+ "frequency_hz": 17186.690074496233,
102+ "q_total": 222.49241835759418,
103+ "hit_participation": -9.894639052400358,
104+ "radiation_participation": 2.040628807792887e-05
105+ },
106+ {
107+ "mode": 9,
108+ "frequency_hz": 18640.51955827077,
109+ "q_total": 215.55182577190828,
110+ "hit_participation": 2.8439176438108427,
111+ "radiation_participation": 1.455904212866501e-16
112+ },
113+ {
114+ "mode": 10,
115+ "frequency_hz": 21032.380680531944,
116+ "q_total": 204.772249224193,
117+ "hit_participation": 23.457303258457074,
118+ "radiation_participation": 2.6798947239031933e-17
119+ },
120+ {
121+ "mode": 11,
122+ "frequency_hz": 28498.130528923146,
123+ "q_total": 179.6188655807077,
124+ "hit_participation": -25.21825057823593,
125+ "radiation_participation": -1.7138782347340778e-15
126+ },
127+ {
128+ "mode": 12,
129+ "frequency_hz": 28529.915562837206,
130+ "q_total": 179.31059579926938,
131+ "hit_participation": -13.839484205341698,
132+ "radiation_participation": -2.036212334621056e-05
133+ },
134+ {
135+ "mode": 13,
136+ "frequency_hz": 31826.28578815292,
137+ "q_total": 171.12813372083784,
138+ "hit_participation": 12.280367336726401,
139+ "radiation_participation": -6.725749902454069e-06
140+ },
141+ {
142+ "mode": 14,
143+ "frequency_hz": 33486.01926408711,
144+ "q_total": 167.25840139258062,
145+ "hit_participation": -9.486981651020756,
146+ "radiation_participation": 1.6500481331107334e-05
147+ },
148+ {
149+ "mode": 15,
150+ "frequency_hz": 35733.8715922345,
151+ "q_total": 162.63778906596605,
152+ "hit_participation": 4.138129148550091,
153+ "radiation_participation": -1.1577883183459675e-15
154+ },
155+ {
156+ "mode": 16,
157+ "frequency_hz": 35769.727749465426,
158+ "q_total": 162.43284557542367,
159+ "hit_participation": 8.742886935299195,
160+ "radiation_participation": 1.951905250464122e-05
161+ },
162+ {
163+ "mode": 17,
164+ "frequency_hz": 41352.50867671962,
165+ "q_total": 152.43256220897143,
166+ "hit_participation": -32.428321724595094,
167+ "radiation_participation": 6.913228805605423e-17
168+ },
169+ {
170+ "mode": 18,
171+ "frequency_hz": 43780.89637646587,
172+ "q_total": 148.47296961212075,
173+ "hit_participation": 13.905315930936016,
174+ "radiation_participation": 2.308969641442734e-05
175+ }
176+ ]
177+}
packages/core/test/fixtures/rr_off_wav.jsonadded+1−0View file
@@ -0,0 +1 @@
1+{"fs": 96000, "samples": [0.0, 0.0671395, -0.2994926, -0.3649469, -0.3915505, -0.0375983, 0.0312076, 0.3763081, 0.8404696, 0.8152946, 0.2538702, 0.0155445, -0.1673931, -0.6182047, -0.0911021, -0.2325933, -0.0170165, -0.0479056, -0.161686, -0.2906556, -0.0063939, -0.3814716, -0.2281287, -0.1104043, -0.3926672, 0.4239432, 0.5895464, 0.7624154, 0.6686486, 0.3611198, -0.1930621, -0.1130842, -0.3605357, -0.3080907, -0.3900331, -0.596767, -0.046108, 0.0245365, 0.4079072, 0.3407175, -0.0297016, -0.514632, -0.2858113, -0.2334117, 0.0961608, 0.2949231, 0.1796706, 0.3481621, 0.4323459, 0.777996, 0.4120558, 0.0580823, -0.6266857, -0.7908532, -0.6155229, -0.255399, -0.0708432, -0.0462103, -0.2035101, -0.1167063, 0.4624945, 0.1843723, 0.3000517, -0.0346671, -0.3493009, 0.083652, 0.6769398, 0.6265531, 0.6059187, 0.1392982, -0.4643661, -0.2269075, -0.4562144, -0.4686063, -0.7472094, -0.891251, -0.5122778, 0.2889231, 0.6854756, 0.8870065, 0.5880187, 0.0528809, 0.1615919, 0.1725599, 0.2613871, -0.1014083, -0.284264, -0.4009476, -0.0821678, 0.2834784, 0.2705125, -0.2090659, -0.6595279, -0.6615387, -0.4527125, 0.1130577, 0.1786641, 0.2559949, 0.1164277, 0.3944391, 0.7504773, 0.6439708, 0.2456703, -0.2674913, -0.6752697, -0.5130436, 0.0634832, 0.0427573, 0.1605373, -0.1412031, -0.204675, 0.0696127, 0.0785328, -0.1110971, -0.3529875, -0.658487, -0.3785145, 0.3935839, 0.55859, 0.8015066, 0.5381931, 0.1728863, 0.1465925, 0.205454, -0.0723685, -0.4045114, -0.6440701, -0.6913491, -0.2459938, 0.0952376, 0.2663976, -0.035193, -0.191742, -0.263181, -0.0100386, 0.2250409, 0.2234791, 0.25381, 0.2551977, 0.4327826, 0.6580333, 0.5969159, -0.0020573, -0.3604095, -0.811972, -0.8388719, -0.5256965, -0.3844245, -0.2266574, -0.0482493, 0.2010644, 0.4992263, 0.6560598, 0.4124034, 0.242665, -0.1648519, -0.1444426, 0.0944518, 0.11549, 0.1785256, 0.1137515, -0.0837579, -0.1348463, -0.1195115, -0.3676706, -0.4169314, -0.5528098, -0.3911764, -0.0424407, 0.2764215, 0.5128716, 0.5439031, 0.394053, 0.1940462, 0.1329703, -0.0742569, -0.1543812, -0.19008, -0.1353411, -0.0975698, 0.1599441, 0.2167672, 0.0399672, -0.0365853, -0.3379401, -0.5248855, -0.4568886, -0.3103147, -0.1381664, 0.2238486, 0.3332303, 0.5369255, 0.6308593, 0.4841033, 0.385605, 0.0419325, -0.2905813, -0.3794911, -0.352179, -0.2947294, -0.0549594, -0.1072644, -0.1581724, -0.1727375, -0.1818857, -0.1055092, -0.1105518, -0.0614408, 0.0336883, 0.2586947, 0.5462967, 0.7939995, 0.6300501, 0.3369058, -0.0482247, -0.3682437, -0.4905817, -0.5360275, -0.5598894, -0.5879894, -0.3403511, -0.0050306, 0.3367223, 0.4782438, 0.3631815, 0.0602822, -0.032645, 0.0164714, 0.1499639, 0.2660514, 0.1115388, 0.0943222, 0.1217529, 0.1403307, 0.1434564, -0.0975424, -0.5757881, -0.6641965, -0.5304189, -0.2545293, 0.1668931, 0.2334686, 0.181025, 0.2219355, 0.3176161, 0.3480893, 0.2528811, -0.0908766, -0.2831112, -0.2213913, 0.0631356, 0.3830501, 0.3613263, 0.1066831, -0.1802588, -0.2746471, -0.2995605, -0.3023908, -0.4293193, -0.5384988, -0.3941081, 0.084222, 0.540369, 0.7152075, 0.6132609, 0.2629638, 0.0890033, 0.0776236, 0.1006134, -0.0282805, -0.2630812, -0.3791966, -0.2323615, -0.0365474, 0.0749674, -0.0720495, -0.45801, -0.5245221, -0.3374614, 3.67e-05, 0.3273098, 0.4192825, 0.3818686, 0.4902043, 0.5791441, 0.5578132, 0.284884, -0.2830037, -0.6356694, -0.698767, -0.4994568, -0.191303, -0.047105, -0.1098043, -0.0449207, 0.1194422, 0.2401787, 0.2538342, 0.0652239, -0.1420008, -0.135883, 0.1665524, 0.4158086, 0.4833519, 0.3099499, 0.0203521, -0.1508362, -0.1880697, -0.2501413, -0.3677961, -0.4871512, -0.4708833, -0.1381452, 0.1810932, 0.3740461, 0.3479679, 0.1036748, -0.0589571, -0.0173279, 0.041216, 0.0844822, 0.0912346, 0.0645646, 0.1929468, 0.311925, 0.3372734, 0.133976, -0.2508656, -0.5391216, -0.6148662, -0.5365452, -0.3167903, -0.1165246, -4.23e-05, 0.2209392, 0.419536, 0.5315746, 0.4845142, 0.2827266, 0.0360813, -0.0867136, -0.0675396, 0.0080409, 0.0399236, -0.0362217, -0.1357998, -0.2303685, -0.2884735, -0.3545715, -0.3660944, -0.3648204, -0.2412072, 0.0678027, 0.3623636, 0.5814903, 0.6880427, 0.5606813, 0.3100918, 0.1068064, -0.1553635, -0.3269145, -0.4010324, -0.4267371, -0.3234141, -0.1586132, -0.0107861, 0.0992772, 0.0827706, -0.0256677, -0.083396, -0.131163, -0.0518291, 0.1109269, 0.2396025, 0.324722, 0.3593922, 0.3218755, 0.2220501, 0.0699108, -0.145596, -0.3533978, -0.4765411, -0.4045785, -0.2283427, -0.0290606, 0.1015483, 0.1122316, 0.0591645, 0.0211602, 0.0247082, -0.001479, -0.0360159, -0.0194992, 0.0889844, 0.2650394, 0.4657723, 0.4729432, 0.2670994, -0.0161622, -0.2955982, -0.4353635, -0.4669149, -0.4805277, -0.4393151, -0.3015933, -0.0698923, 0.2670307, 0.4530151, 0.4153224, 0.2801732, 0.1057394, 0.057051, 0.1519529, 0.188712, 0.1123166, 0.023275, -0.0592817, -0.0671145, -0.0884233, -0.232651, -0.4610739, -0.6060204, -0.5085067, -0.1933483, 0.1724147, 0.3988864, 0.4659545, 0.4469454, 0.4358802, 0.4042639, 0.2600594, -0.0454396, -0.3351542, -0.427866, -0.3170004, -0.077669, 0.0630175, 0.0041641, -0.1234229, -0.1715619, -0.1285259, -0.0550625, -0.0718716, -0.1228279, -0.0800126, 0.1140033, 0.4132451, 0.5579364, 0.4380166, 0.176572, -0.0763841, -0.1962278, -0.1671446, -0.1815775, -0.2575329, -0.2823485, -0.1961599, 0.0096111, 0.1604698, 0.1109895, -0.0867661, -0.2599047, -0.2549969, -0.0404819, 0.1868553, 0.2996105, 0.3301367, 0.3437355, 0.3784413, 0.375507, 0.2098725, -0.1505871, -0.4904163, -0.62939, -0.5405889, -0.3120004, -0.1114081, -0.0383368, 0.0185893, 0.1338008, 0.2589927, 0.3259011, 0.2397898, 0.0915412, 0.0346456, 0.1113975, 0.2603648, 0.3239431, 0.1762879, -0.0663654, -0.270709, -0.3918558, -0.4129844, -0.4235472, -0.4246647, -0.322805, -0.0705485, 0.2602291, 0.5118988, 0.5522395, 0.4162858, 0.2088045, 0.0480738, -0.0134048, -0.0665095, -0.134815, -0.168418, -0.1303641, -0.035356, 0.0537016, 0.0288101, -0.1188396, -0.2779115, -0.3329763, -0.2535867, -0.0915399, 0.0778424, 0.1945694, 0.2860964, 0.3519528, 0.368624, 0.2984347, 0.1185745, -0.0888683, -0.2133576, -0.2368161, -0.1605822, -0.0472424, -0.0162741, -0.032371, -0.0677521, -0.1322739, -0.1803571, -0.2048974, -0.211821, -0.1358366, 0.0314802, 0.245098, 0.4465404, 0.5351117, 0.4871729, 0.3185652, 0.0794115, -0.1490574, -0.319879, -0.425877, -0.4437703, -0.3757598, -0.2510687, -0.0939558, 0.0479569, 0.1271322, 0.1232342, 0.0889038, 0.0723679, 0.1067342, 0.1963523, 0.2885446, 0.320862, 0.2769934, 0.176019, 0.0375572, -0.1281159, -0.3096575, -0.4602587, -0.5393327, -0.4733756, -0.2545241, 0.0099784, 0.2278682, 0.3419394, 0.3373675, 0.2835321, 0.2223463, 0.1319597, 0.028746, -0.0721936, -0.1083341, -0.0364817, 0.0725852, 0.1299164, 0.077328, -0.0872939, -0.2488381, -0.3095997, -0.2943577, -0.2330179, -0.1555911, -0.0620339, 0.0866808, 0.2780688, 0.4060362, 0.3824558, 0.2252529, 0.0334367, -0.0766345, -0.0690255, -0.0125624, 0.0026953, -0.0306279, -0.0675093, -0.0629859, -0.055128, -0.121282, -0.2569323, -0.3820469, -0.3770911, -0.1808757, 0.108918, 0.3378818, 0.446517, 0.4462015, 0.4042283, 0.3462696, 0.2173856, -0.0089965, -0.2732225, -0.4420364, -0.4235734, -0.273836, -0.123408, -0.0604779, -0.0926585, -0.1115776, -0.0459083, 0.050965, 0.1125072, 0.1228814, 0.13313, 0.2186773, 0.3614283, 0.436314, 0.3425185, 0.0879538, -0.1951132, -0.3717415, -0.4140399, -0.3832099, -0.3379891, -0.2733482, -0.1380565, 0.0821949, 0.2841434, 0.3434523, 0.2444916, 0.0762166, -0.0371625, -0.0159494, 0.0733106, 0.1212796, 0.1132826, 0.0845592, 0.0788844, 0.0860659, 0.0249624, -0.1391316, -0.331769, -0.4350972, -0.3647209, -0.1709769, 0.0194613, 0.1357954, 0.1763028, 0.195893, 0.2309903, 0.2364952, 0.1625059, 0.0406094, -0.0474285, -0.0316858, 0.0668182, 0.1482029, 0.1216618, -0.0194145, -0.1875687, -0.3003934, -0.3448171, -0.3454678, -0.3120549, -0.2238322, -0.0424447, 0.2136636, 0.4435827, 0.538753, 0.4728627, 0.302996, 0.1149168, -0.0255222, -0.1238586, -0.2075862, -0.2651903, -0.2685533, -0.2100443, -0.1224458, -0.0746808, -0.0913253, -0.1348435, -0.1457871, -0.0665196, 0.0823487, 0.2292087, 0.3357108, 0.3814515, 0.3592359, 0.2872542, 0.154555, -0.0411645, -0.2445808, -0.3890827, -0.4233264, -0.3402563, -0.1886554, -0.0366206, 0.070097, 0.1189876, 0.1242035, 0.0980749, 0.0533634, 0.0125766, -0.000862, 0.0310546, 0.1076455, 0.199345, 0.2499438, 0.2224462, 0.1168143, -0.0402145, -0.1967081, -0.3003857, -0.3347903, -0.3037877, -0.2185035, -0.1011417, 0.0262944, 0.132958, 0.1912225, 0.1848719, 0.1247071, 0.0626617, 0.0463955, 0.0813303, 0.1473736, 0.194857, 0.1785081, 0.1067132, -0.0012847, -0.1308755, -0.2646541, -0.385229, -0.451651, -0.4108706, -0.2503208, -0.0095427, 0.2256984, 0.3718722, 0.413644, 0.3788701, 0.2997779, 0.1975404, 0.0736909, -0.0635923, -0.1662214, -0.1895981, -0.1469513, -0.0958023, -0.0945088, -0.1584655, -0.2352281, -0.2495139, -0.1823734, -0.0668155, 0.0530152, 0.1585543, 0.2636758, 0.365243, 0.4158439, 0.3586466, 0.1785729, -0.0570766, -0.2367028, -0.3036957, -0.2741839, -0.2064574, -0.1496273, -0.0967659, -0.0229781, 0.0536062, 0.082015, 0.0306726, -0.0642198, -0.1142026, -0.0594451, 0.0835402, 0.2268358, 0.2879695, 0.264023, 0.2011222, 0.1288691, 0.0410056, -0.0874125, -0.2447108, -0.3548328, -0.3454955, -0.22111, -0.0638924, 0.034741, 0.0468307, 0.0216186, 0.0233815, 0.064092, 0.1058529, 0.11651, 0.1160967, 0.153184, 0.2321873, 0.2895553, 0.2472838, 0.0756575, -0.1606832, -0.3475339, -0.4267959, -0.4128536, -0.3452498, -0.2487898, -0.1081416, 0.0888207, 0.2884206, 0.4038001, 0.3838522, 0.2590903, 0.1228054, 0.0465262, 0.0297781, 0.0219831, -0.0194384, -0.0816124, -0.1218057, -0.1328106, -0.1477392, -0.1983079, -0.2687378, -0.2918085, -0.2066, -0.0230365, 0.1781862, 0.3130911, 0.3535291, 0.3249451, 0.2625107, 0.1756275, 0.0516359, -0.0986961, -0.2197982, -0.2501166, -0.1815388, -0.0706655, 0.0068598, 0.0089457, -0.0432839, -0.0940027, -0.113977, -0.1101345, -0.0911418, -0.0509075, 0.0321662, 0.1612282, 0.2897567, 0.3514378, 0.3066219, 0.173185, 0.0161562, -0.1095153, -0.1872946, -0.2227428, -0.2299412, -0.2080888, -0.1484305, -0.0711826, -0.0153198, -0.0035359, -0.026955, -0.0452002, -0.0135607, 0.0756453, 0.1896702, 0.2834695, 0.3261307, 0.3068241, 0.227618, 0.0951108, -0.0774838, -0.258794, -0.3999818, -0.4524473, -0.3970314, -0.2568306, -0.0802233, 0.0819755, 0.1937719, 0.2480117, 0.253819, 0.2217636, 0.1688208, 0.1137803, 0.0719434, 0.0521526, 0.0481974, 0.0387237, -0.0011983, -0.0822759, -0.186166, -0.2773065, -0.3189769, -0.2870815, -0.1898363, -0.0555502, 0.0884031, 0.2142376, 0.2971384, 0.3195811, 0.2697633, 0.1582593, 0.0249627, -0.0794358, -0.1196293, -0.0983877, -0.050499, -0.0137979, -0.0078994, -0.0302671, -0.0678394, -0.1152256, -0.1686717, -0.2062114, -0.1928089, -0.106663, 0.0406035, 0.1982162, 0.30131, 0.3200435, 0.2699157, 0.1836716, 0.0873379, -0.0108515, -0.1076142, -0.1812626, -0.2028569, -0.1669171, -0.105711, -0.0715697, -0.0893152, -0.1341554, -0.1553606, -0.1157309, -0.0216117, 0.0886173, 0.1877129, 0.2717895, 0.3381137, 0.3640899, 0.3100948, 0.1569841, -0.0600165, -0.2614912, -0.3751846, -0.3840774, -0.3226569, -0.2327529, -0.132949, -0.0212711, 0.0961633, 0.1840451, 0.2042634, 0.1601966, 0.101556, 0.0832059, 0.1186755, 0.1672406, 0.1712727, 0.1105052, 0.0131648, -0.0813148, -0.1581435, -0.2270981, -0.2897303, -0.3141432, -0.2582144, -0.1147151, 0.0662049, 0.2037415, 0.2501673, 0.2205675, 0.1617652, 0.1099293, 0.0675725, 0.0188226, -0.0283343, -0.0411936, -0.0037854, 0.0561066, 0.0794865, 0.0241033, -0.0932974, -0.2091108, -0.2650172, -0.2486186, -0.1871668, -0.1062411, -0.0075606, 0.1137957, 0.239033, 0.3193884, 0.309891, 0.2139342, 0.0871334, -0.0077831, -0.048044, -0.0568013, -0.0703139, -0.1007372, -0.1302792, -0.1424513, -0.1463268, -0.1623527, -0.18915, -0.1911683, -0.1255174, 0.0163341, 0.1912105, 0.3298976, 0.3865996, 0.3612608, 0.2775487, 0.1567037, 0.0093503, -0.1513568, -0.2889128, -0.3546358, -0.3253862, -0.2239003, -0.1055178, -0.0192153, 0.0240245, 0.0437726, 0.0629304, 0.0872439, 0.1071843, 0.1204004, 0.1386621, 0.1682638, 0.1947118, 0.1864443, 0.1164961, -0.008935, -0.1478193, -0.2514137, -0.2926997, -0.272031, -0.2063146, -0.1135625, -0.0075188, 0.0930392, 0.1609593, 0.1744915, 0.1347414, 0.070088, 0.0201417, 0.01054, 0.040202, 0.0853348, 0.1171942, 0.1187699, 0.0842275, 0.0145843, -0.0808648, -0.1840255, -0.2673951, -0.2994708, -0.2613168, -0.1588153, -0.0218177, 0.1096364, 0.20311, 0.2435227, 0.2352252, 0.194485, 0.1377348, 0.0793542, 0.0314413, -0.0009979, -0.0200934, -0.0355866, -0.063947, -0.1165121, -0.1872355, -0.2516806, -0.2771196, -0.239887, -0.1394274, 0.0018988, 0.1507472, 0.2762881, 0.3554815, 0.371154, 0.3137057, 0.1901245, 0.0293671, -0.12333, -0.223976, -0.2543258, -0.2292328, -0.1784741, -0.1265855, -0.0834816, -0.0470716, -0.0170361, 0.0020076, 0.0130104, 0.0317509, 0.0765033, 0.1485753, 0.2212898, 0.2540841, 0.2208919, 0.1286928, 0.0113229, -0.0971445, -0.1799289, -0.2317126, -0.24609, -0.2127155, -0.1304661, -0.0238541, 0.0625025, 0.0936115, 0.0705574, 0.0280762, 0.005376, 0.0180984, 0.0543342, 0.0940604, 0.1292458, 0.1604413, 0.1775645, 0.1548709, 0.0712448, -0.0637815, -0.2044029, -0.2940656, -0.3040425, -0.2480978, -0.1605076, -0.0663947, 0.0271642, 0.1174225, 0.1879583, 0.2135704, 0.186369, 0.1308297, 0.0884502, 0.0825939, 0.0970506, 0.0928824, 0.0439355, -0.0427227, -0.1358544, -0.2099039, -0.2601731, -0.2868457, -0.2763886, -0.2056189, -0.0661472, 0.1119594, 0.2675063, 0.3469565, 0.3376573, 0.2664365, 0.1701046, 0.0693175, -0.0309991, -0.1222023, -0.1816199, -0.1878524, -0.1463281, -0.0933532, -0.0687596, -0.0826247, -0.1085324, -0.1084982, -0.0655648, 0.0057173, 0.0794221, 0.1424971, 0.1952596, 0.2336313, 0.2380022, 0.185194, 0.0751432, -0.0567961, -0.1589552, -0.1987593, -0.1818378, -0.1379336, -0.0931349, -0.0556696, -0.0229207, 0.001466, 0.0048172, -0.016006, -0.0408237, -0.0361275, 0.0188128, 0.1109334, 0.1993617, 0.2441714, 0.2301783, 0.1676932, 0.0753245, -0.0337029, -0.1473028, -0.2440204, -0.2943575, -0.2768983, -0.197317, -0.0898051, 0.0052816, 0.0666612, 0.0980212, 0.1154242, 0.1302306, 0.1420292, 0.1468347, 0.1463689, 0.1439347, 0.134197, 0.1011154, 0.0287882, -0.0811849, -0.202575, -0.2960754, -0.3301672, -0.2956454, -0.2041033, -0.0770112, 0.0614756, 0.1863893, 0.272691, 0.2997524, 0.2622314, 0.1769432, 0.0761153, -0.0083368, -0.0592213, -0.0784395, -0.080069, -0.0792908, -0.0849015, -0.0989044, -0.1188076, -0.1372547, -0.1424238, -0.1214953, -0.0660983, 0.0202467, 0.1184777, 0.2004104, 0.2408599, 0.2280598, 0.1683701, 0.0823237, -0.0055816, -0.0756893, -0.1166786, -0.1257608, -0.1078977, -0.0753343, -0.045719, -0.0357938, -0.0523732, -0.0856766, -0.1117667, -0.1061604, -0.0581313, 0.0243958, 0.1193887, 0.2019965, 0.2535926, 0.2616281, 0.2185544, 0.1261966, 0.0010422, -0.1255847, -0.2179152, -0.2536068, -0.2347486, -0.1827688, -0.1218566, -0.0653529, -0.015475, 0.0284036, 0.0641639, 0.0898272, 0.1104437, 0.1370191, 0.1739376, 0.2078115, 0.2115286, 0.1626957, 0.0617529, -0.0656815, -0.1843447, -0.2682529, -0.3063103, -0.2947971, -0.2313921, -0.1202784, 0.017787, 0.1449402, 0.2225921, 0.2345323, 0.1959365, 0.1387203, 0.0878885, 0.0501008, 0.0200954, -0.0052358, -0.0220546, -0.0296506, -0.0395546, -0.0685395, -0.1205901, -0.1757324, -0.2001615, -0.1713355, -0.0948562, 0.0011667, 0.0876595, 0.1509021, 0.1890609, 0.1991953, 0.1739807, 0.112183, 0.0311103, -0.0363143, -0.0635864, -0.0515031, -0.0265082, -0.0185187, -0.0387889, -0.0763531, -0.1132056, -0.1396797, -0.1535726, -0.1481492, -0.1081368, -0.0227019, 0.0963875, 0.211994, 0.2812035, 0.281861, 0.2232783, 0.1329731, 0.0348648, -0.0591877, -0.1413526, -0.1983688, -0.2147198, -0.1874005, -0.1350328, -0.0870633, -0.0609717, -0.0491968, -0.0284931, 0.0176388, 0.0848678, 0.1533617, 0.2044779, 0.2306671, 0.2295183, 0.1940087, 0.1149686, -0.0051273, -0.1411922, -0.2516749, -0.3013113, -0.2812804, -0.2095785, -0.1145204, -0.0181183, 0.0673003, 0.1318326, 0.1639314, 0.1571298, 0.1203735, 0.0779548, 0.0543488, 0.0562722, 0.0688752, 0.0688566, 0.0422102, -0.0078363, -0.0675346, -0.1242659, -0.1696944, -0.1948234, -0.187694, -0.1394651, -0.0551583, 0.0420758, 0.1210736, 0.1602156, 0.1579106, 0.1288703, 0.091425, 0.057739, 0.0329059, 0.0188381, 0.0158436, 0.0191222, 0.0160327, -0.0084684, -0.0605947, -0.1299802, -0.1921917, -0.2208958, -0.2011067, -0.1344589, -0.0354253, 0.0756027, 0.1768383, 0.247546, 0.2715519, 0.2425353, 0.1685904, 0.0713707, -0.0225123, -0.09286, -0.1325188, -0.146592, -0.1461077, -0.1404573, -0.13342, -0.1237097, -0.1069056, -0.0772243, -0.0297264, 0.0369068, 0.1170862, 0.1962002, 0.2528539, 0.2661381, 0.2250896, 0.1350683, 0.0166785, -0.1017459, -0.1939606, -0.2433163, -0.2445673, -0.2022429, -0.1293526, -0.0454383, 0.0277995, 0.0733948, 0.0867198, 0.0779837, 0.0656366, 0.0647553, 0.0786529, 0.0990077, 0.1129663, 0.1105614, 0.0871781, 0.0419344, -0.0227221, -0.0977442, -0.1651879, -0.2029118, -0.1957507, -0.1454495, -0.0708481, 0.0030222, 0.0584521, 0.0904758, 0.1024287, 0.0992334, 0.0856032, 0.0688704, 0.0597831, 0.0661637, 0.0836517, 0.093984, 0.0757312, 0.0199357, -0.0624993, -0.1472024, -0.2102423, -0.2378072, -0.2252865, -0.171901, -0.0809615, 0.034135, 0.1469941, 0.2260297, 0.2506759, 0.2229777, 0.1637901, 0.0971183, 0.0364504, -0.0162482, -0.061753, -0.0972002, -0.1188903, -0.1292268, -0.1373053, -0.1492542, -0.1577759, -0.1439166, -0.0917418, -0.0032524, 0.0996755, 0.1880818, 0.2416556, 0.2538048, 0.2254823, 0.1591615, 0.0613004, -0.0505482, -0.1473353, -0.2008245, -0.2004655, -0.1596208, -0.1048474, -0.057696, -0.0248283, -0.0024464, 0.0134883, 0.0232561, 0.0285596, 0.0379869, 0.062516, 0.1032977, 0.1447749, 0.1621951, 0.1387395, 0.0776075, -0.0012572, -0.0754641, -0.1316304, -0.1651157, -0.1729809, -0.1516708, -0.102929, -0.0402317, 0.0142127, 0.0429609, 0.0459342, 0.039548, 0.0429331, 0.0637472, 0.0950661, 0.1236469, 0.1394363, 0.1371421, 0.1116823, 0.0569304, -0.0275024, -0.1276517, -0.2155255, -0.2615905, -0.2506743, -0.1888468, -0.0967966, 0.0030011, 0.0938661, 0.1639086, 0.2031096, 0.2050786, 0.1727219, 0.1207534, 0.0690129, 0.0302235, 0.0029898, -0.0239195, -0.0602006, -0.1054108, -0.1496085, -0.1805333, -0.1894732, -0.1716101, -0.1240217, -0.04706, 0.050592, 0.1491213, 0.2223671, 0.2491426, 0.2236053, 0.156996, 0.070382, -0.0149997, -0.0834447, -0.1256532, -0.1381505, -0.1240077, -0.0937293, -0.0627348, -0.0442989, -0.042115, -0.0483422, -0.0488048, -0.031674, 0.0060341, 0.0576835, 0.1109294, 0.1523189, 0.1703228, 0.1576041, 0.1133605, 0.0451341, -0.0316039, -0.0977991, -0.1380253, -0.1465366, -0.1287033, -0.0971984, -0.0653744, -0.0416489, -0.0273019, -0.0178069, -0.0059962, 0.0149437, 0.0489073, 0.0944444, 0.1433016, 0.181045, 0.1909181, 0.160836, 0.090159, -0.008136, -0.1114292, -0.1955168, -0.2424578, -0.2444765, -0.2036355, -0.129853, -0.0390718, 0.0492723, 0.1170147, 0.1534944, 0.1596477, 0.1462616, 0.1265117, 0.107811, 0.0889628, 0.0639302, 0.0279585, -0.019022, -0.0730769, -0.1282429, -0.1764461, -0.2060874, -0.2036038, -0.1600542, -0.0789029, 0.0217992, 0.1160387, 0.1812904, 0.206843, 0.1940955, 0.1513516, 0.0896336, 0.022149, -0.0358501, -0.0708875, -0.0783274, -0.0666283, -0.0529085, -0.0517156, -0.0657361, -0.0856029, -0.0979051, -0.0936569, -0.0705834, -0.0300709, 0.0250481, 0.0881683, 0.1455673, 0.1788006, 0.174089, 0.1317654, 0.0672553, 0.0019079, -0.048991, -0.0813371, -0.0981362, -0.1029155, -0.0978158, -0.0865018, -0.0759132, -0.0720297, -0.0725883, -0.06525, -0.035473, 0.0214659, 0.0946223, 0.1627766, 0.206012, 0.2137342, 0.1844369, 0.121384, 0.0314966, -0.0713073, -0.1646581, -0.2240064, -0.2344448, -0.1989402, -0.1354858, -0.0653116, -0.0021724, 0.0494816, 0.0890708, 0.1151958, 0.1267221, 0.1266441, 0.1219729, 0.117469, 0.1091309, 0.0854479, 0.0370311, -0.0335338, -0.1103822, -0.172685, -0.205197, -0.2026322, -0.166845, -0.1030364, -0.0201674, 0.0662978, 0.1355772, 0.1696099, 0.1630334, 0.1264445, 0.0794386, 0.0389065, 0.0117929, -0.0035733, -0.011745, -0.0167395, -0.0229131, -0.0362421, -0.0614389, -0.0959382, -0.1272781, -0.1383645, -0.1176731, -0.0666241, 0.0014793, 0.0692843, 0.1229735, 0.1545741, 0.1603259, 0.1397795, 0.0975659, 0.0448507, -0.0035954, -0.0365835, -0.052733, -0.0601583, -0.0695126, -0.0860708, -0.1067457, -0.1230012, -0.1258943, -0.109111, -0.0694541, -0.0074228, 0.0704806, 0.1498521, 0.2105783, 0.2339032, 0.2107183, 0.1457731, 0.0552383, -0.0401571, -0.1217268, -0.176318, -0.1973233, -0.1850019, -0.1464647, -0.0941442, -0.0417043, 0.0013815, 0.0331713, 0.0581036, 0.0822291, 0.1080797, 0.1323831, 0.1475542, 0.1453057, 0.1200862, 0.0711696, 0.0035189, -0.0722481, -0.1412313, -0.1875278, -0.1990394, -0.1721864, -0.1140535, -0.0402931, 0.0304471, 0.0829048, 0.1095864, 0.1115695, 0.0961801, 0.0734477, 0.052863, 0.0407772, 0.0384522, 0.0412723, 0.0401051, 0.0253258, -0.0077754, -0.0552687, -0.1052218, -0.1426182, -0.155576, -0.1393709, -0.0968461, -0.0364184, 0.030167, 0.0896644, 0.1298828, 0.1435317, 0.1315555, 0.1030301, 0.0704804, 0.0431161, 0.0226368, 0.0044006, -0.0176206, -0.0463899, -0.0806159, -0.1159302, -0.1458832, -0.1618364, -0.1536602, -0.1135922, -0.0422027, 0.0481591, 0.1362492, 0.2002033, 0.225958, 0.2106594, 0.1604614, 0.086508, 0.0026596, -0.0753752, -0.1324493, -0.1590229, -0.1557254, -0.132848, -0.1037994, -0.0770146, -0.0525723, -0.0255863, 0.0076183, 0.0461745, 0.0858946, 0.1219912, 0.149521, 0.1616528, 0.1497487, 0.1079511, 0.0395523, -0.040812, -0.1123871, -0.1578261, -0.1700708, -0.1523085, -0.1130949, -0.0621388, -0.0095372, 0.0339142, 0.0591064, 0.0636011, 0.0546024, 0.0455322, 0.0474126, 0.0616207, 0.0797565, 0.0900346, 0.0842831, 0.0603589, 0.0202348, -0.0318918, -0.0886462, -0.1378641, -0.1647892, -0.1589242, -0.1207661, -0.0624458, -0.0011197, 0.0497937, 0.0849055, 0.1047963, 0.1116536, 0.1074842, 0.0953784, 0.0802266, 0.0660484, 0.0517567, 0.0303656, -0.0058799, -0.0579317, -0.1163775, -0.1650901, -0.1890719, -0.1804191, -0.1391829, -0.0710956, 0.0138906, 0.101238, 0.172632, 0.210841, 0.2070558, 0.1654642, 0.1010269, 0.0317283, -0.0289863, -0.0747826, -0.1043068, -0.1183167, -0.1189901, -0.1105311, -0.0981917, -0.08489, -0.0683109, -0.0421444, -0.0015782, 0.0511377, 0.1057053, 0.1481803, 0.1672336, 0.1579267, 0.1217084, 0.0647655, -0.0029253, -0.0681388, -0.1166589, -0.1379898, -0.130166, -0.1009328, -0.0637578, -0.0311226, -0.0095023, 0.0014151, 0.0062463, 0.0109062, 0.0206982, 0.0390487, 0.0658485, 0.095789, 0.1186656, 0.1229466, 0.1012529, 0.0545063, -0.0078542, -0.0715731, -0.1227102, -0.1514716, -0.1537775, -0.1312425, -0.0903927, -0.0411285, 0.0059456, 0.0429211, 0.0671772, 0.0814713, 0.0911734, 0.1002379, 0.1083564, 0.1108159, 0.1008799, 0.0732137, 0.0266686, -0.0344621, -0.100701, -0.158961, -0.1953429, -0.1988589, -0.1652341, -0.0993317, -0.0146929, 0.070197, 0.1375723, 0.1755368, 0.1806039, 0.1570533, 0.1141594, 0.0629529, 0.0135466, -0.0268869, -0.0555145, -0.0740517, -0.0870795, -0.0986868, -0.1091346, -0.1138601, -0.105844, -0.0799227, -0.036221, 0.0192541, 0.0765909, 0.1247622, 0.1538626, 0.1568924, 0.1318114, 0.0834705, 0.0235707, -0.0327572, -0.0727334, -0.0908407, -0.0898267, -0.0775415, -0.0623151, -0.0499182, -0.0428802, -0.0407615, -0.0400934, -0.0345604, -0.0170926, 0.0159037, 0.0606746, 0.1059892, 0.137679, 0.1450489, 0.124965, 0.0816616, 0.0238365, -0.0380209, -0.0928779, -0.1303798, -0.1435814, -0.1321892, -0.1033144, -0.0679563, -0.0350375, -0.007322, 0.0178428, 0.0441898, 0.072613, 0.100458, 0.1232728, 0.1362885, 0.1343705, 0.1120392, 0.0658072, -0.0016004, -0.0786953, -0.1476485, -0.1911104, -0.1990845, -0.1715574, -0.1163124, -0.0449987, 0.0294173, 0.0937414, 0.1363855, 0.1509311, 0.1392546, 0.1108558, 0.0776713, 0.0477154, 0.0222219, -0.0020623, -0.0282035, -0.0562068, -0.0834652, -0.1065546, -0.1217647, -0.1242958, -0.1085819, -0.0714302, -0.016207, 0.045885, 0.0995648, 0.1321545, 0.1385366, 0.1214181, 0.0880468, 0.0468659, 0.0061517, -0.026439, -0.045491, -0.0505636, -0.0473787, -0.0451083, -0.0506914, -0.0641975, -0.0787791, -0.0849615, -0.0757119, -0.0489398, -0.0070955, 0.0440289, 0.095688, 0.1366083, 0.1554129, 0.1451601, 0.107272, 0.0516067, -0.0078927, -0.0590745, -0.0951611, -0.1145896, -0.1188179, -0.1106486, -0.093787, -0.0723631, -0.0493376, -0.0246779, 0.0045358, 0.0411382, 0.0838559, 0.1254749, 0.1549144, 0.1618704, 0.1409859, 0.0934609, 0.0263267, -0.0491191, -0.1189505, -0.1689269, -0.188083, -0.1726114, -0.1277375, -0.0659371, -0.0021378, 0.0514239, 0.0880778, 0.1066755, 0.1098683, 0.1022223, 0.0886959, 0.0731853, 0.0571055, 0.0388305, 0.0149422, -0.0169036, -0.055143, -0.0934566, -0.1226301, -0.1340767, -0.1231024, -0.0904896, -0.0421854, 0.0122721, 0.0619737, 0.0972134, 0.1120579, 0.1061472, 0.0847802, 0.0568352, 0.0311874, 0.0132619, 0.003354, -0.0026189, -0.0105281, -0.0249832, -0.0471277, -0.073902, -0.098715, -0.1131956, -0.1096908, -0.0840384, -0.0377895, 0.0211828, 0.0804231, 0.1266573, 0.1500424, 0.146981, 0.1204008, 0.0778381, 0.0286461, -0.0184276, -0.0568575, -0.0833019, -0.0978997, -0.1034746, -0.103459, -0.0993486, -0.0893129, -0.0692102, -0.0356482, 0.0108477, 0.0646099, 0.1161076, 0.1546474, 0.1710197, 0.1594659, 0.1192413, 0.055861, -0.0188713, -0.0892432, -0.1403083, -0.1628341, -0.1558403, -0.125507, -0.081641, -0.0341282, 0.0090716, 0.0426496, 0.0644283, 0.0757332, 0.0806686, 0.0836383, 0.0861651, 0.0853654, 0.0756379, 0.052598, 0.0164187, -0.0276535, -0.071425, -0.1065091, -0.1260451, -0.1255168, -0.1037959, -0.0645752, -0.0166984, 0.0281882, 0.0601953, 0.0752547, 0.0759271, 0.0686182, 0.0595486, 0.0522615, 0.0474509, 0.0437803, 0.0382824, 0.0265892, 0.0043641, -0.0296959, -0.0711019, -0.1096966, -0.133448, -0.1337339, -0.1087346, -0.0632602, -0.0061709, 0.0522304, 0.1017137, 0.1333782, 0.1417325, 0.1269752, 0.0953868, 0.0565739, 0.0189024, -0.0137583, -0.0418167, -0.0670183, -0.0895748, -0.1074484, -0.1174677, -0.1164979, -0.1017122, -0.0708318, -0.0236425, 0.035538, 0.0966554, 0.1459978, 0.1711019, 0.1655984, 0.1311126, 0.0757021, 0.0106938, -0.0519682, -0.1014251, -0.129617, -0.1334407, -0.116141, -0.0861814, -0.0534129, -0.0246751, -0.0016919, 0.0176623, 0.0362535, 0.0552694, 0.0737193, 0.0891205, 0.0980703, 0.0965033, 0.0805993, 0.0489249, 0.004746, -0.0435572, -0.0850172, -0.1103376, -0.1152783, -0.1013454, -0.0740711, -0.0405898, -0.0078486, 0.0185608, 0.0355656, 0.0436714, 0.0468702, 0.0505947, 0.0584014, 0.0694283, 0.0784147, 0.0782942, 0.0636451, 0.0330665, -0.010264, -0.0593516, -0.1047243, -0.1362317, -0.1455177, -0.1288129, -0.0887793, -0.0340929, 0.0233955, 0.0725697, 0.1060887, 0.1213925, 0.1198898, 0.1053536, 0.0823722, 0.0550876, 0.0262271, -0.0033221, -0.0339632, -0.0658196, -0.0969075, -0.122295, -0.1350686, -0.1288325, -0.1005339, -0.0522651, 0.0086746, 0.0713235, 0.1235726, 0.1549761, 0.1592878, 0.1361557, 0.0913926, 0.0354798, -0.0194399, -0.0630888, -0.0897414, -0.099039, -0.0948037, -0.0826429, -0.0675304, -0.0522871, -0.0372456, -0.0208758, -0.0010132, 0.0236203, 0.0519942, 0.0800542, 0.1013385, 0.1090547, 0.098868, 0.0710214, 0.0306319, -0.0139586, -0.0538434, -0.081975, -0.0946103, -0.0917577, -0.0768589, -0.0557176, -0.0345763, -0.0177357, -0.0058117, 0.0041994, 0.0168154, 0.0350643, 0.0583547, 0.0823243, 0.1003999, 0.1059505, 0.0941544, 0.0634352, 0.0165761, -0.0388744, -0.0917333, -0.1301938, -0.1457147, -0.1358279, -0.1043773, -0.0593511, -0.0098839, 0.0359829, 0.0723438, 0.0958544, 0.1060063, 0.1049118, 0.0959966, 0.081862, 0.0627052, 0.0367055, 0.00237, -0.0388316, -0.0815172, -0.1177397, -0.1395365, -0.1411146, -0.1200286, -0.0778005, -0.0204006, 0.0419297, 0.0965415, 0.1320306, 0.1421287, 0.1275968, 0.095048, 0.0537028, 0.012202, -0.0231018, -0.0485448, -0.0631965, -0.0688547, -0.0693265, -0.0684652, -0.067711, -0.0649164, -0.0556594, -0.0363454, -0.0069227, 0.0285695, 0.0635884, 0.0913173, 0.1062093, 0.1047833, 0.0863956, 0.054129, 0.0148491, -0.022591, -0.0506446, -0.065982, -0.0700955, -0.067343, -0.0620215, -0.0564412, -0.0506886, -0.0433143, -0.0319769, -0.0140198, 0.012352, 0.0461941, 0.0823873, 0.1122097, 0.1262878, 0.1184504, 0.0882394, 0.0408762, -0.0147212, -0.0683594, -0.110494, -0.1337867, -0.1346793, -0.1145829, -0.0796157, -0.0383631, 0.0014032, 0.035136, 0.0617158, 0.0817771, 0.0957412, 0.1029188, 0.1017972, 0.0907329, 0.068482, 0.0347711, -0.0086863, -0.0570327, -0.1020836, -0.1340293, -0.1446511, -0.1304264, -0.0938487, -0.0424777, 0.0134312, 0.0634726, 0.0991423, 0.1153942, 0.1116958, 0.0921114, 0.0638812, 0.0347016, 0.0099151, -0.0089132, -0.0233848, -0.0362741, -0.0494851, -0.062947, -0.0745795, -0.0809302, -0.0781766, -0.0634426, -0.0362898, 0.0002022, 0.0395732, 0.0736722, 0.0953037, 0.1005799, 0.0898975, 0.0672809, 0.0386717, 0.0100179, -0.01424, -0.0320457, -0.0438666, -0.0520018, -0.0591312, -0.0665748, -0.0730848, -0.0749127, -0.0672983, -0.0467523, -0.0130757, 0.0297828, 0.0743474, 0.1114203, 0.1324406, 0.1317409, 0.1081625, 0.0656061, 0.0122621, -0.0414166, -0.0854722, -0.1130059, -0.1215969, -0.113101, -0.092109, -0.0639235, -0.0329636, -0.0020797, 0.0272795, 0.0544018, 0.0784776, 0.0978391, 0.1095597, 0.1097919, 0.0950469, 0.0640838, 0.0195053, -0.0319474, -0.0807916, -0.1173602, -0.1344158, -0.128889, -0.1024195, -0.0608602, -0.0129591, 0.0316751, 0.0650547, 0.0829917, 0.0859716, 0.078169, 0.0650808, 0.0510694, 0.0380375, 0.0255746, 0.0120131, -0.0043794, -0.0244219, -0.047221, -0.0696544, -0.0866226, -0.0924957, -0.0834382, -0.0594295, -0.0247161, 0.013628, 0.0481447, 0.0730987, 0.0854837, 0.0850861, 0.0741145, 0.0565482, 0.0369628, 0.0188526, 0.0032085, -0.0115924, -0.028214, -0.047989, -0.0692986, -0.0877085, -0.0975869, -0.0940919, -0.0746359, -0.0397152, 0.0066364, 0.0568897, 0.1011458, 0.1296417, 0.1358936, 0.1189077, 0.0832273, 0.0369295, -0.0111104, -0.0533072, -0.0843538, -0.101542, -0.1048509, -0.0966426, -0.0805589, -0.0598022, -0.0358151, -0.0084134, 0.0226057, 0.0555773, 0.0862725, 0.1088889, 0.1179723, 0.1100955, 0.0847343, 0.0445641, -0.0045223, -0.0539352, -0.0940107, -0.1167065, -0.118285, -0.1004778, -0.0694283, -0.0331141, 0.0012491, 0.0287234, 0.0470353, 0.056388, 0.0590038, 0.0581895, 0.0567796, 0.0555162, 0.0524534, 0.0440511, 0.0274491, 0.002497, -0.0276747, -0.0576509, -0.0814471, -0.0940243, -0.0923222, -0.0759958, -0.0478509, -0.0135257, 0.0199915, 0.0466601, 0.0633499, 0.0703415, 0.0701767, 0.0657487, 0.0588329, 0.049656, 0.0372807, 0.0202992, -0.00237, -0.0305175, -0.0616486, -0.090577, -0.110291, -0.1141318, -0.0983825, -0.0639665, -0.0164564, 0.035435, 0.0820636, 0.1149804, 0.1286135, 0.1213778, 0.0959852, 0.0586454, 0.0171741, -0.0213461, -0.0523226, -0.0740749, -0.0869783, -0.0920791, -0.0900345, -0.0807893, -0.0638796, -0.0390687, -0.0070978, 0.0296057, 0.0665464, 0.0975089, 0.1158716, 0.1166009, 0.0981341, 0.0632691, 0.0186243, -0.027107, -0.0654104, -0.0899731, -0.0979712, -0.0904054, -0.071461, -0.0470458, -0.0228918, -0.0028374, 0.0120538, 0.0231804, 0.0330646, 0.0436482, 0.0550177, 0.0650957, 0.0703491, 0.0671431, 0.0531975, 0.0286703, -0.0034416, -0.0377322, -0.0676189, -0.0871041, -0.0925222, -0.0835966, -0.0633345, -0.0367854, -0.0092436, 0.0153041, 0.034905, 0.049452, 0.0598878, 0.0672197, 0.0716595, 0.0721179, 0.0662915, 0.0515128, 0.0262324, -0.0084617, -0.0481194, -0.0855601, -0.1127293, -0.1230121, -0.1130945, -0.0838389, -0.0401286, 0.0101295, 0.0576661, 0.0939634, 0.1133411, 0.1142647, 0.0992541, 0.0734215, 0.0424155, 0.0108049, -0.0185087, -0.0439858, -0.0648084, -0.0803205, -0.0896299, -0.0913445, -0.0836343, -0.0649142, -0.0351155, 0.0030907, 0.0440283, 0.0801729, 0.1043064, 0.1116295, 0.100953, 0.0747544, 0.0384374, -0.000849, -0.0357333, -0.0603987, -0.0721546, -0.0720244, -0.0638537, -0.0522915, -0.0407333, -0.030298, -0.0201288, -0.008487, 0.0061185, 0.0241238, 0.0443816, 0.0639066, 0.0782001, 0.0824744, 0.0735407, 0.0514473, 0.0198597, -0.0151006, -0.0468785, -0.070285, -0.0824143, -0.0827493, -0.0728541, -0.0558295, -0.0354109, -0.0147347, 0.004704, 0.0231689, 0.041622, 0.060108, 0.0766893, 0.0876583, 0.0888444, 0.0771996, 0.0519493, 0.0151228, -0.0284054, -0.0713863, -0.1054375, -0.1231113, -0.1201995, -0.0972033, -0.0591573, -0.0139093, 0.0302124, 0.0663123, 0.089955, 0.0994586, 0.095731, 0.0816829, 0.0611377, 0.0374821, 0.0127112, -0.0125158, -0.037832, -0.0619021, -0.081876, -0.093949, -0.0946313, -0.0820036, -0.0564791, -0.0210303, 0.0190305, 0.0567933, 0.0851672, 0.0987809, 0.0956695, 0.0778802, 0.0506158, 0.0203431, -0.0072019, -0.0283238, -0.0417599, -0.0483671, -0.0503719, -0.0503213, -0.049906, -0.0490577, -0.0458537, -0.0374684, -0.0217935, 0.001111, 0.0284989, 0.0555534, 0.0767485, 0.087351, 0.0846253, 0.0685452, 0.0419068, 0.0097289, -0.0220435, -0.0481834, -0.0655339, -0.0734704, -0.0733335, -0.0672348, -0.056912, -0.043146, -0.0258653, -0.0047426, 0.0200082, 0.0469036, 0.0727896, 0.0930333, 0.1024618, 0.0969019, 0.0748123, 0.0383217, -0.0068359, -0.0525419, -0.0902538, -0.1130595, -0.1172271, -0.1028947, -0.0738008, -0.0361762, 0.0029007, 0.0372245, 0.0627413, 0.0779908, 0.0836338, 0.0813769, 0.0728546, 0.058993, 0.0400784, 0.0163761, -0.0110915, -0.0400114, -0.0667369, -0.0867969, -0.0958707, -0.0910198, -0.0718246, -0.0409769, -0.0039575, 0.0322516, 0.0610275, 0.0778458, 0.0812612, 0.0728682, 0.0564146, 0.0364858, 0.0171765, 0.0010595, -0.0113029, -0.0211167, -0.0303822, -0.0405281, -0.0512888, -0.0604273, -0.0644839, -0.06018, -0.0458007, -0.0220094, 0.0080884, 0.0395221, 0.0665029, 0.083777, 0.0880163, 0.0787896, 0.0586323, 0.032081, 0.0040921, -0.0213932, -0.0421203, -0.0573427, -0.0671644, -0.071865, -0.0714345, -0.0653524, -0.0526573, -0.0324419, -0.0047858, 0.0282394, 0.0621438, 0.0906737, 0.1074516, 0.1079122, 0.0907056, 0.0581201, 0.0155872, -0.0294292, -0.0689243, -0.0960586, -0.1067611, -0.1006696, -0.080917, -0.0527759, -0.0218073, 0.0076003, 0.0327743, 0.0524873, 0.06636, 0.0743345, 0.0763044, 0.0718749, 0.0603571, 0.0412134, 0.0149395, -0.0160726, -0.0473683, -0.0733149, -0.0887292, -0.0904583, -0.0782291, -0.0545834, -0.0241631, 0.0073333, 0.0343708, 0.0528636, 0.0612035, 0.0605248, 0.0539166, 0.0448682, 0.035748, 0.0270847, 0.0178802, 0.0065695, -0.0079541, -0.0256138, -0.0447855, -0.0623145, -0.0740448, -0.0759584, -0.0656664, -0.043599, -0.0132203, 0.0199145, 0.0497931, 0.0715023, 0.0821632, 0.081172, 0.0699486, 0.0513659, 0.0289112, 0.0057127, -0.0162146, -0.0360774, -0.0536612, -0.068344, -0.078449, -0.0814097, -0.0746695, -0.0568242, -0.0284872, 0.0073743, 0.0456483, 0.0798164, 0.1032173, 0.1106874, 0.1001123, 0.0732405, 0.0353179, -0.0063604, -0.0443772, -0.0728732, -0.0885286, -0.0907799, -0.0814282, -0.0638135, -0.0417349, -0.0184039, 0.004184, 0.0251797, 0.0441905, 0.0604215, 0.0722089, 0.0772436, 0.0733583, 0.0594736, 0.0363001, 0.0065708, -0.0252528, -0.05378, -0.0738813, -0.082008, -0.0772026, -0.0613678, -0.0386206, -0.01396, 0.0082117, 0.0251633, 0.0361892, 0.0422547, 0.0451665, 0.0465839, 0.04719, 0.046302, 0.0421011, 0.0324633, 0.0161108, -0.0063941, -0.032272, -0.0569877, -0.0754063, -0.083228, -0.0782025, -0.0607563, -0.0338879, -0.0024055, 0.0282562, 0.0533137, 0.0696399, 0.076203, 0.0738187, 0.0643698, 0.0498666, 0.0317891, 0.0109837, -0.0119162, -0.0358381, -0.058769, -0.0776746, -0.088926, -0.089136, -0.0762066, -0.0502966, -0.0143677, 0.0260079, 0.0636931, 0.0916594, 0.1047547, 0.1009523, 0.0816904, 0.0512887, 0.0157362, -0.0187453, -0.0470153, -0.0659338, -0.0746814, -0.0743883, -0.0672122, -0.0552734, -0.0399498, -0.021843, -0.0013281, 0.0207256, 0.0425052, 0.0612916, 0.0739315, 0.0775662, 0.0704482, 0.0526755, 0.0265919, -0.0034357, -0.0319798, -0.0540057, -0.0662441, -0.06791, -0.060536, -0.0471342, -0.0311168, -0.0153531, -0.0015395, 0.0100296, 0.0202096, 0.0302593, 0.0408196, 0.0510991, 0.0587602, 0.0606635, 0.0541338, 0.0381401, 0.0138977, -0.015238, -0.0444338, -0.0683795, -0.0824647, -0.0839259, -0.0726229, -0.0510714, -0.0236151, 0.0049347, 0.0305688, 0.0507407, 0.0643163, 0.0711061, 0.0713559, 0.0653967, 0.0534746, 0.0357724, 0.0126981, -0.0145595, -0.043451, -0.0699188, -0.089017, -0.0962101, -0.0888203, -0.0669997, -0.0338745, 0.0050765, 0.043141, 0.0737259, 0.0917404, 0.0947369, 0.0834364, 0.0613428, 0.0335236, 0.0050559, -0.0201943, -0.0400528, -0.0538172, -0.0616885, -0.0641947, -0.0617656, -0.0545021, -0.0422099, -0.0247788, -0.0028368, 0.0216612, 0.0453488, 0.0640934, 0.0741596, 0.0733649, 0.0617325, 0.0414614, 0.0163425, -0.0091355, -0.0307926, -0.0457007, -0.0528183, -0.053038, -0.0485395, -0.0416879, -0.0340014, -0.0256871, -0.0159082, -0.0035564, 0.0118854, 0.0297272, 0.047865, 0.0630225, 0.0714463, 0.0699602, 0.0571039, 0.033909, 0.0039217, -0.0276136, -0.0551068, -0.0739813, -0.0816041, -0.0775987, -0.0636022, -0.0426323, -0.0182439, 0.0063057, 0.0286349, 0.0473518, 0.0616982, 0.0709732, 0.0741307, 0.0698398, 0.0570203, 0.035591, 0.0070744, -0.0252146, -0.0565026, -0.0813768, -0.0949371, -0.0940278, -0.0781962, -0.0500234, -0.0146333, 0.0215237, 0.0522273, 0.0728808, 0.0813364, 0.0779897, 0.0652336, 0.0465243, 0.025371, 0.0045365, -0.0143363, -0.0306022, -0.0440986, -0.0545157, -0.0609831, -0.0621055, -0.0564644, -0.0433607, -0.0234591, 0.0009657, 0.0263083, 0.0483707, 0.0633413, 0.068733, 0.0640094, 0.0506979, 0.0319213, 0.0114714, -0.0072722, -0.0221863, -0.0326999, -0.0394747, -0.0436618, -0.0460947, -0.0467532, -0.044685, -0.0383854, -0.0265024, -0.0086464, 0.0139745, 0.0383722, 0.0602305, 0.0749356, 0.0788683, 0.0704852, 0.0507988, 0.0231228, -0.0077635, -0.036727, -0.05936, -0.0727927, -0.0760753, -0.0700449, -0.0567298, -0.0385326, -0.0175566, 0.0046309, 0.0267608, 0.0473983, 0.0646091, 0.0759975, 0.079127, 0.0721688, 0.054584, 0.0276605, -0.0052763, -0.039166, -0.0681206, -0.0867826, -0.0917063, -0.082251, -0.0606595, -0.0313557, 0.0002198, 0.0288453, 0.0505349, 0.0631716, 0.0666478, 0.0624688, 0.0529294, 0.040183, 0.02562, 0.0098284, -0.006933, -0.0240977, -0.0403835, -0.0538046, -0.0620512, -0.0630657, -0.0556672, -0.0400909, -0.0182701, 0.0063473, 0.0295352, 0.0473955, 0.0574008, 0.0589324, 0.0531333, 0.0422441, 0.0287746, 0.0148109, 0.0016052, -0.0105134, -0.0218871, -0.0329654, -0.0436091, -0.052604, -0.0577191, -0.0563926, -0.0467686, -0.0286144, -0.0037268, 0.0242853, 0.0507109, 0.0707272, 0.080466, 0.0779475, 0.063615, 0.0402361, 0.0121292, -0.0160118, -0.0402254, -0.0579234, -0.0679568, -0.0702844, -0.0655265, -0.0545967, -0.0384845, -0.0182147, 0.0049952, 0.0294205, 0.0525435, 0.0710783, 0.0814951, 0.0809572, 0.0683127, 0.0447084, 0.0135719, -0.0200199, -0.0503755, -0.0723695, -0.0825247, -0.0797567, -0.0655522, -0.0434613, -0.0180377, 0.0064024, 0.0267361, 0.0414149, 0.0502616, 0.053924, 0.0532686, 0.0489309, 0.0411234, 0.029744, 0.0147584, -0.0032787, -0.022814, -0.0412693, -0.0555151, -0.0627086, -0.0611597, -0.0508779, -0.0336121, -0.012412, 0.0091215, 0.0276767, 0.0409498, 0.0480475, 0.049443, 0.0464908, 0.0406915, 0.0330299, 0.0236859, 0.0122362, -0.0017742, -0.0181701, -0.0356911, -0.0519345, -0.063745, -0.067969, -0.0623787, -0.0464959, -0.0220264, 0.0073004, 0.0364938, 0.0604566, 0.0751674, 0.0785266, 0.0706461, 0.0535698, 0.0305849, 0.0053724, -0.01875, -0.0393075, -0.0547844, -0.0644201, -0.0678449, -0.0647793, -0.0549854, -0.0385262, -0.016212, 0.0100091, 0.0369692, 0.0606395, 0.0768824, 0.082402, 0.0756162, 0.0571969, 0.0300946, -0.0010035, -0.0306864, -0.0540463, -0.0677632, -0.0707152, -0.0639433, -0.0500438, -0.0322554, -0.0135773, 0.0038204, 0.0187861, 0.0309986, 0.0405309, 0.047348, 0.0509578, 0.0503925, 0.0445769, 0.0329437, 0.0160106, -0.0043777, -0.025285, -0.0433202, -0.0554444, -0.0596917, -0.0556109, -0.0443307, -0.0282241, -0.0102458, 0.0068696, 0.0212631, 0.0322056, 0.0398611, 0.0447205, 0.0470147, 0.0463949, 0.0420017, 0.0328614, 0.0184459, -0.0007807, -0.0230112, -0.0451093, -0.0631014, -0.0730988, -0.0723847, -0.0602748, -0.0384126, -0.0104089, 0.0190127, 0.0450591, 0.0638213, 0.0729475, 0.0719292, 0.0619502, 0.0453444, 0.0248514, 0.0029682, -0.0183543, -0.0376129, -0.0534434, -0.0643811, -0.0688905, -0.0656678, -0.0540872, -0.0346251, -0.0091378, 0.0191257, 0.0458794, 0.0665672, 0.077405, 0.076377, 0.063805, 0.0422605, 0.0158712, -0.0107058, -0.0333089, -0.0490491, -0.0567119, -0.056731, -0.0507505, -0.040901, -0.0290484, -0.0163279, -0.0031468, 0.010402, 0.0239928, 0.0366983, 0.0469682, 0.0529354, 0.0529171, 0.0459606, 0.0322911, 0.0135176, -0.0075415, -0.0274501, -0.0430065, -0.052045, -0.0538694, -0.0491833, -0.0396193, -0.0271171, -0.0133901, 0.0003847, 0.0136085, 0.0260519, 0.0374597, 0.0471286, 0.0536854, 0.0552586, 0.0500594, 0.0371618, 0.0171441, -0.0076922, -0.0336232, -0.0562279, -0.0713353, -0.0759671, -0.0690541, -0.051731, -0.0270866, 0.0005903, 0.0268377, 0.0479752, 0.0616895, 0.0671452, 0.0647147, 0.0555347, 0.0410842, 0.0229014, 0.0024896, -0.0186046, -0.0386142, -0.0554247, -0.066663, -0.0700903, -0.0642278, -0.0489868, -0.0260239, 0.0013628, 0.0288047, 0.0517521, 0.0664463, 0.0707257, 0.0644658, 0.0495248, 0.0291971, 0.0073253, -0.0126497, -0.0283934, -0.0389375, -0.044472, -0.0458329, -0.0439265, -0.0393173, -0.032105, -0.0221063, -0.0092468, 0.0059998, 0.0223231, 0.0375967, 0.0492579, 0.0549537, 0.0532303, 0.0440136, 0.0287, 0.0098276, -0.0095518, -0.0265895, -0.0392259, -0.0464871, -0.0484386, -0.0458503, -0.0397174, -0.030836, -0.0196095, -0.0061722, 0.0092304, 0.0257928, 0.0418705, 0.0550455, 0.0625745, 0.0620967, 0.0523813, 0.0338768, 0.0088767, -0.0187857, -0.0444754, -0.0637562, -0.0733855, -0.0720092, -0.0603534, -0.0408829, -0.0170862, 0.0073556, 0.0292612, 0.0463601, 0.0573877, 0.0619288, 0.0601268, 0.0524033, 0.039323, 0.0216743, 0.0007138, -0.021596, -0.0425632, -0.0590808, -0.0682354, -0.0680254, -0.0579499, -0.0392828, -0.0149403, 0.011047, 0.0343604, 0.0513326, 0.0597413, 0.0592064, 0.05106, 0.0377578, 0.0220772, 0.0064036, -0.0076872, -0.0194862, -0.0289185, -0.0361489, -0.0411748, -0.0435647, -0.0424695, -0.0369495, -0.0265043, -0.0115696, 0.0062722, 0.0244948, 0.040174, 0.0506774, 0.05427, 0.0504826, 0.0401716, 0.0252662, 0.0082579, -0.0084244, -0.0229436, -0.0342901, -0.0421523, -0.0465369, -0.0473783, -0.0443517, -0.0369759, -0.0249383, -0.0084984, 0.0111798, 0.0318939, 0.0505694, 0.0637767, 0.0685241, 0.0631094, 0.0477239, 0.0245636, -0.002606, -0.0292809, -0.0511754, -0.0650551, -0.0692756, -0.063943, -0.0506864, -0.0321136, -0.0111226, 0.0096942, 0.0283589, 0.0434878, 0.054072, 0.0592728, 0.058386, 0.0510063, 0.0373002, 0.0182662, -0.0041294, -0.0270286, -0.0470303, -0.0608077, -0.0658699, -0.0612358, -0.0477587, -0.0279583, -0.0054069, 0.0161175, 0.0334456, 0.0445929, 0.0489864, 0.0473273, 0.0411487, 0.0322101, 0.02194, 0.011133, 1.1e-05, -0.0114151, -0.0228915, -0.0336113, -0.0421935, -0.04696, -0.0464028, -0.0396923, -0.0270687, -0.0099874, 0.0090702, 0.0271045, 0.0412905, 0.0496241, 0.0513014, 0.0467233, 0.0371748, 0.0243487, 0.0099, -0.004833, -0.018888, -0.031544, -0.0420761, -0.049533, -0.0526828, -0.0502203, -0.0412139, -0.0256374, -0.0047603, 0.0187992, 0.0414331, 0.0592408, 0.0688807, 0.0683501, 0.0574865, 0.0380504, 0.0133528, -0.0124809, -0.0354065, -0.0522636, -0.0612646, -0.0620794, -0.0555675, -0.0433192, -0.0271938, -0.0089982, 0.0096371, 0.0272151, 0.042255, 0.0532218, 0.0586041, 0.0571777, 0.0483985, 0.0327843, 0.0121051, -0.0107481, -0.0322415, -0.0489144, -0.0581362, -0.0586817, -0.050964, -0.0368645, -0.0192171, -0.0010957, 0.0148879, 0.0270745, 0.0349048, 0.0387096, 0.0392577, 0.0372669, 0.0330797, 0.0266215, 0.0176306, 0.0060418, -0.0076468, -0.0221782, -0.0356081, -0.0456542, -0.0502421, -0.0480872, -0.0391161, -0.0245671, -0.0067225, 0.0116365, 0.0278602, 0.0399452, 0.046801, 0.0482382, 0.044745, 0.0371592, 0.0263683, 0.0131464, -0.0018054, -0.0176477, -0.0331448, -0.046537, -0.0556821, -0.05848, -0.053461, -0.0403338, -0.0203038, 0.0039566, 0.0287081, 0.0498468, 0.0637583, 0.0681127, 0.0623705, 0.0478413, 0.0272855, 0.0042056, -0.0179278, -0.0362932, -0.0490555, -0.0554149, -0.0554344, -0.0497513, -0.0392875, -0.0250671, -0.0082037, 0.0099697, 0.0277516, 0.0430651, 0.053695, 0.0577343, 0.0540962, 0.0429024, 0.0256096, 0.004825, -0.0161604, -0.0340291, -0.0461367, -0.0510651, -0.0488534, -0.0408224, -0.0290632, -0.0157919, -0.0028061, 0.008791, 0.0185692, 0.026526, 0.0327407, 0.0370634, 0.0389549, 0.0375644, 0.0320497, 0.0220363, 0.0080208, -0.0084789, -0.0251201, -0.0392252, -0.0483928, -0.051042, -0.0467441, -0.0362798, -0.0214181, -0.0044823, 0.0121796, 0.0266506, 0.0376812, 0.0446553, 0.0473614, 0.0457375, 0.0397441, 0.0294305, 0.0151469, -0.0022062, -0.0210133, -0.0389595, -0.053286, -0.0612819, -0.0609272, -0.0515029, -0.0339543, -0.0108426, 0.0141301, 0.0368919, 0.0538426, 0.0625469, 0.0621283, 0.0533006, 0.0380577, 0.0191231, -0.0006777, -0.0189356, -0.0339273, -0.0446125, -0.0504484, -0.0511826, -0.0467529, -0.0373373, -0.0235112, -0.0064129, 0.0121815, 0.0299526, 0.0443373, 0.0530026, 0.0543867, 0.048139, 0.0352898, 0.0180655, -0.0006135, -0.0177962, -0.0311254, -0.0392511, -0.0419446, -0.0399145, -0.034408, -0.0267396, -0.0179073, -0.0084291, 0.0015532, 0.0119661, 0.0224513, 0.0321374, 0.039658, 0.0434277, 0.0420895, 0.0349824, 0.0224745, 0.0060428, -0.0119442, -0.0286989, -0.0416138, -0.0488126, -0.0494927, -0.0439688, -0.0334419, -0.0196034, -0.0042305, 0.0110986, 0.0251105, 0.0367814, 0.0452071, 0.0495, 0.0488089, 0.0424973, 0.0304452, 0.0133675, -0.0070015, -0.0279805, -0.0463222, -0.0588394, -0.0631154, -0.0580967, -0.0443947, -0.0242051, -0.000861, 0.021862, 0.0405146, 0.0526167, 0.0570235, 0.053941, 0.0446339, 0.0309601, 0.0149078, -0.0017244, -0.0174499, -0.031052, -0.0414774, -0.0477639, -0.0490781, -0.0448754, -0.0351443, -0.0206388, -0.0029826, 0.0154548, 0.0319334, 0.0438939, 0.0495244, 0.0481634, 0.0404156, 0.0279533, 0.0130761, -0.0018381, -0.0148398, -0.0247369, -0.0311631, -0.0343827, -0.0349277, -0.0332257, -0.0293839, -0.0232176, -0.0144957, -0.0032847, 0.0097641, 0.0233061, 0.0354328, 0.0440226, 0.0472342, 0.0440091, 0.0344219, 0.0197494, 0.0022179, -0.0154926, -0.0308237, -0.041813, -0.0473539, -0.0472169, -0.0418872, -0.0323051, -0.0196099, -0.0049666, 0.0104728, 0.02549, 0.0386864, 0.0484582, 0.0531577, 0.0514407, 0.0426984, 0.0274129, 0.0072942, -0.0148758, -0.0356803, -0.0517056, -0.0602418, -0.0598744, -0.0507995, -0.0347575, -0.0145978, 0.0063843, 0.0251329, 0.0393509, 0.0477518, 0.0500459, 0.0467302, 0.0387846, 0.0273795, 0.0136855, -0.001168, -0.0160102, -0.029519, -0.0402068, -0.046577, -0.0474353, -0.0422458, -0.031394, -0.0162499, 0.0010091, 0.0177815, 0.0315541, 0.0404165, 0.043442, 0.0408125, 0.0336462, 0.0235908, 0.012337, 0.0012282, -0.0089169, -0.017748, -0.0251628, -0.0310316, -0.034985, -0.0363556, -0.0343169, -0.028195, -0.0178511, -0.0039806, 0.0118165, 0.0272539, 0.0398163, 0.0473164, 0.0483866, 0.0427719, 0.0313627, 0.0159707, -0.001081, -0.0174397, -0.0311393, -0.0408398, -0.0458554, -0.0460181, -0.0414935, -0.0326597, -0.0201038, -0.0047184, 0.0121824, 0.0288179, 0.0430405, 0.0526109, 0.0556213, 0.0509745, 0.038778, 0.0205072, -0.0011554, -0.0227983, -0.0409614, -0.0528363, -0.0568045, -0.0526875, -0.0416651, -0.0259115, -0.0080612, 0.0093415, 0.0242664, 0.0353996, 0.0421169, 0.0443013, 0.0421263, 0.0359153, 0.0261275, 0.0134513, -0.001072, -0.0159899, -0.0295146, -0.0397586, -0.0450922, -0.044529, -0.0380187, -0.0265384, -0.0119281, 0.0034976, 0.0174541, 0.0281554, 0.034613, 0.0366923, 0.0349377, 0.0302521, 0.0235525, 0.0155268, 0.0065633, -0.0031303, -0.0132869, -0.0233307, -0.032246, -0.0386519, -0.0410867, -0.0384232, -0.0302761, -0.0172579, -0.0009873, 0.0161678, 0.0315492, 0.0427513, 0.0481026, 0.0469639, 0.0397716, 0.0278347, 0.012974, -0.0028688, -0.0179336, -0.0307907, -0.040349, -0.0457926, -0.0465294, -0.0422133, -0.0328549, -0.0189903, -0.0018303, 0.0166978, 0.0341004, 0.0476805, 0.0550584, 0.0547184, 0.0464165, 0.031316, 0.0117985, -0.009002, -0.0278264, -0.0419232, -0.0495366, -0.0501406, -0.0443772, -0.0337416, -0.0201368, -0.0054474, 0.0087423, 0.0212411, 0.0311917, 0.0379511, 0.0410004, 0.0399441, 0.0346075, 0.0252004, 0.0124749, -0.002206, -0.0169679, -0.0297102, -0.0385226, -0.0421082, -0.0400734, -0.0329933, -0.0222366, -0.0096176, 0.0030192, 0.0141641, 0.0228665, 0.0287647, 0.0319308, 0.0326065, 0.0309539, 0.0269367, 0.0203924, 0.0112587, -0.0001508, -0.0129497, -0.0256569, -0.0363789, -0.0431696, -0.0444807, -0.0395834, -0.028833, -0.0136768, 0.0036139, 0.0204151, 0.0342813, 0.0433856, 0.0467659, 0.0443525, 0.0368214, 0.0253497, 0.0113573, -0.0036927, -0.0183938, -0.0314032, -0.0414165, -0.0471896, -0.0476743, -0.042262, -0.0310594, -0.0150824, 0.0037358, 0.0227619, 0.0390869, 0.0500684, 0.0538731, 0.0498831, 0.0388458, 0.0227141, 0.0042099, -0.0137655, -0.0287023, -0.0388889, -0.0435746, -0.0428946, -0.03763, -0.0289018, -0.0178987, -0.0057125, 0.0066921, 0.0184029, 0.0284617, 0.035835, 0.0395138, 0.0387296, 0.0332135, 0.023391, 0.010427, -0.0039157, -0.0175817, -0.0286201, -0.0355736, -0.0377491, -0.0352951, -0.0290617, -0.0202993, -0.010308, -0.0001608, 0.0094152, 0.0179864, 0.0252463, 0.0308244, 0.0341702, 0.0345732, 0.0313381, 0.0240654, 0.0129466, -0.0010475, -0.0161829, -0.0302129, -0.0408143, -0.0460904, -0.0449985, -0.0375845, -0.0249686, -0.0090959, 0.0076722, 0.0230136, 0.0350306, 0.0424818, 0.0448282, 0.0421355, 0.0349075, 0.0239408, 0.0102515, -0.0049269, -0.0201223, -0.0336489, -0.0437338, -0.0487614, -0.0475969, -0.0399115, -0.0264007, -0.0088071, 0.010304, 0.0279881, 0.0414907, 0.0487952, 0.0490073, 0.0424786, 0.0306472, 0.0156586, -0.0001211, -0.0145638, -0.0261016, -0.0338521, -0.0375551, -0.0373852, -0.0337387, -0.0270863, -0.0179405, -0.0069277, 0.0051036, 0.017019, 0.0274501, 0.0349751, 0.038394, 0.0370191, 0.0308895, 0.0208309, 0.0083275, -0.004773, -0.0166421, -0.02583, -0.0314938, -0.0334401, -0.0319926, -0.0277571, -0.0213864, -0.013428, -0.004302, 0.0055988, 0.0157452, 0.0253253, 0.0332078, 0.0380666, 0.0386624, 0.0342063, 0.0246818, 0.0110098, -0.0050182, -0.0210334, -0.0345402, -0.0434088, -0.0462902, -0.0428524, -0.0337883, -0.0206032, -0.0052607, 0.0102026, 0.0239868, 0.0346997, 0.041396, 0.0435376, 0.040942, 0.0337673, 0.0225471, 0.0082489, -0.0076876, -0.0233967, -0.0367615, -0.0457258, -0.0486957, -0.044925, -0.0347626, -0.019667, -0.0019668, 0.0155818, 0.0303279, 0.0402228, 0.0441647, 0.0421169, 0.0349841, 0.0242972, 0.0118132, -0.0008446, -0.0124069, -0.0220094, -0.0291117, -0.0333664, -0.0345187, -0.0323881, -0.0269403, -0.0184178, -0.0074682, 0.0047994, 0.0168889, 0.027139, 0.0340444, 0.0365844, 0.0344502, 0.0280979, 0.0186174, 0.0074664, -0.0038459, -0.0140362, -0.0222155, -0.0279167, -0.0309932, -0.0314504, -0.0292965, -0.0244935, -0.0170392, -0.007146, 0.00457, 0.0169864, 0.0285285, 0.0373926, 0.0418975, 0.0408804, 0.0340283, 0.0220451, 0.0065866, -0.0100354, -0.0253048, -0.0369886, -0.0435294, -0.0442555, -0.0393876, -0.029877, -0.0171465, -0.0028182, 0.0115081, 0.0243889, 0.0345862, 0.0410762, 0.0430739, 0.0401167, 0.0322033, 0.0199401, 0.0046154, -0.0118659, -0.0272133, -0.0391196, -0.0456932, -0.0458523, -0.0395751, -0.0279303, -0.0128661, 0.0031953, 0.0178517, 0.0291708, 0.0359834, 0.0379657, 0.0355209, 0.029533, 0.021084, 0.0112236, 0.0008494, -0.0092938, -0.0185326, -0.026171, -0.0314562, -0.0336486, -0.0321877, -0.0268983, -0.0181525, -0.0069146, 0.0053653, 0.0169964, 0.026367, 0.0322543, 0.0340408, 0.031777, 0.0260853, 0.0179449, 0.0084386, -0.0014538, -0.0109518, -0.0194533, -0.0264347, -0.0313444, -0.0335628, -0.0324709, -0.0276239, -0.0189785, -0.0070908, 0.0068008, 0.0208699, 0.0329797, 0.0410914, 0.0437012, 0.0401858, 0.0309598, 0.0174027, 0.0015769, -0.0141796, -0.0276825, -0.0372495, -0.0418992, -0.0413857, -0.0360934, -0.0268612, -0.0148051, -0.0011911, 0.0126293, 0.025252, 0.0352715, 0.041393, 0.0426147, 0.0384486, 0.0291156, 0.0156399, -0.0002124, -0.0161726, -0.0298529, -0.0391952, -0.0428757, -0.0405541, -0.0329022, -0.0214082, -0.0080177, 0.0052797, 0.0168118, 0.0254496, 0.0306641, 0.0324338, 0.031063, 0.0269914, 0.0206695, 0.0125388, 0.0031033, -0.0069643, -0.0167587, -0.0251766, -0.0310663, -0.0334535, -0.0317819, -0.0260897, -0.0170617, -0.0059326, 0.0057435, 0.0163978, 0.0247371, 0.0299398, 0.0317075, 0.0301855, 0.0258021, 0.0191076, 0.0106704, 0.0010617, -0.0090874, -0.0189992, -0.0276949, -0.0340307, -0.0368521, -0.0352502, -0.0288515, -0.0180424, -0.0040381, 0.0112511, 0.0255369, 0.0365816, 0.0426327, 0.0427601, 0.0370172, 0.0263923, 0.0125716, -0.002416, -0.0165701, -0.028216, -0.03617, -0.0397768, -0.0388601, -0.0336449, -0.0246943, -0.0128788, 0.0006377, 0.0144231, 0.026858, 0.0362958, 0.0413075, 0.0409659, 0.035095, 0.0243955, 0.0103889, -0.0048304, -0.018979, -0.0300139, -0.0365033, -0.0378511, -0.0343297, -0.0269244, -0.0170422, -0.0061769, 0.00437, 0.0136435, 0.021046, 0.0262435, 0.0290374, 0.0292729, 0.0268272, 0.02168, 0.0140358, 0.0044413, -0.0061665, -0.0165225, -0.0252192, -0.0309731, -0.0328971, -0.0306964, -0.0247241, -0.0158899, -0.0054518, 0.0052388, 0.0149678, 0.0228094, 0.0281721, 0.0307517, 0.0304363, 0.0272266, 0.0212178, 0.0126637, 0.0020891, -0.0096109, -0.0211535, -0.0309793, -0.0374911, -0.0393744, -0.0359197, -0.0272572, -0.0144249, 0.0007646, 0.0160406, 0.0291007, 0.038041, 0.0416845, 0.0397356, 0.0327439, 0.0219156, 0.0088363, -0.0048085, -0.0174599, -0.0278221, -0.0349182, -0.0380933, -0.037015, -0.0316986, -0.0225617, -0.010478, 0.0032213, 0.0168414, 0.028532, 0.0365825, 0.0397444, 0.0374997, 0.0301967, 0.0190086, 0.0057125, -0.0076617, -0.0192236, -0.0275574, -0.0319182, -0.0322532, -0.0290674, -0.0231955, -0.0155646, -0.0070196, 0.0017489, 0.0101776, 0.0177447, 0.0238873, 0.0279784, 0.0293931, 0.0276552, 0.0226175, 0.014605, 0.0044593, -0.0065498, -0.0169321, -0.0252523, -0.0303981, -0.0317699, -0.0293479, -0.0236313, -0.0154793, -0.0059145, 0.0040507, 0.0135148, 0.021708, 0.0279578, 0.031649, 0.0322324, 0.0293062, 0.0227579, 0.0129206, 0.0006758, -0.0125538, -0.0249562, -0.0346155, -0.0398767, -0.0397021, -0.0339229, -0.0233123, -0.0094525, 0.0055677, 0.0195479, 0.0305662, 0.0372745, 0.039049, 0.035987, 0.0287769, 0.0185028, 0.0064466, -0.0060663, -0.0177591, -0.027452, -0.034108, -0.0369074, -0.0353631, -0.029451, -0.0197116, -0.0072694, 0.0062647, 0.0190119, 0.0291359, 0.0351808, 0.0363522, 0.0326645, 0.0249136, 0.0144763, 0.0030028, -0.0079167, -0.0170194, -0.0235173, -0.0271062, -0.027863, -0.0260856, -0.0221423, -0.016386, -0.0091597, -0.0008729, 0.0079, 0.0163651, 0.0235465, 0.0284236, 0.0301353, 0.0281941, 0.0226414, 0.0140964, 0.0036731, -0.0072154, -0.0171181, -0.0248022, -0.029435, -0.0306546, -0.0285295, -0.0234509, -0.0160086, -0.0069013, 0.0031015, 0.0131525, 0.0223101, 0.0295516, 0.0338519, 0.0343393, 0.0305045, 0.0224048, 0.0107892, -0.0029182, -0.0168, -0.0287656, -0.0369387, -0.0400194, -0.0375345, -0.0299181, -0.0184062, -0.0047752, 0.0090035]}
\ No newline at end of file
packages/core/test/generate.test.tsadded+132−0View file
@@ -0,0 +1,132 @@
1+import { describe, expect, it } from "vitest";
2+import {
3+ LIBRARY_FILES,
4+ METHODS,
5+ RR_METHOD,
6+ SCENARIO_PRESETS,
7+ buildPlanBundle,
8+ buildRunBundle,
9+ formatNumber,
10+ generateParamsBlock,
11+ isPlainStruct,
12+ modesKey,
13+ parseMethodPlan,
14+ validateSpec,
15+ withEdits,
16+} from "../src/index";
17+import { runner, testSpec } from "./helpers";
18+
19+describe("source generation and editing", () => {
20+ it("formats numbers as exact, readable MATLAB literals", () => {
21+ expect(formatNumber(200e9)).toBe("200e9");
22+ expect(formatNumber(0.002)).toBe("0.002");
23+ expect(formatNumber(96000)).toBe("96000");
24+ expect(formatNumber(1e-3)).toBe("0.001");
25+ expect(formatNumber(2.5e-7)).toBe("250e-9");
26+ expect(Number(formatNumber(68.9e9))).toBe(68.9e9);
27+ expect(formatNumber(-3)).toBe("-3");
28+ });
29+
30+ it("round-trips the spec through the params block", async () => {
31+ const spec = testSpec();
32+ const block = generateParamsBlock(spec);
33+ const run = await runner.run({
34+ bundle: {
35+ files: [{ name: "p.m", source: block }, ...LIBRARY_FILES],
36+ entry: "p.m",
37+ resultVariable: "params",
38+ },
39+ });
40+ expect(run.ok).toBe(true);
41+ if (!run.ok || !isPlainStruct(run.value)) return;
42+ const plate = run.value.fields.plate;
43+ const mat = run.value.fields.material;
44+ const hammer = run.value.fields.hammer;
45+ if (!isPlainStruct(plate) || !isPlainStruct(mat) || !isPlainStruct(hammer))
46+ throw new Error("params fields are not structs");
47+ expect(plate.fields.thickness).toBe(spec.plate.thickness);
48+ expect(mat.fields.E).toBe(spec.material.E);
49+ expect(mat.fields.name).toBe(spec.material.name);
50+ expect(hammer.fields.restitution).toBe(spec.hammer.restitution);
51+ });
52+
53+ it("plans every scenario with both methods", async () => {
54+ for (const sc of SCENARIO_PRESETS)
55+ for (const m of METHODS) {
56+ const run = await runner.run({ bundle: buildPlanBundle(sc.spec, m) });
57+ expect(run.ok, `${sc.id}/${m.id}: ${run.ok ? "" : run.error}`).toBe(true);
58+ if (!run.ok) continue;
59+ const plan = parseMethodPlan(run.value);
60+ expect(plan.method).toBe(m.id);
61+ expect(plan.modes_expected).toBeGreaterThan(0);
62+ expect(plan.pendulum_hz).toBeGreaterThan(0);
63+ expect(plan.hertz_duration).toBeGreaterThan(0);
64+ expect(plan.work).toBeGreaterThan(0);
65+ }
66+ });
67+
68+ it("runs the edited source, and surfaces numbl errors with file and line", async () => {
69+ const spec = testSpec();
70+ const bundle = buildRunBundle(spec, RR_METHOD);
71+ const main = bundle.files.find((f) => f.name === "main.m")!.source;
72+ const edited = withEdits(bundle, {
73+ "main.m": main.replace(
74+ "modes = plate_rr_modes(params);",
75+ "modes = undefined_function_xyz(params);",
76+ ),
77+ });
78+ const run = await runner.run({ bundle: edited });
79+ expect(run.ok).toBe(false);
80+ if (run.ok) return;
81+ expect(run.error).toMatch(/undefined_function_xyz/);
82+ expect(run.error).toMatch(/main\.m/);
83+ });
84+
85+ it("reports a missing result variable rather than an empty screen", async () => {
86+ const spec = testSpec();
87+ const bundle = buildRunBundle(spec, RR_METHOD);
88+ const edited = withEdits(bundle, { "main.m": "x = 1;" });
89+ const run = await runner.run({ bundle: edited });
90+ expect(run.ok).toBe(false);
91+ if (!run.ok) expect(run.error).toMatch(/result/);
92+ });
93+
94+ it("validates obviously bad specs, and accepts every scenario", () => {
95+ const spec = testSpec();
96+ spec.probe.x = 10;
97+ spec.hammer.restitution = 1.5;
98+ spec.hole.edge_offset = 0.1;
99+ const issues = validateSpec(spec);
100+ expect(issues.some((i) => i.path === "probe" && i.severity === "error")).toBe(true);
101+ expect(issues.some((i) => i.path === "hammer.restitution")).toBe(true);
102+ expect(issues.some((i) => i.path === "hole.edge_offset")).toBe(true);
103+ for (const sc of SCENARIO_PRESETS)
104+ expect(
105+ validateSpec(sc.spec).filter((i) => i.severity === "error"),
106+ sc.id,
107+ ).toEqual([]);
108+ });
109+
110+ it("keys the modes on what they depend on, and nothing else", () => {
111+ const a = testSpec();
112+ const b = testSpec();
113+ b.strike.x = 0.005;
114+ b.hammer.mass = 0.02;
115+ b.material.Q = 500;
116+ b.thread.dashpot = 0;
117+ b.mic.distance = 1;
118+ b.render.duration = 1;
119+ expect(modesKey(a, "rr")).toBe(modesKey(b, "rr"));
120+ expect(modesKey(a, "fem")).toBe(modesKey(b, "fem"));
121+ const c = testSpec();
122+ c.plate.thickness = 0.002;
123+ expect(modesKey(a, "rr")).not.toBe(modesKey(c, "rr"));
124+ const d = testSpec();
125+ d.fem.mesh_x = 32;
126+ expect(modesKey(a, "rr")).toBe(modesKey(d, "rr"));
127+ expect(modesKey(a, "fem")).not.toBe(modesKey(d, "fem"));
128+ const e = testSpec();
129+ e.render.fs = 48000;
130+ expect(modesKey(a, "rr")).not.toBe(modesKey(e, "rr"));
131+ });
132+});
packages/core/test/helpers.tsadded+162−0View file
@@ -0,0 +1,162 @@
1+import { readFileSync } from "node:fs";
2+import { fileURLToPath } from "node:url";
3+import path from "node:path";
4+import { NodeRunner } from "../src/node";
5+import {
6+ LIBRARY_FILES,
7+ buildModesBundle,
8+ buildRunBundle,
9+ buildStrikeBundle,
10+ cloneSpec,
11+ generateParamsBlock,
12+ isPlainStruct,
13+ methodById,
14+ parsePlateResult,
15+ scenarioPreset,
16+ type MethodId,
17+ type PlainStruct,
18+ type PlainValue,
19+ type PlateResult,
20+ type ProblemSpec,
21+ type RunResult,
22+} from "../src/index";
23+
24+export const runner = new NodeRunner();
25+
26+/**
27+ * The reference problem: the centre hit of the two Python simulators, with a
28+ * short render (the reference WAV excerpts are 50 ms) and the finite-element
29+ * mesh set per test. The strike point and the mesh are what the tests vary.
30+ */
31+export function testSpec(scenario = "center-hit"): ProblemSpec {
32+ const s = cloneSpec(scenarioPreset(scenario).spec);
33+ s.render.duration = 0.1;
34+ s.render.nframes = 6;
35+ s.render.frame_span = 0.001;
36+ s.fem.mesh_x = 16;
37+ s.fem.mesh_y = 16;
38+ return s;
39+}
40+
41+/** Run a method as its two stages and return the typed result with the raw modes. */
42+export async function solve(
43+ spec: ProblemSpec,
44+ method: MethodId,
45+): Promise<{ modes: PlainValue; result: PlateResult; modesRun: RunResult; strikeRun: RunResult }> {
46+ const m = methodById(method);
47+ const modesRun = await runner.run({ bundle: buildModesBundle(spec, m) });
48+ if (!modesRun.ok)
49+ throw new Error(`${method} modes failed: ${modesRun.error}\n${modesRun.output.join("")}`);
50+ const strikeRun = await runner.run({
51+ bundle: buildStrikeBundle(spec, m),
52+ inputs: { modes: modesRun.value },
53+ });
54+ if (!strikeRun.ok)
55+ throw new Error(`${method} strike failed: ${strikeRun.error}\n${strikeRun.output.join("")}`);
56+ return { modes: modesRun.value, result: parsePlateResult(strikeRun.value), modesRun, strikeRun };
57+}
58+
59+/** Run a method as one script (the generated main.m). */
60+export async function solveSingle(spec: ProblemSpec, method: MethodId): Promise<PlateResult> {
61+ const run = await runner.run({ bundle: buildRunBundle(spec, methodById(method)) });
62+ if (!run.ok) throw new Error(`${method} failed: ${run.error}\n${run.output.join("")}`);
63+ return parsePlateResult(run.value);
64+}
65+
66+/** Run a custom script after the params block and return the struct it leaves in `out`. */
67+export async function runScript(spec: ProblemSpec, body: string): Promise<PlainStruct> {
68+ const main = [generateParamsBlock(spec), "", body, ""].join("\n");
69+ const run = await runner.run({
70+ bundle: {
71+ files: [{ name: "script.m", source: main }, ...LIBRARY_FILES],
72+ entry: "script.m",
73+ resultVariable: "out",
74+ },
75+ });
76+ if (!run.ok) throw new Error(`script failed: ${run.error}\n${run.output.join("")}`);
77+ if (!isPlainStruct(run.value)) throw new Error("script did not leave a struct in out");
78+ return run.value;
79+}
80+
81+export function scalar(s: PlainStruct, name: string): number {
82+ const v: PlainValue | undefined = s.fields[name];
83+ if (typeof v === "number") return v;
84+ if (typeof v === "boolean") return v ? 1 : 0;
85+ if (v && typeof v === "object" && v.kind === "tensor" && v.data.length >= 1) return v.data[0];
86+ throw new Error(`${name} is not a scalar`);
87+}
88+
89+export function vector(s: PlainStruct, name: string): Float64Array {
90+ const v: PlainValue | undefined = s.fields[name];
91+ if (typeof v === "number") return Float64Array.of(v);
92+ if (v && typeof v === "object" && v.kind === "tensor") return v.data;
93+ throw new Error(`${name} is not a vector`);
94+}
95+
96+export function relL2(a: ArrayLike<number>, b: ArrayLike<number>): number {
97+ let num = 0;
98+ let den = 0;
99+ for (let i = 0; i < a.length; i++) {
100+ num += (a[i] - b[i]) ** 2;
101+ den += a[i] ** 2;
102+ }
103+ return Math.sqrt(num / Math.max(den, 1e-300));
104+}
105+
106+export function maxAbs(a: ArrayLike<number>): number {
107+ let m = 0;
108+ for (let i = 0; i < a.length; i++) m = Math.max(m, Math.abs(a[i]));
109+ return m;
110+}
111+
112+// ---- the reference diagnostics of the Python simulators (sim-docs)
113+
114+export interface ReferenceMode {
115+ mode: number;
116+ frequency_hz: number;
117+ q_total: number;
118+ hit_participation: number;
119+ radiation_participation: number;
120+ support_participation?: number;
121+}
122+
123+export interface Reference {
124+ model: string;
125+ derived: Record<string, number>;
126+ modes: ReferenceMode[];
127+}
128+
129+const fixtures = path.join(path.dirname(fileURLToPath(import.meta.url)), "fixtures");
130+
131+export function reference(name: string): Reference {
132+ return JSON.parse(readFileSync(path.join(fixtures, `${name}.json`), "utf8")) as Reference;
133+}
134+
135+/** The first 50 ms of the reference WAV, 24-bit samples as fractions of full scale. */
136+export function referenceWav(name: string): { fs: number; samples: number[] } {
137+ return JSON.parse(readFileSync(path.join(fixtures, `${name}_wav.json`), "utf8")) as {
138+ fs: number;
139+ samples: number[];
140+ };
141+}
142+
143+/** Frequencies of the audible modes of a result, in order. */
144+export function audibleFreqs(r: PlateResult): number[] {
145+ return Array.from(r.modes.freq).filter((_, k) => r.modes.audible[k]);
146+}
147+
148+/** Indices of the audible modes of a result whose neighbours are at least `gap` Hz away (no degeneracy). */
149+export function isolatedAudible(r: PlateResult, gap = 5): number[] {
150+ const f = r.modes.freq;
151+ const out: number[] = [];
152+ for (let k = 0; k < f.length; k++) {
153+ if (!r.modes.audible[k]) continue;
154+ const near =
155+ (k > 0 && Math.abs(f[k] - f[k - 1]) < gap) ||
156+ (k + 1 < f.length && Math.abs(f[k + 1] - f[k]) < gap);
157+ if (!near) out.push(k);
158+ }
159+ return out;
160+}
161+
162+export { cloneSpec };
packages/core/test/rr.test.tsadded+121−0View file
@@ -0,0 +1,121 @@
1+import { describe, expect, it } from "vitest";
2+import {
3+ audibleFreqs,
4+ isolatedAudible,
5+ maxAbs,
6+ reference,
7+ referenceWav,
8+ solve,
9+ solveSingle,
10+ testSpec,
11+} from "./helpers";
12+
13+/**
14+ * The Rayleigh-Ritz method against the diagnostics of aluminum_plate_sim.py
15+ * (sim-docs): the same basis, quadrature, hole correction, thread spring,
16+ * damping, pulse and radiation model, so the numbers should agree to
17+ * rounding, and the first 50 ms of the peak-normalized sound to the 24-bit
18+ * quantization of the reference WAV.
19+ */
20+describe("Rayleigh-Ritz against the reference simulator", () => {
21+ it("reproduces the centre hit: modes, contact, sound", async () => {
22+ const ref = reference("rr_center");
23+ const { result } = await solve(testSpec("center-hit"), "rr");
24+ const f = audibleFreqs(result);
25+ expect(f.length).toBe(ref.modes.length);
26+ ref.modes.forEach((m, k) => {
27+ expect(Math.abs(f[k] - m.frequency_hz) / m.frequency_hz).toBeLessThan(1e-7);
28+ });
29+ // damping and participations, on modes that are not part of a degenerate pair
30+ for (const k of isolatedAudible(result)) {
31+ const m = ref.modes[k];
32+ expect(result.modes.q[k]).toBeCloseTo(m.q_total, 3);
33+ expect(Math.abs(result.modes.hit[k])).toBeCloseTo(Math.abs(m.hit_participation), 5);
34+ expect(Math.abs(result.modes.radiation[k])).toBeCloseTo(
35+ Math.abs(m.radiation_participation),
36+ 8,
37+ );
38+ }
39+ const c = result.contact;
40+ expect(c.tc).toBeCloseTo(ref.derived.contact_duration_s, 12);
41+ expect(c.impulse).toBeCloseTo(ref.derived.impact_impulse_Ns, 10);
42+ expect(c.peak_force).toBeCloseTo(ref.derived.estimated_peak_contact_force_N, 6);
43+ expect(result.info.critical_frequency).toBeCloseTo(ref.derived.critical_frequency_hz, 6);
44+ expect(result.info.plate_mass).toBeCloseTo(ref.derived.plate_mass_kg, 12);
45+ // the sound: the peak of the reference lies in the first 50 ms, so a 100 ms render normalizes alike
46+ const wav = referenceWav("rr_center");
47+ expect(result.audio.fs).toBe(wav.fs);
48+ const a = result.audio.p;
49+ const g = result.audio.gain;
50+ let worst = 0;
51+ for (let i = 0; i < wav.samples.length; i++)
52+ worst = Math.max(worst, Math.abs(a[i] * g - wav.samples[i]));
53+ expect(worst).toBeLessThan(1e-4);
54+ expect(result.audio.peak_pa).toBeCloseTo(ref.derived.unnormalized_peak_pressure_pa_approx, 6);
55+ });
56+
57+ it("reproduces the off-centre hit, which wakes the asymmetric modes", async () => {
58+ const ref = reference("rr_off");
59+ const { result } = await solve(testSpec("off-center-hit"), "rr");
60+ // the modes are the same (the strike does not change them) ...
61+ const f = audibleFreqs(result);
62+ ref.modes.forEach((m, k) => {
63+ expect(Math.abs(f[k] - m.frequency_hz) / m.frequency_hz).toBeLessThan(1e-7);
64+ });
65+ // ... but the lowest mode, silent at the centre, now takes part
66+ expect(Math.abs(result.modes.hit[0])).toBeGreaterThan(1);
67+ for (const k of isolatedAudible(result))
68+ expect(Math.abs(result.modes.hit[k])).toBeCloseTo(
69+ Math.abs(ref.modes[k].hit_participation),
70+ 5,
71+ );
72+ const wav = referenceWav("rr_off");
73+ const a = result.audio.p;
74+ const g = result.audio.gain;
75+ let worst = 0;
76+ for (let i = 0; i < wav.samples.length; i++)
77+ worst = Math.max(worst, Math.abs(a[i] * g - wav.samples[i]));
78+ expect(worst).toBeLessThan(1e-4);
79+ });
80+
81+ it("is stable in the basis order: order 8 moves the first ten modes by less than 0.05 Hz", async () => {
82+ const ref = reference("rr_center_o8");
83+ const spec = testSpec();
84+ spec.rr.basis_order = 8;
85+ spec.rr.quadrature_order = 20;
86+ const { result } = await solve(spec, "rr");
87+ const f = audibleFreqs(result);
88+ ref.modes.slice(0, 10).forEach((m, k) => {
89+ expect(Math.abs(f[k] - m.frequency_hz)).toBeLessThan(0.05);
90+ });
91+ });
92+
93+ it("gives the same result as two stages and as one script", async () => {
94+ const spec = testSpec();
95+ spec.render.duration = 0.02;
96+ const [staged, single] = await Promise.all([solve(spec, "rr"), solveSingle(spec, "rr")]);
97+ expect(maxAbs(staged.result.audio.p)).toBeGreaterThan(0);
98+ let worst = 0;
99+ for (let i = 0; i < single.audio.p.length; i++)
100+ worst = Math.max(worst, Math.abs(single.audio.p[i] - staged.result.audio.p[i]));
101+ expect(worst).toBeLessThan(1e-12 * maxAbs(single.audio.p));
102+ expect(single.frames.w.data.length).toBe(staged.result.frames.w.data.length);
103+ });
104+
105+ it("carries the mode shapes, curvatures and frames the views draw", async () => {
106+ const spec = testSpec();
107+ spec.render.duration = 0.02;
108+ const { result } = await solve(spec, "rr");
109+ const g = spec.rr.grid;
110+ expect(result.grid.x.length).toBe(g);
111+ expect(result.modes.shapes.nx).toBe(g);
112+ expect(result.modes.shapes.nf).toBe(result.modes.n);
113+ expect(result.frames.w.nf).toBe(spec.render.nframes);
114+ expect(result.frames.vm.nf).toBe(spec.render.nframes);
115+ expect(maxAbs(result.frames.w.data)).toBeGreaterThan(0);
116+ expect(maxAbs(result.envelope.vm.data)).toBeGreaterThan(0);
117+ expect(result.probe.w.length).toBe(result.t.length);
118+ expect(result.info.n_rigid).toBe(0);
119+ expect(result.contact.hammer_v.length).toBe(0);
120+ });
121+});
packages/core/test/wav.test.tsadded+56−0View file
@@ -0,0 +1,56 @@
1+import { describe, expect, it } from "vitest";
2+import { encodeWav } from "../src/audio/wav";
3+
4+const str = (b: Uint8Array, off: number, n: number) =>
5+ String.fromCharCode(...b.subarray(off, off + n));
6+
7+describe("encodeWav", () => {
8+ it("writes a well-formed 24-bit mono PCM header by default", () => {
9+ const b = encodeWav(new Float32Array(10), 96000);
10+ const v = new DataView(b.buffer);
11+ expect(b.length).toBe(44 + 30);
12+ expect(str(b, 0, 4)).toBe("RIFF");
13+ expect(v.getUint32(4, true)).toBe(b.length - 8);
14+ expect(str(b, 8, 4)).toBe("WAVE");
15+ expect(str(b, 12, 4)).toBe("fmt ");
16+ expect(v.getUint16(20, true)).toBe(1);
17+ expect(v.getUint16(22, true)).toBe(1);
18+ expect(v.getUint32(24, true)).toBe(96000);
19+ expect(v.getUint32(28, true)).toBe(288000);
20+ expect(v.getUint16(32, true)).toBe(3);
21+ expect(v.getUint16(34, true)).toBe(24);
22+ expect(str(b, 36, 4)).toBe("data");
23+ expect(v.getUint32(40, true)).toBe(30);
24+ });
25+
26+ it("quantizes 24-bit samples as the reference simulator does, little-endian two's complement", () => {
27+ const b = encodeWav([0, 1, -1, 0.5, 2, NaN], 8000, 24);
28+ const s = (i: number) => {
29+ const off = 44 + 3 * i;
30+ let x = b[off] | (b[off + 1] << 8) | (b[off + 2] << 16);
31+ if (x >= 1 << 23) x -= 1 << 24;
32+ return x;
33+ };
34+ expect(s(0)).toBe(0);
35+ expect(s(1)).toBe(8388607);
36+ expect(s(2)).toBe(-8388607);
37+ expect(s(3)).toBe(4194304);
38+ expect(s(4)).toBe(8388607);
39+ expect(s(5)).toBe(0);
40+ });
41+
42+ it("still writes 16-bit files when asked", () => {
43+ const b = encodeWav([0, 1, -1, 0.5, 2, -3, NaN, Infinity], 8000, 16);
44+ const v = new DataView(b.buffer);
45+ expect(v.getUint16(34, true)).toBe(16);
46+ const s = (i: number) => v.getInt16(44 + 2 * i, true);
47+ expect(s(0)).toBe(0);
48+ expect(s(1)).toBe(32767);
49+ expect(s(2)).toBe(-32767);
50+ expect(s(3)).toBe(16384);
51+ expect(s(4)).toBe(32767);
52+ expect(s(5)).toBe(-32767);
53+ expect(s(6)).toBe(0);
54+ expect(s(7)).toBe(0);
55+ });
56+});
packages/core/tsconfig.jsonadded+10−0View file
@@ -0,0 +1,10 @@
1+{
2+ "extends": "../../tsconfig.base.json",
3+ "compilerOptions": {
4+ "noEmit": true
5+ },
6+ "include": [
7+ "src",
8+ "test"
9+ ]
10+}
tsconfig.base.jsonadded+17−0View file
@@ -0,0 +1,17 @@
1+{
2+ "compilerOptions": {
3+ "target": "ES2022",
4+ "lib": ["ES2022", "DOM", "DOM.Iterable", "WebWorker"],
5+ "module": "ESNext",
6+ "moduleResolution": "bundler",
7+ "strict": true,
8+ "noUnusedLocals": true,
9+ "noUnusedParameters": true,
10+ "noFallthroughCasesInSwitch": true,
11+ "skipLibCheck": true,
12+ "isolatedModules": true,
13+ "resolveJsonModule": true,
14+ "jsx": "react-jsx",
15+ "types": ["vite/client", "@webgpu/types"]
16+ }
17+}
tsconfig.jsonadded+5−0View file
@@ -0,0 +1,5 @@
1+{
2+ "extends": "./tsconfig.base.json",
3+ "compilerOptions": { "noEmit": true },
4+ "include": ["packages/*/src", "packages/*/test", "apps/*/src", "apps/*/vite.config.ts", "vite.shared.ts", "vitest.config.ts"]
5+}
vite.shared.tsadded+19−0View file
@@ -0,0 +1,19 @@
1+import { readFileSync } from "node:fs";
2+import { fileURLToPath } from "node:url";
3+import path from "node:path";
4+
5+/**
6+ * Vite `define` entries every host needs to run numbl in the browser:
7+ * numbl reads `process` in a few guarded places, and the lab reports numbl's
8+ * package version (its `exports` map does not expose package.json to imports).
9+ */
10+export function numblDefine(): Record<string, string> {
11+ const entry = fileURLToPath(import.meta.resolve("numbl"));
12+ const pkgPath = path.join(path.dirname(entry), "..", "package.json");
13+ const version = (JSON.parse(readFileSync(pkgPath, "utf8")) as { version: string }).version;
14+ return {
15+ "process.platform": JSON.stringify("browser"),
16+ "process.env": "{}",
17+ __NUMBL_VERSION__: JSON.stringify(version),
18+ };
19+}
vitest.config.tsadded+10−0View file
@@ -0,0 +1,10 @@
1+import { defineConfig } from "vitest/config";
2+import { numblDefine } from "./vite.shared";
3+
4+export default defineConfig({
5+ define: { __NUMBL_VERSION__: numblDefine().__NUMBL_VERSION__ },
6+ test: {
7+ include: ["packages/*/test/**/*.test.ts"],
8+ testTimeout: 120_000,
9+ },
10+});
↑↓ move↵openescclose