Skip to content

Commit 05dead9

Browse files
committed
feat(core): implement FMI 3.0 multi-dimensional array vectorization and slicing
1 parent 27c7d6f commit 05dead9

7 files changed

Lines changed: 427 additions & 113 deletions

File tree

packages/core/src/compiler/context.ts

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,11 @@ import { ModelicaPoParser, ModelicaTranslation } from "./modelica/po.js";
1616
import { ModelicaStoredDefinitionSyntaxNode } from "./modelica/syntax.js";
1717
import { Scope } from "./scope.js";
1818

19+
export interface ModelicaCompilerOptions {
20+
arrayMode?: "scalarize" | "preserve";
21+
fmiVersion?: "2.0" | "3.0";
22+
}
23+
1924
/**
2025
* The compiler context managing file system resources, translations, plugins, and loaded Modelica code.
2126
*/
@@ -95,15 +100,15 @@ export class Context extends Scope {
95100
* @param name - The fully qualified name of the Modelica class to flatten.
96101
* @returns The flattened DAE (Differential Algebraic Equation) output as a string, or null if the class is not found or has errors.
97102
*/
98-
flatten(name: string): string | null {
99-
const dae = this.flattenDAE(name);
103+
flatten(name: string, options?: ModelicaCompilerOptions): string | null {
104+
const dae = this.flattenDAE(name, options);
100105
if (!dae) return null;
101106
const out = new StringWriter();
102107
dae.accept(new ModelicaDAEPrinter(out));
103108
return out.toString();
104109
}
105110

106-
flattenDAE(name: string): ModelicaDAE | null {
111+
flattenDAE(name: string, options?: ModelicaCompilerOptions): ModelicaDAE | null {
107112
const instance = this.query(name);
108113
if (!instance) return null;
109114
const dae = new ModelicaDAE(name ?? instance.name ?? "DAE", instance.description);
@@ -114,7 +119,7 @@ export class Context extends Scope {
114119
) {
115120
dae.classKind = instance.classKind;
116121
}
117-
const flattener = new ModelicaFlattener();
122+
const flattener = new ModelicaFlattener(options);
118123
instance.accept(flattener, ["", dae]);
119124
flattener.generateFlowBalanceEquations(dae);
120125
flattener.foldDAEConstants(dae);

packages/core/src/compiler/modelica/dae.ts

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -259,6 +259,49 @@ export class ModelicaSimpleEquation extends ModelicaEquation {
259259
}
260260
}
261261

262+
export class ModelicaArrayEquation extends ModelicaEquation {
263+
expression1: ModelicaExpression;
264+
expression2: ModelicaExpression;
265+
266+
constructor(expression1: ModelicaExpression, expression2: ModelicaExpression, description?: string | null) {
267+
super(description);
268+
this.expression1 = expression1;
269+
this.expression2 = expression2;
270+
}
271+
272+
override accept<R, A>(visitor: IModelicaDAEVisitor<R, A>, argument?: A): R {
273+
return visitor.visitArrayEquation(this, argument);
274+
}
275+
276+
override get hash(): string {
277+
const hash = createHash("sha256");
278+
hash.update(this.expression1.hash);
279+
hash.update("=array=");
280+
hash.update(this.expression2.hash);
281+
return hash.digest("hex");
282+
}
283+
284+
override get toJSON(): JSONValue {
285+
return {
286+
"@type": "ArrayEquation",
287+
expression1: this.expression1.toJSON,
288+
expression2: this.expression2.toJSON,
289+
description: this.description,
290+
};
291+
}
292+
293+
override get toRDF(): Triple[] {
294+
const id = `_:eq_${this.hash.substring(0, 8)}`;
295+
return [
296+
{ s: id, p: "rdf:type", o: "modelica:ArrayEquation" },
297+
{ s: id, p: "modelica:expression1", o: `_:expr_${this.expression1.hash.substring(0, 8)}` },
298+
{ s: id, p: "modelica:expression2", o: `_:expr_${this.expression2.hash.substring(0, 8)}` },
299+
...this.expression1.toRDF,
300+
...this.expression2.toRDF,
301+
];
302+
}
303+
}
304+
262305
/** Represents a standalone function call as an equation (e.g., `assert(...)`, `Func(2)`). */
263306
export class ModelicaFunctionCallEquation extends ModelicaEquation {
264307
call: ModelicaFunctionCallExpression;
@@ -3604,6 +3647,8 @@ export interface IModelicaDAEVisitor<R, A> {
36043647

36053648
visitSimpleEquation(node: ModelicaSimpleEquation, argument?: A): R;
36063649

3650+
visitArrayEquation(node: ModelicaArrayEquation, argument?: A): R;
3651+
36073652
visitSubscriptedExpression(node: ModelicaSubscriptedExpression, argument?: A): R;
36083653

36093654
visitTupleExpression(node: ModelicaTupleExpression, argument?: A): R;
@@ -3794,6 +3839,11 @@ export abstract class ModelicaDAEVisitor<A> implements IModelicaDAEVisitor<void,
37943839
node.expression2.accept(this, argument);
37953840
}
37963841

3842+
visitArrayEquation(node: ModelicaArrayEquation, argument?: A): void {
3843+
node.expression1.accept(this, argument);
3844+
node.expression2.accept(this, argument);
3845+
}
3846+
37973847
visitStringLiteral(node: ModelicaStringLiteral, argument?: A): void {
37983848
/* no-op */
37993849
}
@@ -3890,6 +3940,14 @@ export class ModelicaDAEPrinter extends ModelicaDAEVisitor<never> {
38903940
this.out.write(";\n");
38913941
}
38923942

3943+
visitArrayEquation(node: ModelicaArrayEquation): void {
3944+
this.out.write(this.indent());
3945+
node.expression1.accept(this);
3946+
this.out.write(" = ");
3947+
node.expression2.accept(this);
3948+
this.out.write(";\n");
3949+
}
3950+
38933951
visitBreakStatement(node: ModelicaBreakStatement): void {
38943952
this.out.write(this.indent() + "break;\n");
38953953
}

0 commit comments

Comments
 (0)