@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/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
- declare type InputVarId = string;
4
+ type InputVarId = string;
5
5
  /** An output variable identifier string, as used in SDEverywhere. */
6
- declare type OutputVarId = string;
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 numElements The number of 64-bit `double` elements in the buffer.
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, numElements: number);
57
+ constructor(wasmModule: WasmModule, byteOffset: number, heapArray: ArrType);
45
58
  /**
46
- * @return A new `Float64Array` view on the underlying heap buffer.
59
+ * @return An `ArrType` view on the underlying heap buffer.
47
60
  */
48
- getArrayView(): Float64Array;
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, outputs: WasmBuffer): void;
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
- declare type ParseError = 'invalid-point-count';
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
- constructor(wasmModule, numElements) {
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
- const sizeOfFloat64 = 8;
6
- const lengthInBytes = numElements * sizeOfFloat64;
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
- runModel(inputs, outputs) {
40
- this.wasmRunModel(inputs.getAddress(), outputs.getAddress());
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 = new WasmBuffer(wasmModule, numInputs);
46
- const outputsBuffer = new WasmBuffer(wasmModule, outputVarIds.length * model.numSavePoints);
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) => ({ ...p }));
88
- return new Series(this.varId, pointsCopy);
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
- async runWasmModelNow() {
247
- var _a;
248
- for (let i = 0; i < this.userInputs.length; i++) {
249
- this.currentInputs[i].set(this.userInputs[i].get());
250
- }
251
- try {
252
- this.outputs = await this.runner.runModel(this.currentInputs, this.outputs);
253
- (_a = this.onOutputsChanged) == null ? void 0 : _a.call(this, this.outputs);
254
- } catch (e) {
255
- console.error(`ERROR: Failed to run model: ${e.message}`);
256
- }
257
- if (this.runNeeded) {
258
- this.runNeeded = false;
259
- setTimeout(() => {
260
- this.runWasmModelNow();
261
- }, 0);
262
- } else {
263
- this.runNeeded = false;
264
- this.runInProgress = false;
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