de6cf45cymroom: load a .cymsim file and show every part of it in a 3D scene; the room acoustics solver is not written yetJeremy Magland 1// Generate a real .cymsim file with cymsimweb's CPU loop (coarse fixture config, 0.35 s, with
2// the acceleration record and displacement frames), for trying the viewer without a browser run.
3// Requires a cymsimweb checkout beside this repository: node tools/make-test-file.ts out.cymsim
4import fs from "node:fs";
5import { buildSetup } from "../../cymsimweb/src/model/setup.ts";
6import { CpuSimulator } from "../../cymsimweb/src/cpu/simulator.ts";
7import { AccelerationStore, DisplacementStore } from "../../cymsimweb/src/analysis/acceleration.ts";
8import { writeCymsim, type CymsimRun, type H5Module } from "../../cymsimweb/src/io/cymsimFile.ts";
9import { configToYaml } from "../../cymsimweb/src/config/yaml.ts";
10import { dispEveryFor } from "../../cymsimweb/src/model/simulator.ts";
12const h5: H5Module = (await import("h5wasm/node")).default as unknown as H5Module;
13await h5.ready;
15const fixture = JSON.parse(fs.readFileSync(new URL("../../cymsimweb/tests/fixtures/python_reference.json", import.meta.url), "utf8"));
16const cfg = structuredClone(fixture.config);
17cfg.output.record_acceleration = true;
18cfg.output.displacement_rate = 1000;
19cfg.numerics.duration = 0.35;
21const setup = await buildSetup(cfg), N = setup.mesh.n;
22const nSamples = Math.round(0.35 * setup.fs);
23const dispEvery = dispEveryFor(setup.fs, cfg.output.displacement_rate);
24const sim = new CpuSimulator(setup, { recordAcceleration: true, dispEvery });
25const acc = new AccelerationStore(N, setup.fs), disp = new DisplacementStore(N, setup.fs / dispEvery);
26const front: number[] = [], back: number[] = [];
27const energy: number[][] = [];
28for (let done = 0; done < nSamples; done += 1000) {
29 const r = await sim.runChunk(Math.min(1000, nSamples - done));
30 front.push(...r.pressureFront); back.push(...r.pressureBack);
31 acc.push(r.acceleration!, r.pressureFront.length, r.firstSample);
32 r.dispFrames.forEach((w, i) => disp.push(w, r.dispSamples[i] / setup.fs));
33 const d = await sim.diagnostics();
34 energy.push([d.time, d.kinetic, d.membrane, d.bending, d.exciters, d.maxWoverH]);
35}
36const d = await sim.diagnostics(), state = await sim.exportState();
37const { mesh, shell } = setup;
38const run: CymsimRun = {
39 yaml: configToYaml(cfg), config: cfg,
40 mesh: { xy: mesh.xy, z0: shell.z0Node, thickness: shell.hNode, normal: shell.normalNode, triangles: Int32Array.from(mesh.tri), area: mesh.area, boundary: Int32Array.from(mesh.boundary), nNodes: N, nElements: mesh.e },
41 listener: { rFront: setup.radiation.rFront, rBack: setup.radiation.rBack },
42 fs: setup.fs, dt: setup.dt, substeps: setup.substeps, duration: cfg.numerics.duration, nSamples: setup.nSamples, nStepsDone: state.step, complete: true, wallTime: 1.0, decimationSpan: 32,
43 audioBaffled: Float32Array.from(front), audioUnbaffled: Float32Array.from(front.map((f, i) => f - back[i])),
44 acceleration: acc, taps: setup.radiation.taps, displacement: { rate: disp.rate, times: disp.times, frames: disp.frames },
45 energy,
46 hits: d.hits.map((h, k) => ({ time: setup.patches[k].time, kind: setup.patches[k].hit.exciter.kind, peak_force: h.peakForce, contact_time: h.contactTime, rebound_velocity: h.velocity, energy_in: 9.6, energy_out: 0.5 * setup.patches[k].mass * h.velocity ** 2 })),
47 state, creator: "cymsimweb test", created: "",
48};
49const out = process.argv[2] ?? "test.cymsim";
50writeCymsim(h5, out, run, "cymsimweb testgen");
51console.log(`${out}: ${(fs.statSync(out).size / 1048576).toFixed(1)} MB, ${N} nodes, ${nSamples} samples, ${disp.frames.length} displacement frames`);