// Set-up summary, energy and hit tables, and a short log. import type { SetupSummary } from "../model/setup.ts"; import type { Diagnostics } from "../model/simulator.ts"; const f = (x: number, d = 3) => (Number.isFinite(x) ? x.toFixed(d) : "-"); const e = (x: number) => (Number.isFinite(x) ? x.toExponential(2) : "-"); export function formatSummary(s: SetupSummary): string { const lines = [ `mesh: ${s.nodes} nodes, ${s.elements} triangles, ${s.dofs} DOFs`, `profile: height ${f(s.height * 1e3, 1)} mm, rim warp p-p ${f(s.rimWarp * 1e3, 2)} mm, ${s.dents} dents, h in [${f(s.hMin * 1e3, 2)}, ${f(s.hMax * 1e3, 2)}] mm, max |grad z0| = ${f(s.maxSlope)}${s.slopeWarning ? " (above the shallow-shell bound)" : ""}, mass ${f(s.mass)} kg`, `omega_max: element bound ${e(s.omegaMax)} rad/s (limiting element at r = ${f(s.limitingR)} m, h = ${f(s.limitingH * 1e3, 2)} mm), global ${e(s.omegaGlobal)} rad/s (${s.omegaGlobalIterations} power iterations${s.omegaGlobalConverged ? "" : ", did not converge"}); ${s.boundUsed} bound used`, `dt = ${e(s.dt)} s, ${s.substeps} sub-steps per sample, ${s.nSteps} steps`, ]; s.hits.forEach((h, k) => lines.push( `hit ${k} at t = ${f(h.time)} s: ${h.kind}, ${h.points}-point patch at r = ${f(h.r)} m, k = ${e(h.kTotal)} N/m^${f(h.alpha, 2)}, m = ${f(h.mass)} kg, v = ${f(h.velocity, 1)} m/s, energy ${f(h.energy, 2)} J, free contact ~ ${f(h.freeContact * 1e3, 2)} ms (direct excitation below ~${f(h.directBelow, 0)} Hz)${h.late ? " (after the end of the run)" : ""}`)); lines.push(`set-up took ${f(s.setupSeconds, 1)} s`); return lines.join("\n"); } export class Readouts { private readonly root: HTMLElement; private readonly energies: HTMLTableElement; private readonly hits: HTMLTableElement; private readonly logEl: HTMLDivElement; private lines: string[] = []; constructor(root: HTMLElement) { this.root = root; this.energies = document.createElement("table"); this.hits = document.createElement("table"); this.logEl = document.createElement("div"); this.logEl.className = "log"; root.append(this.energies, this.hits, this.logEl); this.energies.innerHTML = "t [s]kinetic [J]membrane [J]bending [J]exciters [J]total [J]max|w|/hno run yet"; } setDiagnostics(d: Diagnostics, kinds: string[]) { const tot = d.kinetic + d.membrane + d.bending + d.exciters; this.energies.innerHTML = `t [s]kinetic [J]membrane [J]bending [J]exciters [J]total [J]max|w|/h` + `${f(d.time)}${e(d.kinetic)}${e(d.membrane)}${e(d.bending)}${e(d.exciters)}${e(tot)}${f(d.maxWoverH, 2)}`; this.hits.innerHTML = `hitpeak force [N]contact [ms]exciter velocity [m/s]` + d.hits.map((h, k) => `${k} (${kinds[k]})${f(h.peakForce, 1)}${f(h.contactTime * 1e3, 2)}${f(h.velocity, 2)}`).join(""); } log(line: string) { this.lines.push(line); if (this.lines.length > 8) this.lines.shift(); this.logEl.textContent = this.lines.join("\n"); this.root.scrollTop = this.root.scrollHeight; } clearLog() { this.lines = []; this.logEl.textContent = ""; } }