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 type { MethodDefinition } from "../methods/types";
3import { generateParamsBlock } from "./params";
4import { LIBRARY_FILES, type SourceFile } from "./library";
6/** A complete, self-contained set of `.m` files plus the one to run. */
7export 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}
15export const MAIN_FILE = "main.m";
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 */
23export 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}
36/** Bundle for running the method on a spec in one script. */
37export 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}
45/** Stage 1 of a run: the modes, read back by the host and kept. */
46export 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}
61/** Stage 2 of a run: the strike, with the modes passed in as the workspace variable `modes`. */
62export 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}
77/** Bundle that evaluates the plan: sizes and estimates before a run. */
78export 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}
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 */
94export 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}