Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions bun.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
"url": "git+https://github.com/opencor/webapp.git"
},
"type": "module",
"version": "0.20260611.0",
"version": "0.20260611.1",
"engines": {
"bun": ">=1.2.0"
},
Expand Down Expand Up @@ -69,7 +69,7 @@
"@electron-toolkit/utils": "^4.0.0",
"@tailwindcss/postcss": "^4.3.0",
"@tailwindcss/vite": "^4.3.0",
"@types/node": "^25.9.2",
"@types/node": "^25.9.3",
"@types/plotly.js": "^3.0.10",
"@vitejs/plugin-vue": "^6.0.7",
"@vue/tsconfig": "^0.9.1",
Expand Down
4 changes: 2 additions & 2 deletions src/renderer/bun.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions src/renderer/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@
},
"./style.css": "./dist/opencor.css"
},
"version": "0.20260611.0",
"version": "0.20260611.1",
"libopencorVersion": "0.20260604.0",
"scripts": {
"build": "vite build && bun scripts/generate.version.js",
Expand Down Expand Up @@ -79,7 +79,7 @@
"@biomejs/biome": "^2.4.16",
"@tailwindcss/postcss": "^4.3.0",
"@tailwindcss/vite": "^4.3.0",
"@types/node": "^25.9.2",
"@types/node": "^25.9.3",
"@types/plotly.js": "^3.0.10",
"@vitejs/plugin-vue": "^6.0.7",
"@vue/tsconfig": "^0.9.1",
Expand Down
34 changes: 23 additions & 11 deletions src/renderer/src/common/math.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ const ELEMENTWISE_REGEX = /\.([*/^])/g;
export class Float64ArrayMath {
private readonly math: IMathJsLike;
private readonly compiledExpressions = new Map<string, ICompiledExpression>();
private readonly validatedTokens = new Map<string, true>();

constructor() {
this.math = dependencies._mathJs.create(dependencies._mathJs.all, {});
Expand Down Expand Up @@ -785,10 +786,11 @@ export class Float64ArrayMath {
);
}

// A method to reset the compiled expressions.
// Reset both the compiled expressions and the token validation cache.

resetCompiledExpressions(): void {
this.compiledExpressions.clear();
this.validatedTokens.clear();
}

// A method to compile expressions, normalising element-wise operators so that `.*`, `./`, and `.^` behave like `*`,
Expand Down Expand Up @@ -819,8 +821,6 @@ export class Float64ArrayMath {
// is used for expression results.

evaluate(expression: string, scope: ExpressionScope): MathJsResult {
const tokens = expression.match(/[A-Za-z_$][A-Za-z0-9_$]*/g) ?? [];
const allowedVariables = new Set(Object.keys(scope));
const allowedFunctions = new Set([
// Note: this list must be kept in sync with the functions imported into Math.js in the constructor, as well as
// some constants.
Expand Down Expand Up @@ -870,18 +870,30 @@ export class Float64ArrayMath {
'e',
'pi'
]);
const seenTokens = new Set<string>();

for (const token of tokens) {
if (seenTokens.has(token)) {
continue;
}
// If this expression has already been validated and its scope-allowed variables haven't changed, skip token
// scanning entirely.

const normalisedExpression = this.normaliseExpression(expression);

if (!this.validatedTokens.has(normalisedExpression)) {
const tokens = expression.match(/[A-Za-z_$][A-Za-z0-9_$]*/g) ?? [];
const allowedVariables = new Set(Object.keys(scope));
const seenTokens = new Set<string>();

seenTokens.add(token);
for (const token of tokens) {
if (seenTokens.has(token)) {
continue;
}

seenTokens.add(token);

if (!allowedVariables.has(token) && !allowedFunctions.has(token)) {
throw new MathError(expression, `Unknown symbol ${token}.`);
if (!allowedVariables.has(token) && !allowedFunctions.has(token)) {
throw new MathError(expression, `Unknown symbol ${token}.`);
}
}

this.validatedTokens.set(normalisedExpression, true);
}

return this.compile(expression).evaluate(scope);
Expand Down
16 changes: 14 additions & 2 deletions src/renderer/src/components/views/SimulationExperimentView.vue
Original file line number Diff line number Diff line change
Expand Up @@ -340,14 +340,24 @@ const populateParameters = (
parameters.value.sort((parameter1: string, parameter2: string) => parameter1.localeCompare(parameter2));
};

// Small helper to yield to the UI thread.

const yieldToUi = (): Promise<void> => {
return new Promise((resolve) => {
requestAnimationFrame(() => {
setTimeout(resolve, 0);
});
});
};

// Event handlers.

const onRun = (): void => {
const onRun = async (): Promise<void> => {
// Run either the standard or interactive simulation.

if (!interactiveModeEnabled.value) {
// Run the standard simulation, i.e. run the instance, output the simulation time to the console, and update the
// plot.
// plot. We yield to the UI thread between the simulation run and the plot update to keep the UI responsive.

const simulationTime = standardInstance.run();

Expand Down Expand Up @@ -381,6 +391,8 @@ const onRun = (): void => {
}
});

await yieldToUi();

updatePlot();

return;
Expand Down
Loading
Loading