/** * The available models: their MATLAB source, and the metadata the host owns. * * A model's *algorithm* lives in its .m file. Everything around it lives here: * the parameter names the .m may take as arguments, their defaults and slider * ranges, which grid fields to render, and the dealiasing degree. The .m * declares nothing about these — it just names the parameters it wants, and * `CompiledModel` matches each against this table. * * Trimmed from turing-surface: this app ships one model (Schnakenberg, flux * form). The discrete parameter choices the app actually offers live in * src/cache/options.ts; the min/max/step here are only the numeric bounds. * * Naming convention, documented in each .m: * `u`, `v`, ... grid fields the model computes and the app renders * `U`, `V`, ... the corresponding spectral state (uppercase) */ import schnakenbergSource from '../../models/schnakenberg.m?raw'; export type Params = Record; /** A tunable scalar the .m may take as an argument. */ export interface ParamSpec { key: string; label: string; value: number; min: number; max: number; step: number; /** * This parameter is a random seed: its value picks a draw and means nothing * on its own, so the UI offers a button that jumps to another one rather * than a box to type a number into. `min`/`max` still bound what the button * picks. */ reseed?: boolean; } export interface MModel { key: string; label: string; blurb: string; /** Grid fields to render, one panel each. */ species: string[]; /** Spectral state names the .m advances. */ state: string[]; params: ParamSpec[]; /** Polynomial degree of the reaction in the fields, for grid dealiasing. */ pdeg: number; /** Amplitude of the seeded perturbation handed to `init`. */ seedAmp: number; /** MATLAB source — the algorithm itself. */ source: string; } /** Spectral state names follow the grid-field names, uppercased. */ const stateFor = (species: string[]): string[] => species.map((s) => s.toUpperCase()); const schnakenberg: MModel = { key: 'schnakenberg', label: 'Schnakenberg', blurb: 'Turing spots.', species: ['u', 'v'], state: stateFor(['u', 'v']), params: [ { key: 'a', label: 'a', value: 0.1, min: 0.01, max: 0.5, step: 0.01 }, { key: 'b', label: 'b', value: 0.9, min: 0.1, max: 2, step: 0.05 }, { key: 'D1', label: 'D₁', value: 4e-4, min: 1e-5, max: 5e-3, step: 1e-5 }, { key: 'D2', label: 'Dā‚‚', value: 8e-3, min: 1e-4, max: 5e-2, step: 1e-4 }, { key: 'dt', label: 'dt', value: 0.05, min: 0.005, max: 0.5, step: 0.005 }, ], pdeg: 3, seedAmp: 1e-2, source: schnakenbergSource, }; export const mModels: MModel[] = [schnakenberg]; export const mModelByKey = (key: string): MModel | undefined => mModels.find((m) => m.key === key); export const defaultParams = (m: MModel): Params => Object.fromEntries(m.params.map((p) => [p.key, p.value]));