/ proof-of-concept / cymsimweb
Sign in
proof-of-concept / cymsimweb
cymsimweb / src / model / setup.ts
61 lines · 3.9 KBCodeBlameHistory
1e29264cymsimweb: WebGPU companion of cymsim with YAML/form editor, live spectrogram and playbackJeremy Magland 1// Everything the time loop needs, built once from a configuration: profile, mesh, shell, time
2// step, hit schedule and radiation, plus a human-readable summary of the set-up.
3import type { SimulationConfig } from "../config/schema.ts";
4import { buildPatch, buildSchedule, freeContactTime, patchEnergy, type ContactPatch, type HitSchedule } from "./exciter.ts";
5import { CymbalProfile } from "./geometry.ts";
6import { diskMesh, type Mesh } from "./mesh.ts";
7import { buildRadiation, type Radiation } from "./radiation.ts";
8import { buildShell, type Shell } from "./shell.ts";
10export interface Setup {
11 config: SimulationConfig; profile: CymbalProfile; mesh: Mesh; shell: Shell;
12 fs: number; dt: number; substeps: number; nSamples: number; nSteps: number;
13 patches: ContactPatch[]; schedule: HitSchedule; radiation: Radiation;
14 summary: SetupSummary;
17export interface SetupSummary {
18 nodes: number; elements: number; dofs: number; mass: number; height: number; rimWarp: number; dents: number;
19 hMin: number; hMax: number; maxSlope: number; slopeWarning: boolean; omegaMax: number; limitingR: number; limitingH: number;
20 dt: number; substeps: number; nSteps: number; setupSeconds: number;
21 hits: { time: number; kind: string; points: number; r: number; kTotal: number; alpha: number; mass: number; velocity: number;
22 energy: number; freeContact: number; directBelow: number; late: boolean }[];
25/** Build the set-up; ``progress`` is awaited between phases so a UI can repaint. */
26export async function buildSetup(config: SimulationConfig, progress?: (phase: string) => Promise<void> | void): Promise<Setup> {
27 const t0 = performance.now();
28 const step = async (s: string) => { if (progress) await progress(s); };
29 await step("profile and mesh");
30 const profile = new CymbalProfile(config.profile, config.imperfections);
31 const mesh = diskMesh(config.profile.radius, config.mesh.element_size, config.mesh.smoothing);
32 await step(`shell assembly (${mesh.n} nodes, ${mesh.e} elements)`);
33 const shell = buildShell(mesh, profile, config.material, config.numerics);
34 const num = config.numerics, fs = num.fs;
35 const substeps = Math.ceil(1 / (fs * num.cfl_safety * shell.dtStable));
36 const dt = 1 / (fs * substeps);
37 const nSamples = Math.ceil(num.duration * fs);
38 const nSteps = nSamples * substeps;
39 await step("hits and radiation");
40 const patches = config.hits.map((h) => buildPatch(h, mesh, profile));
41 const schedule = buildSchedule(patches, dt);
42 const radiation = buildRadiation(mesh, profile, shell.z0Node, config.listener, fs, substeps);
43 let zMax = -Infinity, rimMin = Infinity, rimMax = -Infinity, hMin = Infinity, hMax = -Infinity;
44 for (const z of shell.z0Node) zMax = Math.max(zMax, z);
45 for (const b of mesh.boundary) { rimMin = Math.min(rimMin, shell.z0Node[b]); rimMax = Math.max(rimMax, shell.z0Node[b]); }
46 for (const h of shell.hE) { hMin = Math.min(hMin, h); hMax = Math.max(hMax, h); }
47 const le = shell.limitingElement;
48 const summary: SetupSummary = {
49 nodes: mesh.n, elements: mesh.e, dofs: 5 * mesh.n, mass: shell.mass, height: zMax, rimWarp: rimMax - rimMin,
50 dents: profile.dentX.length, hMin, hMax, maxSlope: shell.maxSlope, slopeWarning: shell.maxSlope > config.profile.max_slope,
51 omegaMax: shell.omegaMax, limitingR: Math.hypot(mesh.centroid[2 * le], mesh.centroid[2 * le + 1]), limitingH: shell.hE[le],
52 dt, substeps, nSteps, setupSeconds: 0,
53 hits: patches.map((p) => {
54 const tau = freeContactTime(p);
55 return { time: p.time, kind: p.hit.exciter.kind, points: p.x.length, r: p.hit.r, kTotal: p.kTotal, alpha: p.alpha, mass: p.mass,
56 velocity: p.velocity, energy: patchEnergy(p), freeContact: tau, directBelow: 0.5 / tau, late: p.time >= num.duration };
57 }),
58 };
59 summary.setupSeconds = (performance.now() - t0) / 1000;
60 return { config, profile, mesh, shell, fs, dt, substeps, nSamples, nSteps, patches, schedule, radiation, summary };
↑↓ move↵openescclose