@sdeverywhere/runtime 0.2.0 → 0.2.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +22 -5
- package/dist/index.cjs +326 -37
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +398 -0
- package/dist/index.d.ts +94 -12
- package/dist/index.js +324 -36
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,13 +1,30 @@
|
|
|
1
1
|
# @sdeverywhere/runtime
|
|
2
2
|
|
|
3
|
-
This package provides a simplified API around a
|
|
4
|
-
|
|
5
|
-
|
|
3
|
+
This package provides a simplified API around a System Dynamics model as generated by [SDEverywhere](https://github.com/climateinteractive/SDEverywhere) and compiled to a WebAssembly (Wasm) module via [Emscripten](https://emscripten.org).
|
|
4
|
+
|
|
5
|
+
## Quick Start
|
|
6
|
+
|
|
7
|
+
The best way to get started with SDEverywhere is to follow the [Quick Start](https://github.com/climateinteractive/SDEverywhere#quick-start) instructions.
|
|
8
|
+
If you follow those instructions, the `@sdeverywhere/runtime` package will be added to your project automatically, in which case you can skip the next section and jump straight to the ["Usage"](#usage) section below.
|
|
9
|
+
|
|
10
|
+
## Install
|
|
11
|
+
|
|
12
|
+
```sh
|
|
13
|
+
# npm
|
|
14
|
+
npm install @sdeverywhere/runtime
|
|
15
|
+
|
|
16
|
+
# pnpm
|
|
17
|
+
pnpm add @sdeverywhere/runtime
|
|
18
|
+
|
|
19
|
+
# yarn
|
|
20
|
+
yarn add @sdeverywhere/runtime
|
|
21
|
+
```
|
|
6
22
|
|
|
7
23
|
## Usage
|
|
8
24
|
|
|
9
|
-
|
|
10
|
-
|
|
25
|
+
_NOTE:_ If you followed the "Quick Start" instructions and/or used the
|
|
26
|
+
`@sdeverywhere/create` package to generate your project, the initialization
|
|
27
|
+
steps listed below are already implemented for you in the generated `core` package,
|
|
11
28
|
and you can work directly with a `ModelRunner` and/or `ModelScheduler` instance.
|
|
12
29
|
|
|
13
30
|
### 1. Initialize the `WasmModel`
|
package/dist/index.cjs
CHANGED
|
@@ -1,7 +1,21 @@
|
|
|
1
1
|
var __defProp = Object.defineProperty;
|
|
2
2
|
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
3
3
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
4
|
+
var __getOwnPropSymbols = Object.getOwnPropertySymbols;
|
|
4
5
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __propIsEnum = Object.prototype.propertyIsEnumerable;
|
|
7
|
+
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
8
|
+
var __spreadValues = (a, b) => {
|
|
9
|
+
for (var prop in b || (b = {}))
|
|
10
|
+
if (__hasOwnProp.call(b, prop))
|
|
11
|
+
__defNormalProp(a, prop, b[prop]);
|
|
12
|
+
if (__getOwnPropSymbols)
|
|
13
|
+
for (var prop of __getOwnPropSymbols(b)) {
|
|
14
|
+
if (__propIsEnum.call(b, prop))
|
|
15
|
+
__defNormalProp(a, prop, b[prop]);
|
|
16
|
+
}
|
|
17
|
+
return a;
|
|
18
|
+
};
|
|
5
19
|
var __export = (target, all) => {
|
|
6
20
|
for (var name in all)
|
|
7
21
|
__defProp(target, name, { get: all[name], enumerable: true });
|
|
@@ -15,39 +29,75 @@ var __copyProps = (to, from, except, desc) => {
|
|
|
15
29
|
return to;
|
|
16
30
|
};
|
|
17
31
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
32
|
+
var __async = (__this, __arguments, generator) => {
|
|
33
|
+
return new Promise((resolve, reject) => {
|
|
34
|
+
var fulfilled = (value) => {
|
|
35
|
+
try {
|
|
36
|
+
step(generator.next(value));
|
|
37
|
+
} catch (e) {
|
|
38
|
+
reject(e);
|
|
39
|
+
}
|
|
40
|
+
};
|
|
41
|
+
var rejected = (value) => {
|
|
42
|
+
try {
|
|
43
|
+
step(generator.throw(value));
|
|
44
|
+
} catch (e) {
|
|
45
|
+
reject(e);
|
|
46
|
+
}
|
|
47
|
+
};
|
|
48
|
+
var step = (x) => x.done ? resolve(x.value) : Promise.resolve(x.value).then(fulfilled, rejected);
|
|
49
|
+
step((generator = generator.apply(__this, __arguments)).next());
|
|
50
|
+
});
|
|
51
|
+
};
|
|
18
52
|
|
|
19
53
|
// src/index.ts
|
|
20
54
|
var src_exports = {};
|
|
21
55
|
__export(src_exports, {
|
|
56
|
+
ModelListing: () => ModelListing,
|
|
22
57
|
ModelScheduler: () => ModelScheduler,
|
|
23
58
|
Outputs: () => Outputs,
|
|
24
59
|
Series: () => Series,
|
|
25
60
|
WasmBuffer: () => WasmBuffer,
|
|
26
61
|
WasmModel: () => WasmModel,
|
|
62
|
+
createFloat64WasmBuffer: () => createFloat64WasmBuffer,
|
|
27
63
|
createInputValue: () => createInputValue,
|
|
64
|
+
createInt32WasmBuffer: () => createInt32WasmBuffer,
|
|
28
65
|
createWasmModelRunner: () => createWasmModelRunner,
|
|
29
66
|
initWasmModelAndBuffers: () => initWasmModelAndBuffers,
|
|
30
67
|
perfElapsed: () => perfElapsed,
|
|
31
|
-
perfNow: () => perfNow
|
|
68
|
+
perfNow: () => perfNow,
|
|
69
|
+
updateOutputIndices: () => updateOutputIndices
|
|
32
70
|
});
|
|
33
71
|
module.exports = __toCommonJS(src_exports);
|
|
34
72
|
|
|
35
73
|
// src/wasm-model/wasm-buffer.ts
|
|
36
74
|
var WasmBuffer = class {
|
|
37
|
-
|
|
75
|
+
/**
|
|
76
|
+
* @param wasmModule The `WasmModule` used to initialize the memory.
|
|
77
|
+
* @param byteOffset The byte offset within the wasm heap.
|
|
78
|
+
* @param heapArray The array view on the underlying heap buffer.
|
|
79
|
+
*/
|
|
80
|
+
constructor(wasmModule, byteOffset, heapArray) {
|
|
38
81
|
this.wasmModule = wasmModule;
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
this.byteOffset = wasmModule._malloc(lengthInBytes);
|
|
42
|
-
const float64Offset = this.byteOffset / sizeOfFloat64;
|
|
43
|
-
this.heapArray = wasmModule.HEAPF64.subarray(float64Offset, float64Offset + numElements);
|
|
82
|
+
this.byteOffset = byteOffset;
|
|
83
|
+
this.heapArray = heapArray;
|
|
44
84
|
}
|
|
85
|
+
/**
|
|
86
|
+
* @return An `ArrType` view on the underlying heap buffer.
|
|
87
|
+
*/
|
|
45
88
|
getArrayView() {
|
|
46
89
|
return this.heapArray;
|
|
47
90
|
}
|
|
91
|
+
/**
|
|
92
|
+
* @return The raw address of the underlying heap buffer.
|
|
93
|
+
* @hidden This is intended for use by `WasmModel` only.
|
|
94
|
+
*/
|
|
48
95
|
getAddress() {
|
|
49
96
|
return this.byteOffset;
|
|
50
97
|
}
|
|
98
|
+
/**
|
|
99
|
+
* Dispose the buffer by freeing the allocated heap memory.
|
|
100
|
+
*/
|
|
51
101
|
dispose() {
|
|
52
102
|
if (this.heapArray) {
|
|
53
103
|
this.wasmModule._free(this.byteOffset);
|
|
@@ -56,9 +106,29 @@ var WasmBuffer = class {
|
|
|
56
106
|
}
|
|
57
107
|
}
|
|
58
108
|
};
|
|
109
|
+
function createInt32WasmBuffer(wasmModule, numElements) {
|
|
110
|
+
const elemSizeInBytes = 4;
|
|
111
|
+
const lengthInBytes = numElements * elemSizeInBytes;
|
|
112
|
+
const byteOffset = wasmModule._malloc(lengthInBytes);
|
|
113
|
+
const elemOffset = byteOffset / elemSizeInBytes;
|
|
114
|
+
const heapArray = wasmModule.HEAP32.subarray(elemOffset, elemOffset + numElements);
|
|
115
|
+
return new WasmBuffer(wasmModule, byteOffset, heapArray);
|
|
116
|
+
}
|
|
117
|
+
function createFloat64WasmBuffer(wasmModule, numElements) {
|
|
118
|
+
const elemSizeInBytes = 8;
|
|
119
|
+
const lengthInBytes = numElements * elemSizeInBytes;
|
|
120
|
+
const byteOffset = wasmModule._malloc(lengthInBytes);
|
|
121
|
+
const elemOffset = byteOffset / elemSizeInBytes;
|
|
122
|
+
const heapArray = wasmModule.HEAPF64.subarray(elemOffset, elemOffset + numElements);
|
|
123
|
+
return new WasmBuffer(wasmModule, byteOffset, heapArray);
|
|
124
|
+
}
|
|
59
125
|
|
|
60
126
|
// src/wasm-model/wasm-model.ts
|
|
127
|
+
var indicesPerOutput = 4;
|
|
61
128
|
var WasmModel = class {
|
|
129
|
+
/**
|
|
130
|
+
* @param wasmModule The `WasmModule` that provides access to the native functions.
|
|
131
|
+
*/
|
|
62
132
|
constructor(wasmModule) {
|
|
63
133
|
function getNumberValue(funcName) {
|
|
64
134
|
const wasmGetValue = wasmModule.cwrap(funcName, "number", []);
|
|
@@ -67,24 +137,59 @@ var WasmModel = class {
|
|
|
67
137
|
this.startTime = getNumberValue("getInitialTime");
|
|
68
138
|
this.endTime = getNumberValue("getFinalTime");
|
|
69
139
|
this.saveFreq = getNumberValue("getSaveper");
|
|
140
|
+
try {
|
|
141
|
+
this.maxOutputIndices = getNumberValue("getMaxOutputIndices");
|
|
142
|
+
} catch (e) {
|
|
143
|
+
this.maxOutputIndices = 0;
|
|
144
|
+
}
|
|
70
145
|
this.numSavePoints = Math.round((this.endTime - this.startTime) / this.saveFreq) + 1;
|
|
71
|
-
this.wasmRunModel = wasmModule.cwrap("runModelWithBuffers", null, ["number", "number"]);
|
|
146
|
+
this.wasmRunModel = wasmModule.cwrap("runModelWithBuffers", null, ["number", "number", "number"]);
|
|
72
147
|
}
|
|
73
|
-
|
|
74
|
-
|
|
148
|
+
/**
|
|
149
|
+
* Run the model, using inputs from the `inputs` buffer, and writing outputs into
|
|
150
|
+
* the `outputs` buffer.
|
|
151
|
+
*
|
|
152
|
+
* @param inputs The buffer containing inputs in the order expected by the model.
|
|
153
|
+
* @param outputs The buffer into which the model will store output values.
|
|
154
|
+
* @param outputIndices The buffer used to control which variables are written to `outputs`.
|
|
155
|
+
*/
|
|
156
|
+
runModel(inputs, outputs, outputIndices) {
|
|
157
|
+
this.wasmRunModel(inputs.getAddress(), outputs.getAddress(), (outputIndices == null ? void 0 : outputIndices.getAddress()) || 0);
|
|
75
158
|
}
|
|
76
159
|
};
|
|
77
160
|
function initWasmModelAndBuffers(wasmModule, numInputs, outputVarIds) {
|
|
78
161
|
const model = new WasmModel(wasmModule);
|
|
79
|
-
const inputsBuffer =
|
|
80
|
-
const
|
|
162
|
+
const inputsBuffer = createFloat64WasmBuffer(wasmModule, numInputs);
|
|
163
|
+
const outputVarCount = Math.max(outputVarIds.length, model.maxOutputIndices);
|
|
164
|
+
const outputsBuffer = createFloat64WasmBuffer(wasmModule, outputVarCount * model.numSavePoints);
|
|
165
|
+
let outputIndicesBuffer;
|
|
166
|
+
if (model.maxOutputIndices > 0) {
|
|
167
|
+
outputIndicesBuffer = createInt32WasmBuffer(wasmModule, model.maxOutputIndices * indicesPerOutput);
|
|
168
|
+
}
|
|
81
169
|
return {
|
|
82
170
|
model,
|
|
83
171
|
inputsBuffer,
|
|
84
172
|
outputsBuffer,
|
|
173
|
+
outputIndicesBuffer,
|
|
85
174
|
outputVarIds
|
|
86
175
|
};
|
|
87
176
|
}
|
|
177
|
+
function updateOutputIndices(indicesArray, outputVarSpecs) {
|
|
178
|
+
var _a;
|
|
179
|
+
if (indicesArray.length < outputVarSpecs.length * indicesPerOutput) {
|
|
180
|
+
throw new Error("Length of indicesArray must be large enough to accommodate the given outputVarSpecs");
|
|
181
|
+
}
|
|
182
|
+
let offset = 0;
|
|
183
|
+
for (const outputVarSpec of outputVarSpecs) {
|
|
184
|
+
const subCount = ((_a = outputVarSpec.subscriptIndices) == null ? void 0 : _a.length) || 0;
|
|
185
|
+
indicesArray[offset + 0] = outputVarSpec.varIndex;
|
|
186
|
+
indicesArray[offset + 1] = subCount > 0 ? outputVarSpec.subscriptIndices[0] : 0;
|
|
187
|
+
indicesArray[offset + 2] = subCount > 1 ? outputVarSpec.subscriptIndices[1] : 0;
|
|
188
|
+
indicesArray[offset + 3] = subCount > 2 ? outputVarSpec.subscriptIndices[2] : 0;
|
|
189
|
+
offset += indicesPerOutput;
|
|
190
|
+
}
|
|
191
|
+
indicesArray.fill(0, offset);
|
|
192
|
+
}
|
|
88
193
|
|
|
89
194
|
// src/model-runner/inputs.ts
|
|
90
195
|
function createInputValue(varId, defaultValue, initialValue) {
|
|
@@ -108,21 +213,43 @@ function createInputValue(varId, defaultValue, initialValue) {
|
|
|
108
213
|
|
|
109
214
|
// src/model-runner/outputs.ts
|
|
110
215
|
var import_neverthrow = require("neverthrow");
|
|
111
|
-
var Series = class {
|
|
216
|
+
var Series = class _Series {
|
|
217
|
+
/**
|
|
218
|
+
* @param varId The ID for the output variable (as used by SDEverywhere).
|
|
219
|
+
* @param points The data points for the variable, one point per time increment.
|
|
220
|
+
*/
|
|
112
221
|
constructor(varId, points) {
|
|
113
222
|
this.varId = varId;
|
|
114
223
|
this.points = points;
|
|
115
224
|
}
|
|
225
|
+
/**
|
|
226
|
+
* Return the Y value at the given time. Note that this does not attempt to interpolate
|
|
227
|
+
* if there is no data point defined for the given time and will return undefined in
|
|
228
|
+
* that case.
|
|
229
|
+
*
|
|
230
|
+
* @param time The x (time) value.
|
|
231
|
+
* @return The y value for the given time, or undefined if there is no data point defined
|
|
232
|
+
* for the given time.
|
|
233
|
+
*/
|
|
116
234
|
getValueAtTime(time) {
|
|
117
235
|
var _a;
|
|
118
236
|
return (_a = this.points.find((p) => p.x === time)) == null ? void 0 : _a.y;
|
|
119
237
|
}
|
|
238
|
+
/**
|
|
239
|
+
* Create a new `Series` instance that is a copy of this one.
|
|
240
|
+
*/
|
|
120
241
|
copy() {
|
|
121
|
-
const pointsCopy = this.points.map((p) => ({
|
|
122
|
-
return new
|
|
242
|
+
const pointsCopy = this.points.map((p) => __spreadValues({}, p));
|
|
243
|
+
return new _Series(this.varId, pointsCopy);
|
|
123
244
|
}
|
|
124
245
|
};
|
|
125
246
|
var Outputs = class {
|
|
247
|
+
/**
|
|
248
|
+
* @param varIds The output variable identifiers.
|
|
249
|
+
* @param startTime The start time for the model.
|
|
250
|
+
* @param endTime The end time for the model.
|
|
251
|
+
* @param saveFreq The frequency with which output values are saved (aka `SAVEPER`).
|
|
252
|
+
*/
|
|
126
253
|
constructor(varIds, startTime, endTime, saveFreq = 1) {
|
|
127
254
|
this.varIds = varIds;
|
|
128
255
|
this.startTime = startTime;
|
|
@@ -139,6 +266,32 @@ var Outputs = class {
|
|
|
139
266
|
this.varSeries[i] = new Series(varId, points);
|
|
140
267
|
}
|
|
141
268
|
}
|
|
269
|
+
/**
|
|
270
|
+
* The optional set of specs that dictate which variables from the model will be
|
|
271
|
+
* stored in this `Outputs` instance. If undefined, the default set of outputs
|
|
272
|
+
* will be stored (as configured in `varIds`).
|
|
273
|
+
* @hidden This is not yet part of the public API; it is exposed here for use
|
|
274
|
+
* in experimental testing tools.
|
|
275
|
+
*/
|
|
276
|
+
setVarSpecs(varSpecs) {
|
|
277
|
+
if (varSpecs.length !== this.varIds.length) {
|
|
278
|
+
throw new Error("Length of output varSpecs must match that of varIds");
|
|
279
|
+
}
|
|
280
|
+
this.varSpecs = varSpecs;
|
|
281
|
+
}
|
|
282
|
+
/**
|
|
283
|
+
* Parse the given raw float buffer (produced by the model) and store the values
|
|
284
|
+
* into this `Outputs` instance.
|
|
285
|
+
*
|
|
286
|
+
* Note that the length of `outputsBuffer` must be greater than or equal to
|
|
287
|
+
* the capacity of this `Outputs` instance. The `Outputs` instance is allowed
|
|
288
|
+
* to be smaller to support the case where you want to extract a subset of
|
|
289
|
+
* the time range in the buffer produced by the model.
|
|
290
|
+
*
|
|
291
|
+
* @param outputsBuffer The raw outputs buffer produced by the model.
|
|
292
|
+
* @param rowLength The number of elements per row (one element per save point).
|
|
293
|
+
* @return An `ok` result if the buffer is valid, otherwise an `err` result.
|
|
294
|
+
*/
|
|
142
295
|
updateFromBuffer(outputsBuffer, rowLength) {
|
|
143
296
|
const result = parseOutputsBuffer(outputsBuffer, rowLength, this);
|
|
144
297
|
if (result.isOk()) {
|
|
@@ -147,6 +300,11 @@ var Outputs = class {
|
|
|
147
300
|
return (0, import_neverthrow.err)(result.error);
|
|
148
301
|
}
|
|
149
302
|
}
|
|
303
|
+
/**
|
|
304
|
+
* Return the series for the given output variable.
|
|
305
|
+
*
|
|
306
|
+
* @param varId The ID of the output variable (as used by SDEverywhere).
|
|
307
|
+
*/
|
|
150
308
|
getSeriesForVar(varId) {
|
|
151
309
|
const seriesIndex = this.varIds.indexOf(varId);
|
|
152
310
|
if (seriesIndex >= 0) {
|
|
@@ -209,6 +367,8 @@ function createWasmModelRunner(wasmResult) {
|
|
|
209
367
|
const inputsArray = inputsBuffer.getArrayView();
|
|
210
368
|
const outputsBuffer = wasmResult.outputsBuffer;
|
|
211
369
|
const outputsArray = outputsBuffer.getArrayView();
|
|
370
|
+
const outputIndicesBuffer = wasmResult.outputIndicesBuffer;
|
|
371
|
+
const outputIndicesArray = outputIndicesBuffer == null ? void 0 : outputIndicesBuffer.getArrayView();
|
|
212
372
|
const rowLength = wasmModel.numSavePoints;
|
|
213
373
|
let terminated = false;
|
|
214
374
|
const runModelSync = (inputs, outputs) => {
|
|
@@ -216,8 +376,16 @@ function createWasmModelRunner(wasmResult) {
|
|
|
216
376
|
for (const input of inputs) {
|
|
217
377
|
inputsArray[i++] = input.get();
|
|
218
378
|
}
|
|
379
|
+
const outputSpecs = outputs.varSpecs;
|
|
380
|
+
let useIndices;
|
|
381
|
+
if (outputIndicesArray && outputSpecs !== void 0 && outputSpecs.length > 0) {
|
|
382
|
+
updateOutputIndices(outputIndicesArray, outputSpecs);
|
|
383
|
+
useIndices = true;
|
|
384
|
+
} else {
|
|
385
|
+
useIndices = false;
|
|
386
|
+
}
|
|
219
387
|
const t0 = perfNow();
|
|
220
|
-
wasmModel.runModel(inputsBuffer, outputsBuffer);
|
|
388
|
+
wasmModel.runModel(inputsBuffer, outputsBuffer, useIndices ? outputIndicesBuffer : void 0);
|
|
221
389
|
outputs.runTimeInMillis = perfElapsed(t0);
|
|
222
390
|
outputs.updateFromBuffer(outputsArray, rowLength);
|
|
223
391
|
return outputs;
|
|
@@ -247,13 +415,121 @@ function createWasmModelRunner(wasmResult) {
|
|
|
247
415
|
};
|
|
248
416
|
}
|
|
249
417
|
|
|
418
|
+
// src/model-runner/model-listing.ts
|
|
419
|
+
var ModelListing = class {
|
|
420
|
+
constructor(modelJsonString) {
|
|
421
|
+
this.varSpecs = /* @__PURE__ */ new Map();
|
|
422
|
+
const modelJson = JSON.parse(modelJsonString);
|
|
423
|
+
const dimensions = /* @__PURE__ */ new Map();
|
|
424
|
+
for (const dimInfo of modelJson.dimensions) {
|
|
425
|
+
const dimId = dimInfo.name;
|
|
426
|
+
const subscripts = [];
|
|
427
|
+
for (let i = 0; i < dimInfo.value.length; i++) {
|
|
428
|
+
subscripts.push({
|
|
429
|
+
id: dimInfo.value[i],
|
|
430
|
+
// name: dimInfo.modelValue[i]
|
|
431
|
+
index: i
|
|
432
|
+
});
|
|
433
|
+
}
|
|
434
|
+
dimensions.set(dimId, {
|
|
435
|
+
id: dimId,
|
|
436
|
+
// name: dimInfo.modelName,
|
|
437
|
+
subscripts
|
|
438
|
+
});
|
|
439
|
+
}
|
|
440
|
+
function dimensionForId(dimId) {
|
|
441
|
+
const dim = dimensions.get(dimId);
|
|
442
|
+
if (dim === void 0) {
|
|
443
|
+
throw new Error(`No dimension info found for id=${dimId}`);
|
|
444
|
+
}
|
|
445
|
+
return dim;
|
|
446
|
+
}
|
|
447
|
+
const baseVarIds = /* @__PURE__ */ new Set();
|
|
448
|
+
for (const v of modelJson.variables) {
|
|
449
|
+
const baseVarId = varIdWithoutSubscripts(v.varName);
|
|
450
|
+
if (!baseVarIds.has(baseVarId)) {
|
|
451
|
+
const dimIds = v.families || [];
|
|
452
|
+
const dimensions2 = dimIds.map(dimensionForId);
|
|
453
|
+
if (dimensions2.length > 0) {
|
|
454
|
+
if (dimensions2.length > 3) {
|
|
455
|
+
throw new Error("Variables with more than 3 dimensions not currently supported");
|
|
456
|
+
}
|
|
457
|
+
const dimSubs = [];
|
|
458
|
+
for (const dim of dimensions2) {
|
|
459
|
+
dimSubs.push(dim.subscripts);
|
|
460
|
+
}
|
|
461
|
+
const combos = cartesianProductOf(dimSubs);
|
|
462
|
+
for (const combo of combos) {
|
|
463
|
+
const subs = combo.map((sub) => sub.id).join(",");
|
|
464
|
+
const subIndices = combo.map((sub) => sub.index);
|
|
465
|
+
const fullVarId = `${baseVarId}[${subs}]`;
|
|
466
|
+
this.varSpecs.set(fullVarId, {
|
|
467
|
+
varIndex: v.varIndex,
|
|
468
|
+
subscriptIndices: subIndices
|
|
469
|
+
});
|
|
470
|
+
}
|
|
471
|
+
} else {
|
|
472
|
+
this.varSpecs.set(baseVarId, {
|
|
473
|
+
varIndex: v.varIndex
|
|
474
|
+
});
|
|
475
|
+
}
|
|
476
|
+
baseVarIds.add(baseVarId);
|
|
477
|
+
}
|
|
478
|
+
}
|
|
479
|
+
}
|
|
480
|
+
/**
|
|
481
|
+
* Create a new `Outputs` instance that uses the same start/end years as the given "normal"
|
|
482
|
+
* `Outputs` instance but is prepared for reading the specified internal variables from the model.
|
|
483
|
+
*
|
|
484
|
+
* @param normalOutputs The `Outputs` that is used to access normal output variables from the model.
|
|
485
|
+
* @param varIds The variable IDs to include with the new `Outputs` instance.
|
|
486
|
+
*/
|
|
487
|
+
deriveOutputs(normalOutputs, varIds) {
|
|
488
|
+
const varSpecs = [];
|
|
489
|
+
for (const varId of varIds) {
|
|
490
|
+
const varSpec = this.varSpecs.get(varId);
|
|
491
|
+
if (varSpec !== void 0) {
|
|
492
|
+
varSpecs.push(varSpec);
|
|
493
|
+
} else {
|
|
494
|
+
console.warn(`WARNING: No output var spec found for id=${varId}`);
|
|
495
|
+
}
|
|
496
|
+
}
|
|
497
|
+
const newOutputs = new Outputs(varIds, normalOutputs.startTime, normalOutputs.endTime, normalOutputs.saveFreq);
|
|
498
|
+
newOutputs.varSpecs = varSpecs;
|
|
499
|
+
return newOutputs;
|
|
500
|
+
}
|
|
501
|
+
};
|
|
502
|
+
function varIdWithoutSubscripts(fullVarId) {
|
|
503
|
+
const bracketIndex = fullVarId.indexOf("[");
|
|
504
|
+
if (bracketIndex >= 0) {
|
|
505
|
+
return fullVarId.substring(0, bracketIndex);
|
|
506
|
+
} else {
|
|
507
|
+
return fullVarId;
|
|
508
|
+
}
|
|
509
|
+
}
|
|
510
|
+
function cartesianProductOf(arr) {
|
|
511
|
+
return arr.reduce(
|
|
512
|
+
(a, b) => {
|
|
513
|
+
return a.map((x) => b.map((y) => x.concat([y]))).reduce((v, w) => v.concat(w), []);
|
|
514
|
+
},
|
|
515
|
+
[[]]
|
|
516
|
+
);
|
|
517
|
+
}
|
|
518
|
+
|
|
250
519
|
// src/model-scheduler/model-scheduler.ts
|
|
251
520
|
var ModelScheduler = class {
|
|
521
|
+
/**
|
|
522
|
+
* @param runner The model runner.
|
|
523
|
+
* @param userInputs The input values, in the same order as in the spec file passed to `sde`.
|
|
524
|
+
* @param outputs The structure into which the model outputs will be stored.
|
|
525
|
+
*/
|
|
252
526
|
constructor(runner, userInputs, outputs) {
|
|
253
527
|
this.runner = runner;
|
|
254
528
|
this.userInputs = userInputs;
|
|
255
529
|
this.outputs = outputs;
|
|
530
|
+
/** Whether a model run has been scheduled. */
|
|
256
531
|
this.runNeeded = false;
|
|
532
|
+
/** Whether a model run is in progress. */
|
|
257
533
|
this.runInProgress = false;
|
|
258
534
|
const afterSet = () => {
|
|
259
535
|
this.runWasmModelIfNeeded();
|
|
@@ -266,6 +542,10 @@ var ModelScheduler = class {
|
|
|
266
542
|
this.currentInputs.push(createSimpleInputValue(userInput.varId));
|
|
267
543
|
}
|
|
268
544
|
}
|
|
545
|
+
/**
|
|
546
|
+
* Schedule a wasm model run (if not already pending). When the run is
|
|
547
|
+
* complete, save the outputs and call the `onOutputsChanged` callback.
|
|
548
|
+
*/
|
|
269
549
|
runWasmModelIfNeeded() {
|
|
270
550
|
this.runNeeded = true;
|
|
271
551
|
if (this.runInProgress) {
|
|
@@ -277,26 +557,31 @@ var ModelScheduler = class {
|
|
|
277
557
|
}, 0);
|
|
278
558
|
}
|
|
279
559
|
}
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
560
|
+
/**
|
|
561
|
+
* Run the wasm model asynchronously using the current set of input values.
|
|
562
|
+
*/
|
|
563
|
+
runWasmModelNow() {
|
|
564
|
+
return __async(this, null, function* () {
|
|
565
|
+
var _a;
|
|
566
|
+
for (let i = 0; i < this.userInputs.length; i++) {
|
|
567
|
+
this.currentInputs[i].set(this.userInputs[i].get());
|
|
568
|
+
}
|
|
569
|
+
try {
|
|
570
|
+
this.outputs = yield this.runner.runModel(this.currentInputs, this.outputs);
|
|
571
|
+
(_a = this.onOutputsChanged) == null ? void 0 : _a.call(this, this.outputs);
|
|
572
|
+
} catch (e) {
|
|
573
|
+
console.error(`ERROR: Failed to run model: ${e.message}`);
|
|
574
|
+
}
|
|
575
|
+
if (this.runNeeded) {
|
|
576
|
+
this.runNeeded = false;
|
|
577
|
+
setTimeout(() => {
|
|
578
|
+
this.runWasmModelNow();
|
|
579
|
+
}, 0);
|
|
580
|
+
} else {
|
|
581
|
+
this.runNeeded = false;
|
|
582
|
+
this.runInProgress = false;
|
|
583
|
+
}
|
|
584
|
+
});
|
|
300
585
|
}
|
|
301
586
|
};
|
|
302
587
|
function createSimpleInputValue(varId) {
|
|
@@ -314,15 +599,19 @@ function createSimpleInputValue(varId) {
|
|
|
314
599
|
}
|
|
315
600
|
// Annotate the CommonJS export names for ESM import in node:
|
|
316
601
|
0 && (module.exports = {
|
|
602
|
+
ModelListing,
|
|
317
603
|
ModelScheduler,
|
|
318
604
|
Outputs,
|
|
319
605
|
Series,
|
|
320
606
|
WasmBuffer,
|
|
321
607
|
WasmModel,
|
|
608
|
+
createFloat64WasmBuffer,
|
|
322
609
|
createInputValue,
|
|
610
|
+
createInt32WasmBuffer,
|
|
323
611
|
createWasmModelRunner,
|
|
324
612
|
initWasmModelAndBuffers,
|
|
325
613
|
perfElapsed,
|
|
326
|
-
perfNow
|
|
614
|
+
perfNow,
|
|
615
|
+
updateOutputIndices
|
|
327
616
|
});
|
|
328
617
|
//# sourceMappingURL=index.cjs.map
|