2004981Plate dynamics lab 2: a hammer on a hung plate by Rayleigh-Ritz and by Mindlin-Reissner finite elementsJeremy Magland 1import type { ProblemSpec } from "../spec/types";
2import { formatNumber as n, formatString as s } from "./format";
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 */
8export 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}