|
1 | 1 | import { |
| 2 | + ModelicaArray, |
2 | 3 | ModelicaArrayEquation, |
3 | 4 | ModelicaBinaryExpression, |
4 | 5 | ModelicaBooleanLiteral, |
|
8 | 9 | ModelicaIntegerLiteral, |
9 | 10 | ModelicaNameExpression, |
10 | 11 | ModelicaRealLiteral, |
| 12 | + ModelicaSubscriptedExpression, |
11 | 13 | ModelicaUnaryExpression, |
12 | 14 | } from "./dae.js"; |
13 | 15 | import { ModelicaBinaryOperator, ModelicaUnaryOperator } from "./syntax.js"; |
@@ -126,10 +128,65 @@ export class StaticTapeBuilder { |
126 | 128 | } |
127 | 129 | } |
128 | 130 |
|
| 131 | + // Array expressions: walk each flat element, return last index |
| 132 | + if (expr instanceof ModelicaArray) { |
| 133 | + let lastIdx = this.pushOp({ type: "const", val: 0 }); |
| 134 | + for (const elem of expr.flatElements) { |
| 135 | + lastIdx = this.walk(elem); |
| 136 | + } |
| 137 | + return lastIdx; |
| 138 | + } |
| 139 | + |
| 140 | + // Subscripted expression: x[i] |
| 141 | + if (expr instanceof ModelicaSubscriptedExpression) { |
| 142 | + // If subscript is a literal integer, resolve statically |
| 143 | + if (expr.subscripts.length === 1) { |
| 144 | + const sub = expr.subscripts[0]; |
| 145 | + if (sub instanceof ModelicaIntegerLiteral) { |
| 146 | + const base = expr.base; |
| 147 | + if (base instanceof ModelicaArray) { |
| 148 | + const elem = base.getFlatElement(sub.value - 1); // Modelica is 1-indexed |
| 149 | + if (elem) return this.walk(elem); |
| 150 | + } |
| 151 | + // Named variable with subscript: x[3] → "x[3]" |
| 152 | + const baseName = |
| 153 | + expr.base instanceof ModelicaNameExpression |
| 154 | + ? expr.base.name |
| 155 | + : expr.base && typeof expr.base === "object" && "name" in expr.base |
| 156 | + ? (expr.base as { name: string }).name |
| 157 | + : null; |
| 158 | + if (baseName) return this.pushOp({ type: "var", name: `${baseName}[${sub.value}]` }); |
| 159 | + } |
| 160 | + } |
| 161 | + // Fallback: treat base as a variable if it has a name |
| 162 | + const baseName = |
| 163 | + expr.base instanceof ModelicaNameExpression |
| 164 | + ? expr.base.name |
| 165 | + : expr.base && typeof expr.base === "object" && "name" in expr.base |
| 166 | + ? (expr.base as { name: string }).name |
| 167 | + : null; |
| 168 | + if (baseName) return this.pushOp({ type: "var", name: baseName }); |
| 169 | + } |
| 170 | + |
129 | 171 | // Unrecognized or non-differentiable features default to constant 0 tape node. |
130 | 172 | return this.pushOp({ type: "const", val: 0 }); |
131 | 173 | } |
132 | 174 |
|
| 175 | + /** |
| 176 | + * Walk an array expression element-wise, returning one tape index per flat element. |
| 177 | + * For non-array expressions, returns a single-element array. |
| 178 | + */ |
| 179 | + public walkArray(expr: ModelicaExpression): number[] { |
| 180 | + if (expr instanceof ModelicaArray) { |
| 181 | + const indices: number[] = []; |
| 182 | + for (const elem of expr.flatElements) { |
| 183 | + indices.push(this.walk(elem)); |
| 184 | + } |
| 185 | + return indices; |
| 186 | + } |
| 187 | + return [this.walk(expr)]; |
| 188 | + } |
| 189 | + |
133 | 190 | /** |
134 | 191 | * Emit the C-code evaluating the expressions step-by-step. |
135 | 192 | * `varResolver` maps the Modelica variable name into a valid C-code getter string |
@@ -479,11 +536,24 @@ export function generateModelEvaluateJacobian(id: string, dae: ModelicaDAE, vars |
479 | 536 | // Gather target equations (der(x) = f(x,u)) |
480 | 537 | const derEqs: { state: string; rhs: ModelicaExpression }[] = []; |
481 | 538 | for (const eq of dae.equations) { |
482 | | - if (eq instanceof ModelicaArrayEquation) continue; // Skip unsupported for NLP right now |
483 | 539 | if (!("expression1" in eq && "expression2" in eq)) continue; |
484 | 540 | const se = eq as { expression1: ModelicaExpression; expression2: ModelicaExpression }; |
485 | 541 | const ld = extractDer(se.expression1); |
486 | 542 | const rd = extractDer(se.expression2); |
| 543 | + |
| 544 | + if (eq instanceof ModelicaArrayEquation) { |
| 545 | + const baseName = ld || rd; |
| 546 | + if (!baseName) continue; |
| 547 | + const rhs = ld ? se.expression2 : se.expression1; |
| 548 | + const v = dae.variables.find((dv) => dv.name === baseName); |
| 549 | + const dims = v?.arrayDimensions ?? []; |
| 550 | + const size = dims.length > 0 ? dims.reduce((a: number, b: number) => a * b, 1) : 1; |
| 551 | + for (let i = 0; i < size; i++) { |
| 552 | + derEqs.push({ state: `${baseName}[${i + 1}]`, rhs }); |
| 553 | + } |
| 554 | + continue; |
| 555 | + } |
| 556 | + |
487 | 557 | if (ld) derEqs.push({ state: ld, rhs: se.expression2 }); |
488 | 558 | else if (rd) derEqs.push({ state: rd, rhs: se.expression1 }); |
489 | 559 | } |
|
0 commit comments