@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/dist/index.d.ts
CHANGED
|
@@ -1,9 +1,19 @@
|
|
|
1
1
|
import { Result } from 'neverthrow';
|
|
2
2
|
|
|
3
3
|
/** An input variable identifier string, as used in SDEverywhere. */
|
|
4
|
-
|
|
4
|
+
type InputVarId = string;
|
|
5
5
|
/** An output variable identifier string, as used in SDEverywhere. */
|
|
6
|
-
|
|
6
|
+
type OutputVarId = string;
|
|
7
|
+
/**
|
|
8
|
+
* The variable index values for use with the optional output indices buffer.
|
|
9
|
+
* @hidden This is not yet part of the public API; it is exposed here for use in testing tools.
|
|
10
|
+
*/
|
|
11
|
+
interface OutputVarSpec {
|
|
12
|
+
/** The variable index as used in the generated C code. */
|
|
13
|
+
varIndex: number;
|
|
14
|
+
/** The subscript index values as used in the generated C code. */
|
|
15
|
+
subscriptIndices?: number[];
|
|
16
|
+
}
|
|
7
17
|
|
|
8
18
|
/**
|
|
9
19
|
* Type declaration for a WebAssembly module wrapper produced
|
|
@@ -18,6 +28,8 @@ interface WasmModule {
|
|
|
18
28
|
/** @hidden */
|
|
19
29
|
_free: (byteOffset: number) => void;
|
|
20
30
|
/** @hidden */
|
|
31
|
+
HEAP32: Int32Array;
|
|
32
|
+
/** @hidden */
|
|
21
33
|
HEAPF64: Float64Array;
|
|
22
34
|
}
|
|
23
35
|
|
|
@@ -33,19 +45,20 @@ interface WasmModule {
|
|
|
33
45
|
* in the browser's normal JS thread, and then use `getArrayView` to copy into and
|
|
34
46
|
* out of the wasm buffer.
|
|
35
47
|
*/
|
|
36
|
-
declare class WasmBuffer {
|
|
48
|
+
declare class WasmBuffer<ArrType> {
|
|
37
49
|
private readonly wasmModule;
|
|
38
50
|
private byteOffset;
|
|
39
51
|
private heapArray;
|
|
40
52
|
/**
|
|
41
53
|
* @param wasmModule The `WasmModule` used to initialize the memory.
|
|
42
|
-
* @param
|
|
54
|
+
* @param byteOffset The byte offset within the wasm heap.
|
|
55
|
+
* @param heapArray The array view on the underlying heap buffer.
|
|
43
56
|
*/
|
|
44
|
-
constructor(wasmModule: WasmModule,
|
|
57
|
+
constructor(wasmModule: WasmModule, byteOffset: number, heapArray: ArrType);
|
|
45
58
|
/**
|
|
46
|
-
* @return
|
|
59
|
+
* @return An `ArrType` view on the underlying heap buffer.
|
|
47
60
|
*/
|
|
48
|
-
getArrayView():
|
|
61
|
+
getArrayView(): ArrType;
|
|
49
62
|
/**
|
|
50
63
|
* @return The raw address of the underlying heap buffer.
|
|
51
64
|
* @hidden This is intended for use by `WasmModel` only.
|
|
@@ -56,6 +69,24 @@ declare class WasmBuffer {
|
|
|
56
69
|
*/
|
|
57
70
|
dispose(): void;
|
|
58
71
|
}
|
|
72
|
+
/**
|
|
73
|
+
* Return a `WasmBuffer` that holds int32 elements.
|
|
74
|
+
*
|
|
75
|
+
* @hidden For internal use only.
|
|
76
|
+
*
|
|
77
|
+
* @param wasmModule The `WasmModule` used to initialize the memory.
|
|
78
|
+
* @param numElements The number of elements in the buffer.
|
|
79
|
+
*/
|
|
80
|
+
declare function createInt32WasmBuffer(wasmModule: WasmModule, numElements: number): WasmBuffer<Int32Array>;
|
|
81
|
+
/**
|
|
82
|
+
* Return a `WasmBuffer` that holds float64 elements.
|
|
83
|
+
*
|
|
84
|
+
* @hidden For internal use only.
|
|
85
|
+
*
|
|
86
|
+
* @param wasmModule The `WasmModule` used to initialize the memory.
|
|
87
|
+
* @param numElements The number of elements in the buffer.
|
|
88
|
+
*/
|
|
89
|
+
declare function createFloat64WasmBuffer(wasmModule: WasmModule, numElements: number): WasmBuffer<Float64Array>;
|
|
59
90
|
|
|
60
91
|
/**
|
|
61
92
|
* An interface to the generated WebAssembly model. Allows for running the model with
|
|
@@ -70,6 +101,12 @@ declare class WasmModel {
|
|
|
70
101
|
readonly saveFreq: number;
|
|
71
102
|
/** The number of save points for each output. */
|
|
72
103
|
readonly numSavePoints: number;
|
|
104
|
+
/**
|
|
105
|
+
* The maximum number of output indices that can be passed for each run.
|
|
106
|
+
* @hidden This is not yet part of the public API; it is exposed here for use
|
|
107
|
+
* in experimental testing tools.
|
|
108
|
+
*/
|
|
109
|
+
readonly maxOutputIndices: number;
|
|
73
110
|
private readonly wasmRunModel;
|
|
74
111
|
/**
|
|
75
112
|
* @param wasmModule The `WasmModule` that provides access to the native functions.
|
|
@@ -81,8 +118,9 @@ declare class WasmModel {
|
|
|
81
118
|
*
|
|
82
119
|
* @param inputs The buffer containing inputs in the order expected by the model.
|
|
83
120
|
* @param outputs The buffer into which the model will store output values.
|
|
121
|
+
* @param outputIndices The buffer used to control which variables are written to `outputs`.
|
|
84
122
|
*/
|
|
85
|
-
runModel(inputs: WasmBuffer
|
|
123
|
+
runModel(inputs: WasmBuffer<Float64Array>, outputs: WasmBuffer<Float64Array>, outputIndices?: WasmBuffer<Int32Array>): void;
|
|
86
124
|
}
|
|
87
125
|
/**
|
|
88
126
|
* The result of model initialization.
|
|
@@ -91,9 +129,15 @@ interface WasmModelInitResult {
|
|
|
91
129
|
/** The wasm model. */
|
|
92
130
|
model: WasmModel;
|
|
93
131
|
/** The buffer used to pass input values to the model. */
|
|
94
|
-
inputsBuffer: WasmBuffer
|
|
132
|
+
inputsBuffer: WasmBuffer<Float64Array>;
|
|
95
133
|
/** The buffer used to receive output values from the model. */
|
|
96
|
-
outputsBuffer: WasmBuffer
|
|
134
|
+
outputsBuffer: WasmBuffer<Float64Array>;
|
|
135
|
+
/**
|
|
136
|
+
* The buffer used to control which variables are written to `outputsBuffer`.
|
|
137
|
+
* @hidden This is not yet part of the public API; it is exposed here for use
|
|
138
|
+
* in experimental testing tools.
|
|
139
|
+
*/
|
|
140
|
+
outputIndicesBuffer?: WasmBuffer<Int32Array>;
|
|
97
141
|
/** The output variable IDs. */
|
|
98
142
|
outputVarIds: OutputVarId[];
|
|
99
143
|
}
|
|
@@ -105,6 +149,11 @@ interface WasmModelInitResult {
|
|
|
105
149
|
* @param outputVarIds The output variable IDs, per the spec file passed to `sde`.
|
|
106
150
|
*/
|
|
107
151
|
declare function initWasmModelAndBuffers(wasmModule: WasmModule, numInputs: number, outputVarIds: OutputVarId[]): WasmModelInitResult;
|
|
152
|
+
/**
|
|
153
|
+
* @hidden This is not part of the public API; it is exposed here for use by
|
|
154
|
+
* the synchronous and asynchronous model runner implementations.
|
|
155
|
+
*/
|
|
156
|
+
declare function updateOutputIndices(indicesArray: Int32Array, outputVarSpecs: OutputVarSpec[]): void;
|
|
108
157
|
|
|
109
158
|
/** Callback functions that are called when the input value is changed. */
|
|
110
159
|
interface InputCallbacks {
|
|
@@ -136,7 +185,7 @@ interface InputValue {
|
|
|
136
185
|
declare function createInputValue(varId: InputVarId, defaultValue: number, initialValue?: number): InputValue;
|
|
137
186
|
|
|
138
187
|
/** Indicates the type of error encountered when parsing an outputs buffer. */
|
|
139
|
-
|
|
188
|
+
type ParseError = 'invalid-point-count';
|
|
140
189
|
/** A data point. */
|
|
141
190
|
interface Point {
|
|
142
191
|
/** The x value (typically a time value). */
|
|
@@ -186,6 +235,14 @@ declare class Outputs {
|
|
|
186
235
|
* in performance testing tools.
|
|
187
236
|
*/
|
|
188
237
|
runTimeInMillis: number;
|
|
238
|
+
/**
|
|
239
|
+
* The optional set of specs that dictate which variables from the model will be
|
|
240
|
+
* stored in this `Outputs` instance. If undefined, the default set of outputs
|
|
241
|
+
* will be stored (as configured in `varIds`).
|
|
242
|
+
* @hidden This is not yet part of the public API; it is exposed here for use
|
|
243
|
+
* in experimental testing tools.
|
|
244
|
+
*/
|
|
245
|
+
varSpecs?: OutputVarSpec[];
|
|
189
246
|
/**
|
|
190
247
|
* @param varIds The output variable identifiers.
|
|
191
248
|
* @param startTime The start time for the model.
|
|
@@ -193,6 +250,14 @@ declare class Outputs {
|
|
|
193
250
|
* @param saveFreq The frequency with which output values are saved (aka `SAVEPER`).
|
|
194
251
|
*/
|
|
195
252
|
constructor(varIds: OutputVarId[], startTime: number, endTime: number, saveFreq?: number);
|
|
253
|
+
/**
|
|
254
|
+
* The optional set of specs that dictate which variables from the model will be
|
|
255
|
+
* stored in this `Outputs` instance. If undefined, the default set of outputs
|
|
256
|
+
* will be stored (as configured in `varIds`).
|
|
257
|
+
* @hidden This is not yet part of the public API; it is exposed here for use
|
|
258
|
+
* in experimental testing tools.
|
|
259
|
+
*/
|
|
260
|
+
setVarSpecs(varSpecs: OutputVarSpec[]): void;
|
|
196
261
|
/**
|
|
197
262
|
* Parse the given raw float buffer (produced by the model) and store the values
|
|
198
263
|
* into this `Outputs` instance.
|
|
@@ -259,6 +324,23 @@ interface ModelRunner {
|
|
|
259
324
|
*/
|
|
260
325
|
declare function createWasmModelRunner(wasmResult: WasmModelInitResult): ModelRunner;
|
|
261
326
|
|
|
327
|
+
/**
|
|
328
|
+
* @hidden This is not yet part of the public API; it is exposed here for use
|
|
329
|
+
* in experimental testing tools.
|
|
330
|
+
*/
|
|
331
|
+
declare class ModelListing {
|
|
332
|
+
readonly varSpecs: Map<OutputVarId, OutputVarSpec>;
|
|
333
|
+
constructor(modelJsonString: string);
|
|
334
|
+
/**
|
|
335
|
+
* Create a new `Outputs` instance that uses the same start/end years as the given "normal"
|
|
336
|
+
* `Outputs` instance but is prepared for reading the specified internal variables from the model.
|
|
337
|
+
*
|
|
338
|
+
* @param normalOutputs The `Outputs` that is used to access normal output variables from the model.
|
|
339
|
+
* @param varIds The variable IDs to include with the new `Outputs` instance.
|
|
340
|
+
*/
|
|
341
|
+
deriveOutputs(normalOutputs: Outputs, varIds: OutputVarId[]): Outputs;
|
|
342
|
+
}
|
|
343
|
+
|
|
262
344
|
/**
|
|
263
345
|
* Return a timestamp that can be passed to `perfElapsed` for calculating the elapsed
|
|
264
346
|
* time of an operation.
|
|
@@ -313,4 +395,4 @@ declare class ModelScheduler {
|
|
|
313
395
|
private runWasmModelNow;
|
|
314
396
|
}
|
|
315
397
|
|
|
316
|
-
export { InputCallbacks, InputValue, InputVarId, ModelRunner, ModelScheduler, OutputVarId, Outputs, ParseError, Point, Series, WasmBuffer, WasmModel, WasmModelInitResult, WasmModule, createInputValue, createWasmModelRunner, initWasmModelAndBuffers, perfElapsed, perfNow };
|
|
398
|
+
export { InputCallbacks, InputValue, InputVarId, ModelListing, ModelRunner, ModelScheduler, OutputVarId, OutputVarSpec, Outputs, ParseError, Point, Series, WasmBuffer, WasmModel, WasmModelInitResult, WasmModule, createFloat64WasmBuffer, createInputValue, createInt32WasmBuffer, createWasmModelRunner, initWasmModelAndBuffers, perfElapsed, perfNow, updateOutputIndices };
|
package/dist/index.js
CHANGED
|
@@ -1,19 +1,68 @@
|
|
|
1
|
+
var __defProp = Object.defineProperty;
|
|
2
|
+
var __getOwnPropSymbols = Object.getOwnPropertySymbols;
|
|
3
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
4
|
+
var __propIsEnum = Object.prototype.propertyIsEnumerable;
|
|
5
|
+
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
6
|
+
var __spreadValues = (a, b) => {
|
|
7
|
+
for (var prop in b || (b = {}))
|
|
8
|
+
if (__hasOwnProp.call(b, prop))
|
|
9
|
+
__defNormalProp(a, prop, b[prop]);
|
|
10
|
+
if (__getOwnPropSymbols)
|
|
11
|
+
for (var prop of __getOwnPropSymbols(b)) {
|
|
12
|
+
if (__propIsEnum.call(b, prop))
|
|
13
|
+
__defNormalProp(a, prop, b[prop]);
|
|
14
|
+
}
|
|
15
|
+
return a;
|
|
16
|
+
};
|
|
17
|
+
var __async = (__this, __arguments, generator) => {
|
|
18
|
+
return new Promise((resolve, reject) => {
|
|
19
|
+
var fulfilled = (value) => {
|
|
20
|
+
try {
|
|
21
|
+
step(generator.next(value));
|
|
22
|
+
} catch (e) {
|
|
23
|
+
reject(e);
|
|
24
|
+
}
|
|
25
|
+
};
|
|
26
|
+
var rejected = (value) => {
|
|
27
|
+
try {
|
|
28
|
+
step(generator.throw(value));
|
|
29
|
+
} catch (e) {
|
|
30
|
+
reject(e);
|
|
31
|
+
}
|
|
32
|
+
};
|
|
33
|
+
var step = (x) => x.done ? resolve(x.value) : Promise.resolve(x.value).then(fulfilled, rejected);
|
|
34
|
+
step((generator = generator.apply(__this, __arguments)).next());
|
|
35
|
+
});
|
|
36
|
+
};
|
|
37
|
+
|
|
1
38
|
// src/wasm-model/wasm-buffer.ts
|
|
2
39
|
var WasmBuffer = class {
|
|
3
|
-
|
|
40
|
+
/**
|
|
41
|
+
* @param wasmModule The `WasmModule` used to initialize the memory.
|
|
42
|
+
* @param byteOffset The byte offset within the wasm heap.
|
|
43
|
+
* @param heapArray The array view on the underlying heap buffer.
|
|
44
|
+
*/
|
|
45
|
+
constructor(wasmModule, byteOffset, heapArray) {
|
|
4
46
|
this.wasmModule = wasmModule;
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
this.byteOffset = wasmModule._malloc(lengthInBytes);
|
|
8
|
-
const float64Offset = this.byteOffset / sizeOfFloat64;
|
|
9
|
-
this.heapArray = wasmModule.HEAPF64.subarray(float64Offset, float64Offset + numElements);
|
|
47
|
+
this.byteOffset = byteOffset;
|
|
48
|
+
this.heapArray = heapArray;
|
|
10
49
|
}
|
|
50
|
+
/**
|
|
51
|
+
* @return An `ArrType` view on the underlying heap buffer.
|
|
52
|
+
*/
|
|
11
53
|
getArrayView() {
|
|
12
54
|
return this.heapArray;
|
|
13
55
|
}
|
|
56
|
+
/**
|
|
57
|
+
* @return The raw address of the underlying heap buffer.
|
|
58
|
+
* @hidden This is intended for use by `WasmModel` only.
|
|
59
|
+
*/
|
|
14
60
|
getAddress() {
|
|
15
61
|
return this.byteOffset;
|
|
16
62
|
}
|
|
63
|
+
/**
|
|
64
|
+
* Dispose the buffer by freeing the allocated heap memory.
|
|
65
|
+
*/
|
|
17
66
|
dispose() {
|
|
18
67
|
if (this.heapArray) {
|
|
19
68
|
this.wasmModule._free(this.byteOffset);
|
|
@@ -22,9 +71,29 @@ var WasmBuffer = class {
|
|
|
22
71
|
}
|
|
23
72
|
}
|
|
24
73
|
};
|
|
74
|
+
function createInt32WasmBuffer(wasmModule, numElements) {
|
|
75
|
+
const elemSizeInBytes = 4;
|
|
76
|
+
const lengthInBytes = numElements * elemSizeInBytes;
|
|
77
|
+
const byteOffset = wasmModule._malloc(lengthInBytes);
|
|
78
|
+
const elemOffset = byteOffset / elemSizeInBytes;
|
|
79
|
+
const heapArray = wasmModule.HEAP32.subarray(elemOffset, elemOffset + numElements);
|
|
80
|
+
return new WasmBuffer(wasmModule, byteOffset, heapArray);
|
|
81
|
+
}
|
|
82
|
+
function createFloat64WasmBuffer(wasmModule, numElements) {
|
|
83
|
+
const elemSizeInBytes = 8;
|
|
84
|
+
const lengthInBytes = numElements * elemSizeInBytes;
|
|
85
|
+
const byteOffset = wasmModule._malloc(lengthInBytes);
|
|
86
|
+
const elemOffset = byteOffset / elemSizeInBytes;
|
|
87
|
+
const heapArray = wasmModule.HEAPF64.subarray(elemOffset, elemOffset + numElements);
|
|
88
|
+
return new WasmBuffer(wasmModule, byteOffset, heapArray);
|
|
89
|
+
}
|
|
25
90
|
|
|
26
91
|
// src/wasm-model/wasm-model.ts
|
|
92
|
+
var indicesPerOutput = 4;
|
|
27
93
|
var WasmModel = class {
|
|
94
|
+
/**
|
|
95
|
+
* @param wasmModule The `WasmModule` that provides access to the native functions.
|
|
96
|
+
*/
|
|
28
97
|
constructor(wasmModule) {
|
|
29
98
|
function getNumberValue(funcName) {
|
|
30
99
|
const wasmGetValue = wasmModule.cwrap(funcName, "number", []);
|
|
@@ -33,24 +102,59 @@ var WasmModel = class {
|
|
|
33
102
|
this.startTime = getNumberValue("getInitialTime");
|
|
34
103
|
this.endTime = getNumberValue("getFinalTime");
|
|
35
104
|
this.saveFreq = getNumberValue("getSaveper");
|
|
105
|
+
try {
|
|
106
|
+
this.maxOutputIndices = getNumberValue("getMaxOutputIndices");
|
|
107
|
+
} catch (e) {
|
|
108
|
+
this.maxOutputIndices = 0;
|
|
109
|
+
}
|
|
36
110
|
this.numSavePoints = Math.round((this.endTime - this.startTime) / this.saveFreq) + 1;
|
|
37
|
-
this.wasmRunModel = wasmModule.cwrap("runModelWithBuffers", null, ["number", "number"]);
|
|
111
|
+
this.wasmRunModel = wasmModule.cwrap("runModelWithBuffers", null, ["number", "number", "number"]);
|
|
38
112
|
}
|
|
39
|
-
|
|
40
|
-
|
|
113
|
+
/**
|
|
114
|
+
* Run the model, using inputs from the `inputs` buffer, and writing outputs into
|
|
115
|
+
* the `outputs` buffer.
|
|
116
|
+
*
|
|
117
|
+
* @param inputs The buffer containing inputs in the order expected by the model.
|
|
118
|
+
* @param outputs The buffer into which the model will store output values.
|
|
119
|
+
* @param outputIndices The buffer used to control which variables are written to `outputs`.
|
|
120
|
+
*/
|
|
121
|
+
runModel(inputs, outputs, outputIndices) {
|
|
122
|
+
this.wasmRunModel(inputs.getAddress(), outputs.getAddress(), (outputIndices == null ? void 0 : outputIndices.getAddress()) || 0);
|
|
41
123
|
}
|
|
42
124
|
};
|
|
43
125
|
function initWasmModelAndBuffers(wasmModule, numInputs, outputVarIds) {
|
|
44
126
|
const model = new WasmModel(wasmModule);
|
|
45
|
-
const inputsBuffer =
|
|
46
|
-
const
|
|
127
|
+
const inputsBuffer = createFloat64WasmBuffer(wasmModule, numInputs);
|
|
128
|
+
const outputVarCount = Math.max(outputVarIds.length, model.maxOutputIndices);
|
|
129
|
+
const outputsBuffer = createFloat64WasmBuffer(wasmModule, outputVarCount * model.numSavePoints);
|
|
130
|
+
let outputIndicesBuffer;
|
|
131
|
+
if (model.maxOutputIndices > 0) {
|
|
132
|
+
outputIndicesBuffer = createInt32WasmBuffer(wasmModule, model.maxOutputIndices * indicesPerOutput);
|
|
133
|
+
}
|
|
47
134
|
return {
|
|
48
135
|
model,
|
|
49
136
|
inputsBuffer,
|
|
50
137
|
outputsBuffer,
|
|
138
|
+
outputIndicesBuffer,
|
|
51
139
|
outputVarIds
|
|
52
140
|
};
|
|
53
141
|
}
|
|
142
|
+
function updateOutputIndices(indicesArray, outputVarSpecs) {
|
|
143
|
+
var _a;
|
|
144
|
+
if (indicesArray.length < outputVarSpecs.length * indicesPerOutput) {
|
|
145
|
+
throw new Error("Length of indicesArray must be large enough to accommodate the given outputVarSpecs");
|
|
146
|
+
}
|
|
147
|
+
let offset = 0;
|
|
148
|
+
for (const outputVarSpec of outputVarSpecs) {
|
|
149
|
+
const subCount = ((_a = outputVarSpec.subscriptIndices) == null ? void 0 : _a.length) || 0;
|
|
150
|
+
indicesArray[offset + 0] = outputVarSpec.varIndex;
|
|
151
|
+
indicesArray[offset + 1] = subCount > 0 ? outputVarSpec.subscriptIndices[0] : 0;
|
|
152
|
+
indicesArray[offset + 2] = subCount > 1 ? outputVarSpec.subscriptIndices[1] : 0;
|
|
153
|
+
indicesArray[offset + 3] = subCount > 2 ? outputVarSpec.subscriptIndices[2] : 0;
|
|
154
|
+
offset += indicesPerOutput;
|
|
155
|
+
}
|
|
156
|
+
indicesArray.fill(0, offset);
|
|
157
|
+
}
|
|
54
158
|
|
|
55
159
|
// src/model-runner/inputs.ts
|
|
56
160
|
function createInputValue(varId, defaultValue, initialValue) {
|
|
@@ -74,21 +178,43 @@ function createInputValue(varId, defaultValue, initialValue) {
|
|
|
74
178
|
|
|
75
179
|
// src/model-runner/outputs.ts
|
|
76
180
|
import { ok, err } from "neverthrow";
|
|
77
|
-
var Series = class {
|
|
181
|
+
var Series = class _Series {
|
|
182
|
+
/**
|
|
183
|
+
* @param varId The ID for the output variable (as used by SDEverywhere).
|
|
184
|
+
* @param points The data points for the variable, one point per time increment.
|
|
185
|
+
*/
|
|
78
186
|
constructor(varId, points) {
|
|
79
187
|
this.varId = varId;
|
|
80
188
|
this.points = points;
|
|
81
189
|
}
|
|
190
|
+
/**
|
|
191
|
+
* Return the Y value at the given time. Note that this does not attempt to interpolate
|
|
192
|
+
* if there is no data point defined for the given time and will return undefined in
|
|
193
|
+
* that case.
|
|
194
|
+
*
|
|
195
|
+
* @param time The x (time) value.
|
|
196
|
+
* @return The y value for the given time, or undefined if there is no data point defined
|
|
197
|
+
* for the given time.
|
|
198
|
+
*/
|
|
82
199
|
getValueAtTime(time) {
|
|
83
200
|
var _a;
|
|
84
201
|
return (_a = this.points.find((p) => p.x === time)) == null ? void 0 : _a.y;
|
|
85
202
|
}
|
|
203
|
+
/**
|
|
204
|
+
* Create a new `Series` instance that is a copy of this one.
|
|
205
|
+
*/
|
|
86
206
|
copy() {
|
|
87
|
-
const pointsCopy = this.points.map((p) => ({
|
|
88
|
-
return new
|
|
207
|
+
const pointsCopy = this.points.map((p) => __spreadValues({}, p));
|
|
208
|
+
return new _Series(this.varId, pointsCopy);
|
|
89
209
|
}
|
|
90
210
|
};
|
|
91
211
|
var Outputs = class {
|
|
212
|
+
/**
|
|
213
|
+
* @param varIds The output variable identifiers.
|
|
214
|
+
* @param startTime The start time for the model.
|
|
215
|
+
* @param endTime The end time for the model.
|
|
216
|
+
* @param saveFreq The frequency with which output values are saved (aka `SAVEPER`).
|
|
217
|
+
*/
|
|
92
218
|
constructor(varIds, startTime, endTime, saveFreq = 1) {
|
|
93
219
|
this.varIds = varIds;
|
|
94
220
|
this.startTime = startTime;
|
|
@@ -105,6 +231,32 @@ var Outputs = class {
|
|
|
105
231
|
this.varSeries[i] = new Series(varId, points);
|
|
106
232
|
}
|
|
107
233
|
}
|
|
234
|
+
/**
|
|
235
|
+
* The optional set of specs that dictate which variables from the model will be
|
|
236
|
+
* stored in this `Outputs` instance. If undefined, the default set of outputs
|
|
237
|
+
* will be stored (as configured in `varIds`).
|
|
238
|
+
* @hidden This is not yet part of the public API; it is exposed here for use
|
|
239
|
+
* in experimental testing tools.
|
|
240
|
+
*/
|
|
241
|
+
setVarSpecs(varSpecs) {
|
|
242
|
+
if (varSpecs.length !== this.varIds.length) {
|
|
243
|
+
throw new Error("Length of output varSpecs must match that of varIds");
|
|
244
|
+
}
|
|
245
|
+
this.varSpecs = varSpecs;
|
|
246
|
+
}
|
|
247
|
+
/**
|
|
248
|
+
* Parse the given raw float buffer (produced by the model) and store the values
|
|
249
|
+
* into this `Outputs` instance.
|
|
250
|
+
*
|
|
251
|
+
* Note that the length of `outputsBuffer` must be greater than or equal to
|
|
252
|
+
* the capacity of this `Outputs` instance. The `Outputs` instance is allowed
|
|
253
|
+
* to be smaller to support the case where you want to extract a subset of
|
|
254
|
+
* the time range in the buffer produced by the model.
|
|
255
|
+
*
|
|
256
|
+
* @param outputsBuffer The raw outputs buffer produced by the model.
|
|
257
|
+
* @param rowLength The number of elements per row (one element per save point).
|
|
258
|
+
* @return An `ok` result if the buffer is valid, otherwise an `err` result.
|
|
259
|
+
*/
|
|
108
260
|
updateFromBuffer(outputsBuffer, rowLength) {
|
|
109
261
|
const result = parseOutputsBuffer(outputsBuffer, rowLength, this);
|
|
110
262
|
if (result.isOk()) {
|
|
@@ -113,6 +265,11 @@ var Outputs = class {
|
|
|
113
265
|
return err(result.error);
|
|
114
266
|
}
|
|
115
267
|
}
|
|
268
|
+
/**
|
|
269
|
+
* Return the series for the given output variable.
|
|
270
|
+
*
|
|
271
|
+
* @param varId The ID of the output variable (as used by SDEverywhere).
|
|
272
|
+
*/
|
|
116
273
|
getSeriesForVar(varId) {
|
|
117
274
|
const seriesIndex = this.varIds.indexOf(varId);
|
|
118
275
|
if (seriesIndex >= 0) {
|
|
@@ -175,6 +332,8 @@ function createWasmModelRunner(wasmResult) {
|
|
|
175
332
|
const inputsArray = inputsBuffer.getArrayView();
|
|
176
333
|
const outputsBuffer = wasmResult.outputsBuffer;
|
|
177
334
|
const outputsArray = outputsBuffer.getArrayView();
|
|
335
|
+
const outputIndicesBuffer = wasmResult.outputIndicesBuffer;
|
|
336
|
+
const outputIndicesArray = outputIndicesBuffer == null ? void 0 : outputIndicesBuffer.getArrayView();
|
|
178
337
|
const rowLength = wasmModel.numSavePoints;
|
|
179
338
|
let terminated = false;
|
|
180
339
|
const runModelSync = (inputs, outputs) => {
|
|
@@ -182,8 +341,16 @@ function createWasmModelRunner(wasmResult) {
|
|
|
182
341
|
for (const input of inputs) {
|
|
183
342
|
inputsArray[i++] = input.get();
|
|
184
343
|
}
|
|
344
|
+
const outputSpecs = outputs.varSpecs;
|
|
345
|
+
let useIndices;
|
|
346
|
+
if (outputIndicesArray && outputSpecs !== void 0 && outputSpecs.length > 0) {
|
|
347
|
+
updateOutputIndices(outputIndicesArray, outputSpecs);
|
|
348
|
+
useIndices = true;
|
|
349
|
+
} else {
|
|
350
|
+
useIndices = false;
|
|
351
|
+
}
|
|
185
352
|
const t0 = perfNow();
|
|
186
|
-
wasmModel.runModel(inputsBuffer, outputsBuffer);
|
|
353
|
+
wasmModel.runModel(inputsBuffer, outputsBuffer, useIndices ? outputIndicesBuffer : void 0);
|
|
187
354
|
outputs.runTimeInMillis = perfElapsed(t0);
|
|
188
355
|
outputs.updateFromBuffer(outputsArray, rowLength);
|
|
189
356
|
return outputs;
|
|
@@ -213,13 +380,121 @@ function createWasmModelRunner(wasmResult) {
|
|
|
213
380
|
};
|
|
214
381
|
}
|
|
215
382
|
|
|
383
|
+
// src/model-runner/model-listing.ts
|
|
384
|
+
var ModelListing = class {
|
|
385
|
+
constructor(modelJsonString) {
|
|
386
|
+
this.varSpecs = /* @__PURE__ */ new Map();
|
|
387
|
+
const modelJson = JSON.parse(modelJsonString);
|
|
388
|
+
const dimensions = /* @__PURE__ */ new Map();
|
|
389
|
+
for (const dimInfo of modelJson.dimensions) {
|
|
390
|
+
const dimId = dimInfo.name;
|
|
391
|
+
const subscripts = [];
|
|
392
|
+
for (let i = 0; i < dimInfo.value.length; i++) {
|
|
393
|
+
subscripts.push({
|
|
394
|
+
id: dimInfo.value[i],
|
|
395
|
+
// name: dimInfo.modelValue[i]
|
|
396
|
+
index: i
|
|
397
|
+
});
|
|
398
|
+
}
|
|
399
|
+
dimensions.set(dimId, {
|
|
400
|
+
id: dimId,
|
|
401
|
+
// name: dimInfo.modelName,
|
|
402
|
+
subscripts
|
|
403
|
+
});
|
|
404
|
+
}
|
|
405
|
+
function dimensionForId(dimId) {
|
|
406
|
+
const dim = dimensions.get(dimId);
|
|
407
|
+
if (dim === void 0) {
|
|
408
|
+
throw new Error(`No dimension info found for id=${dimId}`);
|
|
409
|
+
}
|
|
410
|
+
return dim;
|
|
411
|
+
}
|
|
412
|
+
const baseVarIds = /* @__PURE__ */ new Set();
|
|
413
|
+
for (const v of modelJson.variables) {
|
|
414
|
+
const baseVarId = varIdWithoutSubscripts(v.varName);
|
|
415
|
+
if (!baseVarIds.has(baseVarId)) {
|
|
416
|
+
const dimIds = v.families || [];
|
|
417
|
+
const dimensions2 = dimIds.map(dimensionForId);
|
|
418
|
+
if (dimensions2.length > 0) {
|
|
419
|
+
if (dimensions2.length > 3) {
|
|
420
|
+
throw new Error("Variables with more than 3 dimensions not currently supported");
|
|
421
|
+
}
|
|
422
|
+
const dimSubs = [];
|
|
423
|
+
for (const dim of dimensions2) {
|
|
424
|
+
dimSubs.push(dim.subscripts);
|
|
425
|
+
}
|
|
426
|
+
const combos = cartesianProductOf(dimSubs);
|
|
427
|
+
for (const combo of combos) {
|
|
428
|
+
const subs = combo.map((sub) => sub.id).join(",");
|
|
429
|
+
const subIndices = combo.map((sub) => sub.index);
|
|
430
|
+
const fullVarId = `${baseVarId}[${subs}]`;
|
|
431
|
+
this.varSpecs.set(fullVarId, {
|
|
432
|
+
varIndex: v.varIndex,
|
|
433
|
+
subscriptIndices: subIndices
|
|
434
|
+
});
|
|
435
|
+
}
|
|
436
|
+
} else {
|
|
437
|
+
this.varSpecs.set(baseVarId, {
|
|
438
|
+
varIndex: v.varIndex
|
|
439
|
+
});
|
|
440
|
+
}
|
|
441
|
+
baseVarIds.add(baseVarId);
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
/**
|
|
446
|
+
* Create a new `Outputs` instance that uses the same start/end years as the given "normal"
|
|
447
|
+
* `Outputs` instance but is prepared for reading the specified internal variables from the model.
|
|
448
|
+
*
|
|
449
|
+
* @param normalOutputs The `Outputs` that is used to access normal output variables from the model.
|
|
450
|
+
* @param varIds The variable IDs to include with the new `Outputs` instance.
|
|
451
|
+
*/
|
|
452
|
+
deriveOutputs(normalOutputs, varIds) {
|
|
453
|
+
const varSpecs = [];
|
|
454
|
+
for (const varId of varIds) {
|
|
455
|
+
const varSpec = this.varSpecs.get(varId);
|
|
456
|
+
if (varSpec !== void 0) {
|
|
457
|
+
varSpecs.push(varSpec);
|
|
458
|
+
} else {
|
|
459
|
+
console.warn(`WARNING: No output var spec found for id=${varId}`);
|
|
460
|
+
}
|
|
461
|
+
}
|
|
462
|
+
const newOutputs = new Outputs(varIds, normalOutputs.startTime, normalOutputs.endTime, normalOutputs.saveFreq);
|
|
463
|
+
newOutputs.varSpecs = varSpecs;
|
|
464
|
+
return newOutputs;
|
|
465
|
+
}
|
|
466
|
+
};
|
|
467
|
+
function varIdWithoutSubscripts(fullVarId) {
|
|
468
|
+
const bracketIndex = fullVarId.indexOf("[");
|
|
469
|
+
if (bracketIndex >= 0) {
|
|
470
|
+
return fullVarId.substring(0, bracketIndex);
|
|
471
|
+
} else {
|
|
472
|
+
return fullVarId;
|
|
473
|
+
}
|
|
474
|
+
}
|
|
475
|
+
function cartesianProductOf(arr) {
|
|
476
|
+
return arr.reduce(
|
|
477
|
+
(a, b) => {
|
|
478
|
+
return a.map((x) => b.map((y) => x.concat([y]))).reduce((v, w) => v.concat(w), []);
|
|
479
|
+
},
|
|
480
|
+
[[]]
|
|
481
|
+
);
|
|
482
|
+
}
|
|
483
|
+
|
|
216
484
|
// src/model-scheduler/model-scheduler.ts
|
|
217
485
|
var ModelScheduler = class {
|
|
486
|
+
/**
|
|
487
|
+
* @param runner The model runner.
|
|
488
|
+
* @param userInputs The input values, in the same order as in the spec file passed to `sde`.
|
|
489
|
+
* @param outputs The structure into which the model outputs will be stored.
|
|
490
|
+
*/
|
|
218
491
|
constructor(runner, userInputs, outputs) {
|
|
219
492
|
this.runner = runner;
|
|
220
493
|
this.userInputs = userInputs;
|
|
221
494
|
this.outputs = outputs;
|
|
495
|
+
/** Whether a model run has been scheduled. */
|
|
222
496
|
this.runNeeded = false;
|
|
497
|
+
/** Whether a model run is in progress. */
|
|
223
498
|
this.runInProgress = false;
|
|
224
499
|
const afterSet = () => {
|
|
225
500
|
this.runWasmModelIfNeeded();
|
|
@@ -232,6 +507,10 @@ var ModelScheduler = class {
|
|
|
232
507
|
this.currentInputs.push(createSimpleInputValue(userInput.varId));
|
|
233
508
|
}
|
|
234
509
|
}
|
|
510
|
+
/**
|
|
511
|
+
* Schedule a wasm model run (if not already pending). When the run is
|
|
512
|
+
* complete, save the outputs and call the `onOutputsChanged` callback.
|
|
513
|
+
*/
|
|
235
514
|
runWasmModelIfNeeded() {
|
|
236
515
|
this.runNeeded = true;
|
|
237
516
|
if (this.runInProgress) {
|
|
@@ -243,26 +522,31 @@ var ModelScheduler = class {
|
|
|
243
522
|
}, 0);
|
|
244
523
|
}
|
|
245
524
|
}
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
525
|
+
/**
|
|
526
|
+
* Run the wasm model asynchronously using the current set of input values.
|
|
527
|
+
*/
|
|
528
|
+
runWasmModelNow() {
|
|
529
|
+
return __async(this, null, function* () {
|
|
530
|
+
var _a;
|
|
531
|
+
for (let i = 0; i < this.userInputs.length; i++) {
|
|
532
|
+
this.currentInputs[i].set(this.userInputs[i].get());
|
|
533
|
+
}
|
|
534
|
+
try {
|
|
535
|
+
this.outputs = yield this.runner.runModel(this.currentInputs, this.outputs);
|
|
536
|
+
(_a = this.onOutputsChanged) == null ? void 0 : _a.call(this, this.outputs);
|
|
537
|
+
} catch (e) {
|
|
538
|
+
console.error(`ERROR: Failed to run model: ${e.message}`);
|
|
539
|
+
}
|
|
540
|
+
if (this.runNeeded) {
|
|
541
|
+
this.runNeeded = false;
|
|
542
|
+
setTimeout(() => {
|
|
543
|
+
this.runWasmModelNow();
|
|
544
|
+
}, 0);
|
|
545
|
+
} else {
|
|
546
|
+
this.runNeeded = false;
|
|
547
|
+
this.runInProgress = false;
|
|
548
|
+
}
|
|
549
|
+
});
|
|
266
550
|
}
|
|
267
551
|
};
|
|
268
552
|
function createSimpleInputValue(varId) {
|
|
@@ -279,15 +563,19 @@ function createSimpleInputValue(varId) {
|
|
|
279
563
|
return { varId, get, set, reset, callbacks: {} };
|
|
280
564
|
}
|
|
281
565
|
export {
|
|
566
|
+
ModelListing,
|
|
282
567
|
ModelScheduler,
|
|
283
568
|
Outputs,
|
|
284
569
|
Series,
|
|
285
570
|
WasmBuffer,
|
|
286
571
|
WasmModel,
|
|
572
|
+
createFloat64WasmBuffer,
|
|
287
573
|
createInputValue,
|
|
574
|
+
createInt32WasmBuffer,
|
|
288
575
|
createWasmModelRunner,
|
|
289
576
|
initWasmModelAndBuffers,
|
|
290
577
|
perfElapsed,
|
|
291
|
-
perfNow
|
|
578
|
+
perfNow,
|
|
579
|
+
updateOutputIndices
|
|
292
580
|
};
|
|
293
581
|
//# sourceMappingURL=index.js.map
|