@sdeverywhere/runtime 0.2.2 → 0.2.3

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.js CHANGED
@@ -35,128 +35,7 @@ var __async = (__this, __arguments, generator) => {
35
35
  });
36
36
  };
37
37
 
38
- // src/wasm-model/wasm-buffer.ts
39
- var WasmBuffer = class {
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) {
46
- this.wasmModule = wasmModule;
47
- this.byteOffset = byteOffset;
48
- this.heapArray = heapArray;
49
- }
50
- /**
51
- * @return An `ArrType` view on the underlying heap buffer.
52
- */
53
- getArrayView() {
54
- return this.heapArray;
55
- }
56
- /**
57
- * @return The raw address of the underlying heap buffer.
58
- * @hidden This is intended for use by `WasmModel` only.
59
- */
60
- getAddress() {
61
- return this.byteOffset;
62
- }
63
- /**
64
- * Dispose the buffer by freeing the allocated heap memory.
65
- */
66
- dispose() {
67
- if (this.heapArray) {
68
- this.wasmModule._free(this.byteOffset);
69
- this.heapArray = void 0;
70
- this.byteOffset = void 0;
71
- }
72
- }
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
- }
90
-
91
- // src/wasm-model/wasm-model.ts
92
- var indicesPerOutput = 4;
93
- var WasmModel = class {
94
- /**
95
- * @param wasmModule The `WasmModule` that provides access to the native functions.
96
- */
97
- constructor(wasmModule) {
98
- function getNumberValue(funcName) {
99
- const wasmGetValue = wasmModule.cwrap(funcName, "number", []);
100
- return wasmGetValue();
101
- }
102
- this.startTime = getNumberValue("getInitialTime");
103
- this.endTime = getNumberValue("getFinalTime");
104
- this.saveFreq = getNumberValue("getSaveper");
105
- try {
106
- this.maxOutputIndices = getNumberValue("getMaxOutputIndices");
107
- } catch (e) {
108
- this.maxOutputIndices = 0;
109
- }
110
- this.numSavePoints = Math.round((this.endTime - this.startTime) / this.saveFreq) + 1;
111
- this.wasmRunModel = wasmModule.cwrap("runModelWithBuffers", null, ["number", "number", "number"]);
112
- }
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);
123
- }
124
- };
125
- function initWasmModelAndBuffers(wasmModule, numInputs, outputVarIds) {
126
- const model = new WasmModel(wasmModule);
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
- }
134
- return {
135
- model,
136
- inputsBuffer,
137
- outputsBuffer,
138
- outputIndicesBuffer,
139
- outputVarIds
140
- };
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
- }
158
-
159
- // src/model-runner/inputs.ts
38
+ // src/_shared/inputs.ts
160
39
  function createInputValue(varId, defaultValue, initialValue) {
161
40
  let currentValue = initialValue !== void 0 ? initialValue : defaultValue;
162
41
  const callbacks = {};
@@ -176,7 +55,7 @@ function createInputValue(varId, defaultValue, initialValue) {
176
55
  return { varId, get, set, reset, callbacks };
177
56
  }
178
57
 
179
- // src/model-runner/outputs.ts
58
+ // src/_shared/outputs.ts
180
59
  import { ok, err } from "neverthrow";
181
60
  var Series = class _Series {
182
61
  /**
@@ -303,102 +182,132 @@ function validateNumber(x) {
303
182
  }
304
183
  }
305
184
 
306
- // src/model-runner/perf.ts
307
- var isWeb;
308
- function perfNow() {
309
- if (isWeb === void 0) {
310
- isWeb = typeof self !== "undefined" && (self == null ? void 0 : self.performance) !== void 0;
185
+ // src/_shared/var-indices.ts
186
+ function getEncodedVarIndicesLength(varSpecs) {
187
+ var _a;
188
+ let length = 1;
189
+ for (const varSpec of varSpecs) {
190
+ length += 2;
191
+ const subCount = ((_a = varSpec.subscriptIndices) == null ? void 0 : _a.length) || 0;
192
+ length += subCount;
311
193
  }
312
- if (isWeb) {
313
- return self.performance.now();
314
- } else {
315
- return process == null ? void 0 : process.hrtime();
194
+ return length;
195
+ }
196
+ function encodeVarIndices(varSpecs, indicesArray) {
197
+ let offset = 0;
198
+ indicesArray[offset++] = varSpecs.length;
199
+ for (const varSpec of varSpecs) {
200
+ indicesArray[offset++] = varSpec.varIndex;
201
+ const subs = varSpec.subscriptIndices;
202
+ const subCount = (subs == null ? void 0 : subs.length) || 0;
203
+ indicesArray[offset++] = subCount;
204
+ for (let i = 0; i < subCount; i++) {
205
+ indicesArray[offset++] = subs[i];
206
+ }
316
207
  }
317
208
  }
318
- function perfElapsed(t0) {
319
- if (isWeb) {
320
- const t1 = self.performance.now();
321
- return t1 - t0;
322
- } else {
323
- const elapsed = process.hrtime(t0);
324
- return (elapsed[0] * 1e9 + elapsed[1]) / 1e6;
209
+ function getEncodedLookupBufferLengths(lookupDefs) {
210
+ var _a;
211
+ let lookupIndicesLength = 1;
212
+ let lookupsLength = 0;
213
+ for (const lookupDef of lookupDefs) {
214
+ const varSpec = lookupDef.varRef.varSpec;
215
+ if (varSpec === void 0) {
216
+ throw new Error("Cannot compute lookup buffer lengths until all lookup var specs are defined");
217
+ }
218
+ lookupIndicesLength += 2;
219
+ const subCount = ((_a = varSpec.subscriptIndices) == null ? void 0 : _a.length) || 0;
220
+ lookupIndicesLength += subCount;
221
+ lookupIndicesLength += 2;
222
+ lookupsLength += lookupDef.points.length;
325
223
  }
224
+ return {
225
+ lookupIndicesLength,
226
+ lookupsLength
227
+ };
326
228
  }
327
-
328
- // src/model-runner/model-runner.ts
329
- function createWasmModelRunner(wasmResult) {
330
- const wasmModel = wasmResult.model;
331
- const inputsBuffer = wasmResult.inputsBuffer;
332
- const inputsArray = inputsBuffer.getArrayView();
333
- const outputsBuffer = wasmResult.outputsBuffer;
334
- const outputsArray = outputsBuffer.getArrayView();
335
- const outputIndicesBuffer = wasmResult.outputIndicesBuffer;
336
- const outputIndicesArray = outputIndicesBuffer == null ? void 0 : outputIndicesBuffer.getArrayView();
337
- const rowLength = wasmModel.numSavePoints;
338
- let terminated = false;
339
- const runModelSync = (inputs, outputs) => {
340
- let i = 0;
341
- for (const input of inputs) {
342
- inputsArray[i++] = input.get();
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;
229
+ function encodeLookups(lookupDefs, lookupIndicesArray, lookupsArray) {
230
+ let li = 0;
231
+ lookupIndicesArray[li++] = lookupDefs.length;
232
+ let lookupDataOffset = 0;
233
+ for (const lookupDef of lookupDefs) {
234
+ const varSpec = lookupDef.varRef.varSpec;
235
+ lookupIndicesArray[li++] = varSpec.varIndex;
236
+ const subs = varSpec.subscriptIndices;
237
+ const subCount = (subs == null ? void 0 : subs.length) || 0;
238
+ lookupIndicesArray[li++] = subCount;
239
+ for (let i = 0; i < subCount; i++) {
240
+ lookupIndicesArray[li++] = subs[i];
241
+ }
242
+ lookupIndicesArray[li++] = lookupDataOffset;
243
+ lookupIndicesArray[li++] = lookupDef.points.length;
244
+ lookupsArray == null ? void 0 : lookupsArray.set(lookupDef.points, lookupDataOffset);
245
+ lookupDataOffset += lookupDef.points.length;
246
+ }
247
+ }
248
+ function decodeLookups(lookupIndicesArray, lookupsArray) {
249
+ const lookupDefs = [];
250
+ let li = 0;
251
+ const lookupCount = lookupIndicesArray[li++];
252
+ for (let i = 0; i < lookupCount; i++) {
253
+ const varIndex = lookupIndicesArray[li++];
254
+ const subCount = lookupIndicesArray[li++];
255
+ const subscriptIndices = subCount > 0 ? Array(subCount) : void 0;
256
+ for (let subIndex = 0; subIndex < subCount; subIndex++) {
257
+ subscriptIndices[subIndex] = lookupIndicesArray[li++];
258
+ }
259
+ const lookupDataOffset = lookupIndicesArray[li++];
260
+ const lookupDataLength = lookupIndicesArray[li++];
261
+ const varSpec = {
262
+ varIndex,
263
+ subscriptIndices
264
+ };
265
+ let points;
266
+ if (lookupsArray) {
267
+ points = lookupsArray.slice(lookupDataOffset, lookupDataOffset + lookupDataLength);
349
268
  } else {
350
- useIndices = false;
269
+ points = new Float64Array(0);
351
270
  }
352
- const t0 = perfNow();
353
- wasmModel.runModel(inputsBuffer, outputsBuffer, useIndices ? outputIndicesBuffer : void 0);
354
- outputs.runTimeInMillis = perfElapsed(t0);
355
- outputs.updateFromBuffer(outputsArray, rowLength);
356
- return outputs;
357
- };
271
+ lookupDefs.push({
272
+ varRef: {
273
+ varSpec
274
+ },
275
+ points
276
+ });
277
+ }
278
+ return lookupDefs;
279
+ }
280
+
281
+ // src/_shared/lookup-def.ts
282
+ function createLookupDef(varRef, points) {
283
+ const flatPoints = new Float64Array(points.length * 2);
284
+ let i = 0;
285
+ for (const p of points) {
286
+ flatPoints[i++] = p.x;
287
+ flatPoints[i++] = p.y;
288
+ }
358
289
  return {
359
- createOutputs: () => {
360
- return new Outputs(wasmResult.outputVarIds, wasmModel.startTime, wasmModel.endTime, wasmModel.saveFreq);
361
- },
362
- runModel: (inputs, outputs) => {
363
- if (terminated) {
364
- return Promise.reject(new Error("Model runner has already been terminated"));
365
- }
366
- return Promise.resolve(runModelSync(inputs, outputs));
367
- },
368
- runModelSync: (inputs, outputs) => {
369
- if (terminated) {
370
- throw new Error("Model runner has already been terminated");
371
- }
372
- return runModelSync(inputs, outputs);
373
- },
374
- terminate: () => {
375
- if (!terminated) {
376
- terminated = true;
377
- }
378
- return Promise.resolve();
379
- }
290
+ varRef,
291
+ points: flatPoints
380
292
  };
381
293
  }
382
294
 
383
- // src/model-runner/model-listing.ts
295
+ // src/model-listing/model-listing.ts
384
296
  var ModelListing = class {
385
- constructor(modelJsonString) {
297
+ constructor(listingObj) {
386
298
  this.varSpecs = /* @__PURE__ */ new Map();
387
- const modelJson = JSON.parse(modelJsonString);
388
299
  const dimensions = /* @__PURE__ */ new Map();
389
- for (const dimInfo of modelJson.dimensions) {
390
- const dimId = dimInfo.name;
300
+ for (const dimInfo of listingObj.dimensions) {
301
+ const dimId = dimInfo.id;
391
302
  const subscripts = [];
392
- for (let i = 0; i < dimInfo.value.length; i++) {
303
+ for (let i = 0; i < dimInfo.subIds.length; i++) {
393
304
  subscripts.push({
394
- id: dimInfo.value[i],
395
- // name: dimInfo.modelValue[i]
305
+ id: dimInfo.subIds[i],
396
306
  index: i
397
307
  });
398
308
  }
399
309
  dimensions.set(dimId, {
400
310
  id: dimId,
401
- // name: dimInfo.modelName,
402
311
  subscripts
403
312
  });
404
313
  }
@@ -410,15 +319,12 @@ var ModelListing = class {
410
319
  return dim;
411
320
  }
412
321
  const baseVarIds = /* @__PURE__ */ new Set();
413
- for (const v of modelJson.variables) {
414
- const baseVarId = varIdWithoutSubscripts(v.varName);
322
+ for (const v of listingObj.variables) {
323
+ const baseVarId = varIdWithoutSubscripts(v.id);
415
324
  if (!baseVarIds.has(baseVarId)) {
416
- const dimIds = v.families || [];
325
+ const dimIds = v.dimIds || [];
417
326
  const dimensions2 = dimIds.map(dimensionForId);
418
327
  if (dimensions2.length > 0) {
419
- if (dimensions2.length > 3) {
420
- throw new Error("Variables with more than 3 dimensions not currently supported");
421
- }
422
328
  const dimSubs = [];
423
329
  for (const dim of dimensions2) {
424
330
  dimSubs.push(dim.subscripts);
@@ -429,19 +335,34 @@ var ModelListing = class {
429
335
  const subIndices = combo.map((sub) => sub.index);
430
336
  const fullVarId = `${baseVarId}[${subs}]`;
431
337
  this.varSpecs.set(fullVarId, {
432
- varIndex: v.varIndex,
338
+ varIndex: v.index,
433
339
  subscriptIndices: subIndices
434
340
  });
435
341
  }
436
342
  } else {
437
343
  this.varSpecs.set(baseVarId, {
438
- varIndex: v.varIndex
344
+ varIndex: v.index
439
345
  });
440
346
  }
441
347
  baseVarIds.add(baseVarId);
442
348
  }
443
349
  }
444
350
  }
351
+ /**
352
+ * Return the `VarSpec` for the given variable ID, or undefined if there is no spec defined
353
+ * in the listing for that variable.
354
+ */
355
+ getSpecForVarId(varId) {
356
+ return this.varSpecs.get(varId);
357
+ }
358
+ /**
359
+ * Return the `VarSpec` for the given variable name, or undefined if there is no spec defined
360
+ * in the listing for that variable.
361
+ */
362
+ getSpecForVarName(varName) {
363
+ const varId = sdeVarIdForVensimVarName(varName);
364
+ return this.varSpecs.get(varId);
365
+ }
445
366
  /**
446
367
  * Create a new `Outputs` instance that uses the same start/end years as the given "normal"
447
368
  * `Outputs` instance but is prepared for reading the specified internal variables from the model.
@@ -480,6 +401,1382 @@ function cartesianProductOf(arr) {
480
401
  [[]]
481
402
  );
482
403
  }
404
+ function sdeVarIdForVensimName(name) {
405
+ return "_" + name.trim().replace(/"/g, "_").replace(/\s+!$/g, "!").replace(/\s/g, "_").replace(/,/g, "_").replace(/-/g, "_").replace(/\./g, "_").replace(/\$/g, "_").replace(/'/g, "_").replace(/&/g, "_").replace(/%/g, "_").replace(/\//g, "_").replace(/\|/g, "_").toLowerCase();
406
+ }
407
+ function sdeVarIdForVensimVarName(varName) {
408
+ const m = varName.match(/([^[]+)(?:\[([^\]]+)\])?/);
409
+ if (!m) {
410
+ throw new Error(`Invalid Vensim name: ${varName}`);
411
+ }
412
+ let id = sdeVarIdForVensimName(m[1]);
413
+ if (m[2]) {
414
+ const subscripts = m[2].split(",").map((x) => sdeVarIdForVensimName(x));
415
+ id += `[${subscripts.join(",")}]`;
416
+ }
417
+ return id;
418
+ }
419
+
420
+ // src/runnable-model/resolve-var-ref.ts
421
+ function resolveVarRef(listing, varRef, varKind) {
422
+ if (varRef.varSpec) {
423
+ return;
424
+ }
425
+ if (listing === void 0) {
426
+ throw new Error(
427
+ `Unable to resolve ${varKind} variable references by name or identifier when model listing is unavailable`
428
+ );
429
+ }
430
+ if (varRef.varId) {
431
+ const varSpec = listing == null ? void 0 : listing.getSpecForVarId(varRef.varId);
432
+ if (varSpec) {
433
+ varRef.varSpec = varSpec;
434
+ } else {
435
+ throw new Error(`Failed to resolve ${varKind} variable reference for varId=${varRef.varId}`);
436
+ }
437
+ } else {
438
+ const varSpec = listing == null ? void 0 : listing.getSpecForVarName(varRef.varName);
439
+ if (varSpec) {
440
+ varRef.varSpec = varSpec;
441
+ } else {
442
+ throw new Error(`Failed to resolve ${varKind} variable reference for varName='${varRef.varId}'`);
443
+ }
444
+ }
445
+ }
446
+
447
+ // src/runnable-model/buffered-run-model-params.ts
448
+ var headerLengthInElements = 16;
449
+ var extrasLengthInElements = 1;
450
+ var Int32Section = class {
451
+ constructor() {
452
+ this.offsetInBytes = 0;
453
+ this.lengthInElements = 0;
454
+ }
455
+ update(encoded, offsetInBytes, lengthInElements) {
456
+ this.view = lengthInElements > 0 ? new Int32Array(encoded, offsetInBytes, lengthInElements) : void 0;
457
+ this.offsetInBytes = offsetInBytes;
458
+ this.lengthInElements = lengthInElements;
459
+ }
460
+ };
461
+ var Float64Section = class {
462
+ constructor() {
463
+ this.offsetInBytes = 0;
464
+ this.lengthInElements = 0;
465
+ }
466
+ update(encoded, offsetInBytes, lengthInElements) {
467
+ this.view = lengthInElements > 0 ? new Float64Array(encoded, offsetInBytes, lengthInElements) : void 0;
468
+ this.offsetInBytes = offsetInBytes;
469
+ this.lengthInElements = lengthInElements;
470
+ }
471
+ };
472
+ var BufferedRunModelParams = class {
473
+ /**
474
+ * @param listing The model listing that is used to locate a variable that is referenced by
475
+ * name or identifier. If undefined, variables cannot be referenced by name or identifier,
476
+ * and can only be referenced using a valid `VarSpec`.
477
+ */
478
+ constructor(listing) {
479
+ this.listing = listing;
480
+ /**
481
+ * The header section of the `encoded` buffer. The header declares the byte offset and length
482
+ * (in elements) of each section of the buffer.
483
+ */
484
+ this.header = new Int32Section();
485
+ /** The extras section of the `encoded` buffer (holds elapsed time, etc). */
486
+ this.extras = new Float64Section();
487
+ /** The inputs section of the `encoded` buffer. */
488
+ this.inputs = new Float64Section();
489
+ /** The outputs section of the `encoded` buffer. */
490
+ this.outputs = new Float64Section();
491
+ /** The output indices section of the `encoded` buffer. */
492
+ this.outputIndices = new Int32Section();
493
+ /** The lookup data section of the `encoded` buffer. */
494
+ this.lookups = new Float64Section();
495
+ /** The lookup indices section of the `encoded` buffer. */
496
+ this.lookupIndices = new Int32Section();
497
+ }
498
+ /**
499
+ * Return the encoded buffer from this instance, which can be passed to `updateFromEncodedBuffer`.
500
+ */
501
+ getEncodedBuffer() {
502
+ return this.encoded;
503
+ }
504
+ // from RunModelParams interface
505
+ getInputs() {
506
+ return this.inputs.view;
507
+ }
508
+ // from RunModelParams interface
509
+ copyInputs(array, create) {
510
+ if (this.inputs.lengthInElements === 0) {
511
+ return;
512
+ }
513
+ if (array === void 0 || array.length < this.inputs.lengthInElements) {
514
+ array = create(this.inputs.lengthInElements);
515
+ }
516
+ array.set(this.inputs.view);
517
+ }
518
+ // from RunModelParams interface
519
+ getOutputIndicesLength() {
520
+ return this.outputIndices.lengthInElements;
521
+ }
522
+ // from RunModelParams interface
523
+ getOutputIndices() {
524
+ return this.outputIndices.view;
525
+ }
526
+ // from RunModelParams interface
527
+ copyOutputIndices(array, create) {
528
+ if (this.outputIndices.lengthInElements === 0) {
529
+ return;
530
+ }
531
+ if (array === void 0 || array.length < this.outputIndices.lengthInElements) {
532
+ array = create(this.outputIndices.lengthInElements);
533
+ }
534
+ array.set(this.outputIndices.view);
535
+ }
536
+ // from RunModelParams interface
537
+ getOutputsLength() {
538
+ return this.outputs.lengthInElements;
539
+ }
540
+ // from RunModelParams interface
541
+ getOutputs() {
542
+ return this.outputs.view;
543
+ }
544
+ // from RunModelParams interface
545
+ getOutputsObject() {
546
+ return void 0;
547
+ }
548
+ // from RunModelParams interface
549
+ storeOutputs(array) {
550
+ var _a;
551
+ (_a = this.outputs.view) == null ? void 0 : _a.set(array);
552
+ }
553
+ // from RunModelParams interface
554
+ getLookups() {
555
+ if (this.lookupIndices.lengthInElements === 0) {
556
+ return void 0;
557
+ }
558
+ return decodeLookups(this.lookupIndices.view, this.lookups.view);
559
+ }
560
+ // from RunModelParams interface
561
+ getElapsedTime() {
562
+ return this.extras.view[0];
563
+ }
564
+ // from RunModelParams interface
565
+ storeElapsedTime(elapsed) {
566
+ this.extras.view[0] = elapsed;
567
+ }
568
+ /**
569
+ * Copy the outputs buffer to the given `Outputs` instance. This should be called
570
+ * after the `runModel` call has completed so that the output values are copied from
571
+ * the internal buffer to the `Outputs` instance that was passed to `runModel`.
572
+ *
573
+ * @param outputs The `Outputs` instance into which the output values will be copied.
574
+ */
575
+ finalizeOutputs(outputs) {
576
+ if (this.outputs.view) {
577
+ outputs.updateFromBuffer(this.outputs.view, outputs.seriesLength);
578
+ }
579
+ outputs.runTimeInMillis = this.getElapsedTime();
580
+ }
581
+ /**
582
+ * Update this instance using the parameters that are passed to a `runModel` call.
583
+ *
584
+ * @param inputs The model input values (must be in the same order as in the spec file).
585
+ * @param outputs The structure into which the model outputs will be stored.
586
+ * @param options Additional options that influence the model run.
587
+ */
588
+ updateFromParams(inputs, outputs, options) {
589
+ const inputsLengthInElements = inputs.length;
590
+ const outputsLengthInElements = outputs.varIds.length * outputs.seriesLength;
591
+ let outputIndicesLengthInElements;
592
+ const outputVarSpecs = outputs.varSpecs;
593
+ if (outputVarSpecs !== void 0 && outputVarSpecs.length > 0) {
594
+ outputIndicesLengthInElements = getEncodedVarIndicesLength(outputVarSpecs);
595
+ } else {
596
+ outputIndicesLengthInElements = 0;
597
+ }
598
+ let lookupsLengthInElements;
599
+ let lookupIndicesLengthInElements;
600
+ if ((options == null ? void 0 : options.lookups) !== void 0 && options.lookups.length > 0) {
601
+ for (const lookupDef of options.lookups) {
602
+ resolveVarRef(this.listing, lookupDef.varRef, "lookup");
603
+ }
604
+ const encodedLengths = getEncodedLookupBufferLengths(options.lookups);
605
+ lookupsLengthInElements = encodedLengths.lookupsLength;
606
+ lookupIndicesLengthInElements = encodedLengths.lookupIndicesLength;
607
+ } else {
608
+ lookupsLengthInElements = 0;
609
+ lookupIndicesLengthInElements = 0;
610
+ }
611
+ let byteOffset = 0;
612
+ function section(kind, lengthInElements) {
613
+ const sectionOffsetInBytes = byteOffset;
614
+ const bytesPerElement = kind === "float64" ? Float64Array.BYTES_PER_ELEMENT : Int32Array.BYTES_PER_ELEMENT;
615
+ const requiredSectionLengthInBytes = Math.round(lengthInElements * bytesPerElement);
616
+ const alignedSectionLengthInBytes = Math.ceil(requiredSectionLengthInBytes / 8) * 8;
617
+ byteOffset += alignedSectionLengthInBytes;
618
+ return sectionOffsetInBytes;
619
+ }
620
+ const headerOffsetInBytes = section("int32", headerLengthInElements);
621
+ const extrasOffsetInBytes = section("float64", extrasLengthInElements);
622
+ const inputsOffsetInBytes = section("float64", inputsLengthInElements);
623
+ const outputsOffsetInBytes = section("float64", outputsLengthInElements);
624
+ const outputIndicesOffsetInBytes = section("int32", outputIndicesLengthInElements);
625
+ const lookupsOffsetInBytes = section("float64", lookupsLengthInElements);
626
+ const lookupIndicesOffsetInBytes = section("int32", lookupIndicesLengthInElements);
627
+ const requiredLengthInBytes = byteOffset;
628
+ if (this.encoded === void 0 || this.encoded.byteLength < requiredLengthInBytes) {
629
+ const totalLengthInBytes = Math.ceil(requiredLengthInBytes * 1.2);
630
+ this.encoded = new ArrayBuffer(totalLengthInBytes);
631
+ this.header.update(this.encoded, headerOffsetInBytes, headerLengthInElements);
632
+ }
633
+ const headerView = this.header.view;
634
+ let headerIndex = 0;
635
+ headerView[headerIndex++] = extrasOffsetInBytes;
636
+ headerView[headerIndex++] = extrasLengthInElements;
637
+ headerView[headerIndex++] = inputsOffsetInBytes;
638
+ headerView[headerIndex++] = inputsLengthInElements;
639
+ headerView[headerIndex++] = outputsOffsetInBytes;
640
+ headerView[headerIndex++] = outputsLengthInElements;
641
+ headerView[headerIndex++] = outputIndicesOffsetInBytes;
642
+ headerView[headerIndex++] = outputIndicesLengthInElements;
643
+ headerView[headerIndex++] = lookupsOffsetInBytes;
644
+ headerView[headerIndex++] = lookupsLengthInElements;
645
+ headerView[headerIndex++] = lookupIndicesOffsetInBytes;
646
+ headerView[headerIndex++] = lookupIndicesLengthInElements;
647
+ this.inputs.update(this.encoded, inputsOffsetInBytes, inputsLengthInElements);
648
+ this.extras.update(this.encoded, extrasOffsetInBytes, extrasLengthInElements);
649
+ this.outputs.update(this.encoded, outputsOffsetInBytes, outputsLengthInElements);
650
+ this.outputIndices.update(this.encoded, outputIndicesOffsetInBytes, outputIndicesLengthInElements);
651
+ this.lookups.update(this.encoded, lookupsOffsetInBytes, lookupsLengthInElements);
652
+ this.lookupIndices.update(this.encoded, lookupIndicesOffsetInBytes, lookupIndicesLengthInElements);
653
+ const inputsView = this.inputs.view;
654
+ for (let i = 0; i < inputs.length; i++) {
655
+ const input = inputs[i];
656
+ if (typeof input === "number") {
657
+ inputsView[i] = input;
658
+ } else {
659
+ inputsView[i] = input.get();
660
+ }
661
+ }
662
+ if (this.outputIndices.view) {
663
+ encodeVarIndices(outputVarSpecs, this.outputIndices.view);
664
+ }
665
+ if (lookupIndicesLengthInElements > 0) {
666
+ encodeLookups(options.lookups, this.lookupIndices.view, this.lookups.view);
667
+ }
668
+ }
669
+ /**
670
+ * Update this instance using the values contained in the encoded buffer from another
671
+ * `BufferedRunModelParams` instance.
672
+ *
673
+ * @param buffer An encoded buffer returned by `getEncodedBuffer`.
674
+ */
675
+ updateFromEncodedBuffer(buffer) {
676
+ const headerLengthInBytes = headerLengthInElements * Int32Array.BYTES_PER_ELEMENT;
677
+ if (buffer.byteLength < headerLengthInBytes) {
678
+ throw new Error("Buffer must be long enough to contain header section");
679
+ }
680
+ this.encoded = buffer;
681
+ const headerOffsetInBytes = 0;
682
+ this.header.update(this.encoded, headerOffsetInBytes, headerLengthInElements);
683
+ const headerView = this.header.view;
684
+ let headerIndex = 0;
685
+ const extrasOffsetInBytes = headerView[headerIndex++];
686
+ const extrasLengthInElements2 = headerView[headerIndex++];
687
+ const inputsOffsetInBytes = headerView[headerIndex++];
688
+ const inputsLengthInElements = headerView[headerIndex++];
689
+ const outputsOffsetInBytes = headerView[headerIndex++];
690
+ const outputsLengthInElements = headerView[headerIndex++];
691
+ const outputIndicesOffsetInBytes = headerView[headerIndex++];
692
+ const outputIndicesLengthInElements = headerView[headerIndex++];
693
+ const lookupsOffsetInBytes = headerView[headerIndex++];
694
+ const lookupsLengthInElements = headerView[headerIndex++];
695
+ const lookupIndicesOffsetInBytes = headerView[headerIndex++];
696
+ const lookupIndicesLengthInElements = headerView[headerIndex++];
697
+ const extrasLengthInBytes = extrasLengthInElements2 * Float64Array.BYTES_PER_ELEMENT;
698
+ const inputsLengthInBytes = inputsLengthInElements * Float64Array.BYTES_PER_ELEMENT;
699
+ const outputsLengthInBytes = outputsLengthInElements * Float64Array.BYTES_PER_ELEMENT;
700
+ const outputIndicesLengthInBytes = outputIndicesLengthInElements * Int32Array.BYTES_PER_ELEMENT;
701
+ const lookupsLengthInBytes = lookupsLengthInElements * Float64Array.BYTES_PER_ELEMENT;
702
+ const lookupIndicesLengthInBytes = lookupIndicesLengthInElements * Int32Array.BYTES_PER_ELEMENT;
703
+ const requiredLengthInBytes = headerLengthInBytes + extrasLengthInBytes + inputsLengthInBytes + outputsLengthInBytes + outputIndicesLengthInBytes + lookupsLengthInBytes + lookupIndicesLengthInBytes;
704
+ if (buffer.byteLength < requiredLengthInBytes) {
705
+ throw new Error("Buffer must be long enough to contain sections declared in header");
706
+ }
707
+ this.extras.update(this.encoded, extrasOffsetInBytes, extrasLengthInElements2);
708
+ this.inputs.update(this.encoded, inputsOffsetInBytes, inputsLengthInElements);
709
+ this.outputs.update(this.encoded, outputsOffsetInBytes, outputsLengthInElements);
710
+ this.outputIndices.update(this.encoded, outputIndicesOffsetInBytes, outputIndicesLengthInElements);
711
+ this.lookups.update(this.encoded, lookupsOffsetInBytes, lookupsLengthInElements);
712
+ this.lookupIndices.update(this.encoded, lookupIndicesOffsetInBytes, lookupIndicesLengthInElements);
713
+ }
714
+ };
715
+
716
+ // src/runnable-model/referenced-run-model-params.ts
717
+ var ReferencedRunModelParams = class {
718
+ /**
719
+ * @param listing The model listing that is used to locate a variable that is referenced by
720
+ * name or identifier. If undefined, variables cannot be referenced by name or identifier,
721
+ * and can only be referenced using a valid `VarSpec`.
722
+ */
723
+ constructor(listing) {
724
+ this.listing = listing;
725
+ this.outputsLengthInElements = 0;
726
+ this.outputIndicesLengthInElements = 0;
727
+ }
728
+ // from RunModelParams interface
729
+ getInputs() {
730
+ return void 0;
731
+ }
732
+ // from RunModelParams interface
733
+ copyInputs(array, create) {
734
+ const inputsLengthInElements = this.inputs.length;
735
+ if (array === void 0 || array.length < inputsLengthInElements) {
736
+ array = create(inputsLengthInElements);
737
+ }
738
+ for (let i = 0; i < this.inputs.length; i++) {
739
+ const input = this.inputs[i];
740
+ if (typeof input === "number") {
741
+ array[i] = input;
742
+ } else {
743
+ array[i] = input.get();
744
+ }
745
+ }
746
+ }
747
+ // from RunModelParams interface
748
+ getOutputIndicesLength() {
749
+ return this.outputIndicesLengthInElements;
750
+ }
751
+ // from RunModelParams interface
752
+ getOutputIndices() {
753
+ return void 0;
754
+ }
755
+ // from RunModelParams interface
756
+ copyOutputIndices(array, create) {
757
+ if (this.outputIndicesLengthInElements === 0) {
758
+ return;
759
+ }
760
+ if (array === void 0 || array.length < this.outputIndicesLengthInElements) {
761
+ array = create(this.outputIndicesLengthInElements);
762
+ }
763
+ encodeVarIndices(this.outputs.varSpecs, array);
764
+ }
765
+ // from RunModelParams interface
766
+ getOutputsLength() {
767
+ return this.outputsLengthInElements;
768
+ }
769
+ // from RunModelParams interface
770
+ getOutputs() {
771
+ return void 0;
772
+ }
773
+ // from RunModelParams interface
774
+ getOutputsObject() {
775
+ return this.outputs;
776
+ }
777
+ // from RunModelParams interface
778
+ storeOutputs(array) {
779
+ if (this.outputs) {
780
+ const result = this.outputs.updateFromBuffer(array, this.outputs.seriesLength);
781
+ if (result.isErr()) {
782
+ throw new Error(`Failed to store outputs: ${result.error}`);
783
+ }
784
+ }
785
+ }
786
+ // from RunModelParams interface
787
+ getLookups() {
788
+ if (this.lookups !== void 0 && this.lookups.length > 0) {
789
+ return this.lookups;
790
+ } else {
791
+ return void 0;
792
+ }
793
+ }
794
+ // from RunModelParams interface
795
+ getElapsedTime() {
796
+ var _a;
797
+ return (_a = this.outputs) == null ? void 0 : _a.runTimeInMillis;
798
+ }
799
+ // from RunModelParams interface
800
+ storeElapsedTime(elapsed) {
801
+ if (this.outputs) {
802
+ this.outputs.runTimeInMillis = elapsed;
803
+ }
804
+ }
805
+ /**
806
+ * Update this instance using the parameters that are passed to a `runModel` call.
807
+ *
808
+ * @param inputs The model input values (must be in the same order as in the spec file).
809
+ * @param outputs The structure into which the model outputs will be stored.
810
+ * @param options Additional options that influence the model run.
811
+ */
812
+ updateFromParams(inputs, outputs, options) {
813
+ this.inputs = inputs;
814
+ this.outputs = outputs;
815
+ this.outputsLengthInElements = outputs.varIds.length * outputs.seriesLength;
816
+ this.lookups = options == null ? void 0 : options.lookups;
817
+ if (this.lookups) {
818
+ for (const lookupDef of this.lookups) {
819
+ resolveVarRef(this.listing, lookupDef.varRef, "lookup");
820
+ }
821
+ }
822
+ const outputVarSpecs = outputs.varSpecs;
823
+ if (outputVarSpecs !== void 0 && outputVarSpecs.length > 0) {
824
+ this.outputIndicesLengthInElements = getEncodedVarIndicesLength(outputVarSpecs);
825
+ } else {
826
+ this.outputIndicesLengthInElements = 0;
827
+ }
828
+ }
829
+ };
830
+
831
+ // src/js-model/js-model-constants.ts
832
+ var _NA_ = -Number.MAX_VALUE;
833
+
834
+ // src/js-model/js-model-lookup.ts
835
+ var JsModelLookup = class {
836
+ /**
837
+ * @param n The number of (x,y) pairs in the lookup.
838
+ * @param data The lookup data, as (x,y) pairs. The length of the array must be
839
+ * >= 2*n. Note that the data will be stored by reference, so if there is a chance
840
+ * that the array will be reused or modified by other code, be sure to pass in a
841
+ * copy of the array.
842
+ */
843
+ constructor(n, data) {
844
+ this.n = n;
845
+ this.data = data;
846
+ if (data.length < n * 2) {
847
+ throw new Error(`Lookup data array length must be >= 2*size (length=${data.length} size=${n}`);
848
+ }
849
+ this.lastInput = Number.MAX_VALUE;
850
+ this.lastHitIndex = 0;
851
+ }
852
+ getValueForX(x, mode) {
853
+ return this.getValue(x, false, mode);
854
+ }
855
+ getValueForY(y) {
856
+ if (this.invertedData === void 0) {
857
+ const numValues = this.n * 2;
858
+ const normalData = this.data;
859
+ const invertedData = Array(numValues);
860
+ for (let i = 0; i < numValues; i += 2) {
861
+ invertedData[i] = normalData[i + 1];
862
+ invertedData[i + 1] = normalData[i];
863
+ }
864
+ this.invertedData = invertedData;
865
+ }
866
+ return this.getValue(y, true, "interpolate");
867
+ }
868
+ /**
869
+ * Interpolate the y value from the array of (x,y) pairs.
870
+ * NOTE: The x values are assumed to be monotonically increasing.
871
+ */
872
+ getValue(input, useInvertedData, mode) {
873
+ if (this.n === 0) {
874
+ return _NA_;
875
+ }
876
+ const data = useInvertedData ? this.invertedData : this.data;
877
+ const max = this.n * 2;
878
+ const useCachedValues = !useInvertedData;
879
+ let startIndex;
880
+ if (useCachedValues && input >= this.lastInput) {
881
+ startIndex = this.lastHitIndex;
882
+ } else {
883
+ startIndex = 0;
884
+ }
885
+ for (let xi = startIndex; xi < max; xi += 2) {
886
+ const x = data[xi];
887
+ if (x >= input) {
888
+ if (useCachedValues) {
889
+ this.lastInput = input;
890
+ this.lastHitIndex = xi;
891
+ }
892
+ if (xi === 0 || x === input) {
893
+ return data[xi + 1];
894
+ }
895
+ switch (mode) {
896
+ default:
897
+ case "interpolate": {
898
+ const last_x = data[xi - 2];
899
+ const last_y = data[xi - 1];
900
+ const y = data[xi + 1];
901
+ const dx = x - last_x;
902
+ const dy = y - last_y;
903
+ return last_y + dy / dx * (input - last_x);
904
+ }
905
+ case "forward":
906
+ return data[xi + 1];
907
+ case "backward":
908
+ return data[xi - 1];
909
+ }
910
+ }
911
+ }
912
+ if (useCachedValues) {
913
+ this.lastInput = input;
914
+ this.lastHitIndex = max;
915
+ }
916
+ return data[max - 1];
917
+ }
918
+ /**
919
+ * Return the most appropriate y value from the array of (x,y) pairs when
920
+ * this instance is used to provide inputs for the `GAME` function.
921
+ *
922
+ * NOTE: The x values are assumed to be monotonically increasing.
923
+ *
924
+ * This method is similar to `getValueForX` in concept, except that this one
925
+ * returns the provided `defaultValue` if the `time` parameter is earlier than
926
+ * the first data point in the lookup. Also, this method always uses the
927
+ * `backward` interpolation mode, meaning that it holds the "current" value
928
+ * constant instead of interpolating.
929
+ *
930
+ * @param time The time that is used to select the data point that has an
931
+ * `x` value less than or equal to the provided time.
932
+ * @param defaultValue The value that is returned if this lookup is empty (has
933
+ * no points) or if the provided time is earlier than the first data point.
934
+ */
935
+ getValueForGameTime(time, defaultValue) {
936
+ if (this.n <= 0) {
937
+ return defaultValue;
938
+ }
939
+ const x0 = this.data[0];
940
+ if (time < x0) {
941
+ return defaultValue;
942
+ }
943
+ return this.getValue(time, false, "backward");
944
+ }
945
+ /**
946
+ * Interpolate the y value from the array of (x,y) pairs.
947
+ * NOTE: The x values are assumed to be monotonically increasing.
948
+ *
949
+ * This method is similar to `getValue` in concept, but Vensim produces results for
950
+ * the `GET DATA BETWEEN TIMES` function that differ in unexpected ways from normal
951
+ * lookup behavior, so we implement it as a separate method here.
952
+ */
953
+ getValueBetweenTimes(input, mode) {
954
+ if (this.n === 0) {
955
+ return _NA_;
956
+ }
957
+ const max = this.n * 2;
958
+ switch (mode) {
959
+ case "forward": {
960
+ input = Math.floor(input);
961
+ for (let xi = 0; xi < max; xi += 2) {
962
+ const x = this.data[xi];
963
+ if (x >= input) {
964
+ return this.data[xi + 1];
965
+ }
966
+ }
967
+ return this.data[max - 1];
968
+ }
969
+ case "backward": {
970
+ input = Math.floor(input);
971
+ for (let xi = 2; xi < max; xi += 2) {
972
+ const x = this.data[xi];
973
+ if (x >= input) {
974
+ return this.data[xi - 1];
975
+ }
976
+ }
977
+ if (max >= 4) {
978
+ return this.data[max - 3];
979
+ } else {
980
+ return this.data[1];
981
+ }
982
+ }
983
+ case "interpolate":
984
+ default: {
985
+ if (input - Math.floor(input) > 0) {
986
+ let msg = `GET DATA BETWEEN TIMES was called with an input value (${input}) that has a fractional part. `;
987
+ msg += "When mode is 0 (interpolate) and the input value is not a whole number, Vensim produces unexpected ";
988
+ msg += "results that may differ from those produced by SDEverywhere.";
989
+ throw new Error(msg);
990
+ }
991
+ for (let xi = 2; xi < max; xi += 2) {
992
+ const x = this.data[xi];
993
+ if (x >= input) {
994
+ const last_x = this.data[xi - 2];
995
+ const last_y = this.data[xi - 1];
996
+ const y = this.data[xi + 1];
997
+ const dx = x - last_x;
998
+ const dy = y - last_y;
999
+ return last_y + dy / dx * (input - last_x);
1000
+ }
1001
+ }
1002
+ return this.data[max - 1];
1003
+ }
1004
+ }
1005
+ }
1006
+ };
1007
+
1008
+ // src/js-model/js-model-functions.ts
1009
+ var EPSILON = 1e-6;
1010
+ function getJsModelFunctions() {
1011
+ let ctx;
1012
+ const cachedVectors = /* @__PURE__ */ new Map();
1013
+ const cachedSortVectors = /* @__PURE__ */ new Map();
1014
+ return {
1015
+ setContext(context) {
1016
+ ctx = context;
1017
+ },
1018
+ ABS(x) {
1019
+ return Math.abs(x);
1020
+ },
1021
+ ARCCOS(x) {
1022
+ return Math.acos(x);
1023
+ },
1024
+ ARCSIN(x) {
1025
+ return Math.asin(x);
1026
+ },
1027
+ ARCTAN(x) {
1028
+ return Math.atan(x);
1029
+ },
1030
+ COS(x) {
1031
+ return Math.cos(x);
1032
+ },
1033
+ EXP(x) {
1034
+ return Math.exp(x);
1035
+ },
1036
+ GAME(inputs, x) {
1037
+ return inputs ? inputs.getValueForGameTime(ctx.currentTime, x) : x;
1038
+ },
1039
+ // GAMMA_LN(): number {
1040
+ // throw new Error('GAMMA_LN function not yet implemented for JS target')
1041
+ // },
1042
+ INTEG(value, rate) {
1043
+ return value + rate * ctx.timeStep;
1044
+ },
1045
+ INTEGER(x) {
1046
+ return Math.trunc(x);
1047
+ },
1048
+ LN(x) {
1049
+ return Math.log(x);
1050
+ },
1051
+ MAX(x, y) {
1052
+ return Math.max(x, y);
1053
+ },
1054
+ MIN(x, y) {
1055
+ return Math.min(x, y);
1056
+ },
1057
+ MODULO(x, y) {
1058
+ return x % y;
1059
+ },
1060
+ POW(x, y) {
1061
+ return Math.pow(x, y);
1062
+ },
1063
+ POWER(x, y) {
1064
+ return Math.pow(x, y);
1065
+ },
1066
+ PULSE(start, width) {
1067
+ return pulse(ctx, start, width);
1068
+ },
1069
+ PULSE_TRAIN(start, width, interval, end) {
1070
+ const n = Math.floor((end - start) / interval);
1071
+ for (let k = 0; k <= n; k++) {
1072
+ if (ctx.currentTime <= end && pulse(ctx, start + k * interval, width)) {
1073
+ return 1;
1074
+ }
1075
+ }
1076
+ return 0;
1077
+ },
1078
+ QUANTUM(x, y) {
1079
+ return y <= 0 ? x : y * Math.trunc(x / y);
1080
+ },
1081
+ RAMP(slope, startTime, endTime) {
1082
+ if (ctx.currentTime > startTime) {
1083
+ if (ctx.currentTime < endTime || startTime > endTime) {
1084
+ return slope * (ctx.currentTime - startTime);
1085
+ } else {
1086
+ return slope * (endTime - startTime);
1087
+ }
1088
+ } else {
1089
+ return 0;
1090
+ }
1091
+ },
1092
+ SIN(x) {
1093
+ return Math.sin(x);
1094
+ },
1095
+ SQRT(x) {
1096
+ return Math.sqrt(x);
1097
+ },
1098
+ STEP(height, stepTime) {
1099
+ return ctx.currentTime + ctx.timeStep / 2 > stepTime ? height : 0;
1100
+ },
1101
+ TAN(x) {
1102
+ return Math.tan(x);
1103
+ },
1104
+ VECTOR_SORT_ORDER(vector, size, direction) {
1105
+ if (size > vector.length) {
1106
+ throw new Error(`VECTOR SORT ORDER input vector length (${vector.length}) must be >= size (${size})`);
1107
+ }
1108
+ let sortVector = cachedSortVectors.get(size);
1109
+ if (sortVector === void 0) {
1110
+ sortVector = Array(size);
1111
+ for (let i = 0; i < size; i++) {
1112
+ sortVector[i] = { x: 0, ind: 0 };
1113
+ }
1114
+ cachedSortVectors.set(size, sortVector);
1115
+ }
1116
+ let outArray = cachedVectors.get(size);
1117
+ if (outArray === void 0) {
1118
+ outArray = Array(size);
1119
+ cachedVectors.set(size, outArray);
1120
+ }
1121
+ for (let i = 0; i < size; i++) {
1122
+ sortVector[i].x = vector[i];
1123
+ sortVector[i].ind = i;
1124
+ }
1125
+ const sortOrder = direction > 0 ? 1 : -1;
1126
+ sortVector.sort((a, b) => {
1127
+ let result;
1128
+ if (a.x < b.x) {
1129
+ result = -1;
1130
+ } else if (a.x > b.x) {
1131
+ result = 1;
1132
+ } else {
1133
+ result = 0;
1134
+ }
1135
+ return result * sortOrder;
1136
+ });
1137
+ for (let i = 0; i < size; i++) {
1138
+ outArray[i] = sortVector[i].ind;
1139
+ }
1140
+ return outArray;
1141
+ },
1142
+ XIDZ(a, b, x) {
1143
+ return Math.abs(b) < EPSILON ? x : a / b;
1144
+ },
1145
+ ZIDZ(a, b) {
1146
+ if (Math.abs(b) < EPSILON) {
1147
+ return 0;
1148
+ } else {
1149
+ return a / b;
1150
+ }
1151
+ },
1152
+ //
1153
+ // Lookup functions
1154
+ //
1155
+ createLookup(size, data) {
1156
+ return new JsModelLookup(size, data);
1157
+ },
1158
+ LOOKUP(lookup, x) {
1159
+ return lookup ? lookup.getValueForX(x, "interpolate") : _NA_;
1160
+ },
1161
+ LOOKUP_FORWARD(lookup, x) {
1162
+ return lookup ? lookup.getValueForX(x, "forward") : _NA_;
1163
+ },
1164
+ LOOKUP_BACKWARD(lookup, x) {
1165
+ return lookup ? lookup.getValueForX(x, "backward") : _NA_;
1166
+ },
1167
+ LOOKUP_INVERT(lookup, y) {
1168
+ return lookup ? lookup.getValueForY(y) : _NA_;
1169
+ },
1170
+ WITH_LOOKUP(x, lookup) {
1171
+ return lookup ? lookup.getValueForX(x, "interpolate") : _NA_;
1172
+ },
1173
+ GET_DATA_BETWEEN_TIMES(lookup, x, mode) {
1174
+ let lookupMode;
1175
+ if (mode >= 1) {
1176
+ lookupMode = "forward";
1177
+ } else if (mode <= -1) {
1178
+ lookupMode = "backward";
1179
+ } else {
1180
+ lookupMode = "interpolate";
1181
+ }
1182
+ return lookup ? lookup.getValueBetweenTimes(x, lookupMode) : _NA_;
1183
+ }
1184
+ };
1185
+ }
1186
+ function pulse(ctx, start, width) {
1187
+ const timePlus = ctx.currentTime + ctx.timeStep / 2;
1188
+ if (width === 0) {
1189
+ width = ctx.timeStep;
1190
+ }
1191
+ return timePlus > start && timePlus < start + width ? 1 : 0;
1192
+ }
1193
+
1194
+ // src/perf/perf.ts
1195
+ var isWeb;
1196
+ function perfNow() {
1197
+ if (isWeb === void 0) {
1198
+ isWeb = typeof self !== "undefined" && (self == null ? void 0 : self.performance) !== void 0;
1199
+ }
1200
+ if (isWeb) {
1201
+ return self.performance.now();
1202
+ } else {
1203
+ return process == null ? void 0 : process.hrtime();
1204
+ }
1205
+ }
1206
+ function perfElapsed(t0) {
1207
+ if (isWeb) {
1208
+ const t1 = self.performance.now();
1209
+ return t1 - t0;
1210
+ } else {
1211
+ const elapsed = process.hrtime(t0);
1212
+ return (elapsed[0] * 1e9 + elapsed[1]) / 1e6;
1213
+ }
1214
+ }
1215
+
1216
+ // src/runnable-model/base-runnable-model.ts
1217
+ var BaseRunnableModel = class {
1218
+ constructor(options) {
1219
+ this.startTime = options.startTime;
1220
+ this.endTime = options.endTime;
1221
+ this.saveFreq = options.saveFreq;
1222
+ this.numSavePoints = options.numSavePoints;
1223
+ this.outputVarIds = options.outputVarIds;
1224
+ this.modelListing = options.modelListing;
1225
+ this.onRunModel = options.onRunModel;
1226
+ }
1227
+ // from RunnableModel interface
1228
+ runModel(params) {
1229
+ var _a;
1230
+ let inputsArray = params.getInputs();
1231
+ if (inputsArray === void 0) {
1232
+ params.copyInputs(this.inputs, (numElements) => {
1233
+ this.inputs = new Float64Array(numElements);
1234
+ return this.inputs;
1235
+ });
1236
+ inputsArray = this.inputs;
1237
+ }
1238
+ let outputIndicesArray = params.getOutputIndices();
1239
+ if (outputIndicesArray === void 0 && params.getOutputIndicesLength() > 0) {
1240
+ params.copyOutputIndices(this.outputIndices, (numElements) => {
1241
+ this.outputIndices = new Int32Array(numElements);
1242
+ return this.outputIndices;
1243
+ });
1244
+ outputIndicesArray = this.outputIndices;
1245
+ }
1246
+ const outputsLengthInElements = params.getOutputsLength();
1247
+ if (this.outputs === void 0 || this.outputs.length < outputsLengthInElements) {
1248
+ this.outputs = new Float64Array(outputsLengthInElements);
1249
+ }
1250
+ const outputsArray = this.outputs;
1251
+ const t0 = perfNow();
1252
+ (_a = this.onRunModel) == null ? void 0 : _a.call(this, inputsArray, outputsArray, {
1253
+ outputIndices: outputIndicesArray,
1254
+ lookups: params.getLookups()
1255
+ });
1256
+ const elapsed = perfElapsed(t0);
1257
+ params.storeOutputs(outputsArray);
1258
+ params.storeElapsedTime(elapsed);
1259
+ }
1260
+ // from RunnableModel interface
1261
+ terminate() {
1262
+ }
1263
+ };
1264
+
1265
+ // src/js-model/js-model.ts
1266
+ function initJsModel(model) {
1267
+ let fns = model.getModelFunctions();
1268
+ if (fns === void 0) {
1269
+ fns = getJsModelFunctions();
1270
+ model.setModelFunctions(fns);
1271
+ }
1272
+ const initialTime = model.getInitialTime();
1273
+ const finalTime = model.getFinalTime();
1274
+ const timeStep = model.getTimeStep();
1275
+ const saveFreq = model.getSaveFreq();
1276
+ const numSavePoints = Math.round((finalTime - initialTime) / saveFreq) + 1;
1277
+ return new BaseRunnableModel({
1278
+ startTime: initialTime,
1279
+ endTime: finalTime,
1280
+ saveFreq,
1281
+ numSavePoints,
1282
+ outputVarIds: model.outputVarIds,
1283
+ modelListing: model.modelListing,
1284
+ onRunModel: (inputs, outputs, options) => {
1285
+ runJsModel(
1286
+ model,
1287
+ initialTime,
1288
+ finalTime,
1289
+ timeStep,
1290
+ saveFreq,
1291
+ numSavePoints,
1292
+ inputs,
1293
+ outputs,
1294
+ options == null ? void 0 : options.outputIndices,
1295
+ options == null ? void 0 : options.lookups,
1296
+ void 0
1297
+ );
1298
+ }
1299
+ });
1300
+ }
1301
+ function runJsModel(model, initialTime, finalTime, timeStep, saveFreq, numSavePoints, inputs, outputs, outputIndices, lookups, stopAfterTime) {
1302
+ let time = initialTime;
1303
+ model.setTime(time);
1304
+ const fnContext = {
1305
+ timeStep,
1306
+ currentTime: time
1307
+ };
1308
+ model.getModelFunctions().setContext(fnContext);
1309
+ model.initConstants();
1310
+ if (lookups !== void 0) {
1311
+ for (const lookupDef of lookups) {
1312
+ model.setLookup(lookupDef.varRef.varSpec, lookupDef.points);
1313
+ }
1314
+ }
1315
+ if ((inputs == null ? void 0 : inputs.length) > 0) {
1316
+ model.setInputs((index) => inputs[index]);
1317
+ }
1318
+ model.initLevels();
1319
+ const lastStep = Math.round((finalTime - initialTime) / timeStep);
1320
+ const stopTime = stopAfterTime !== void 0 ? stopAfterTime : finalTime;
1321
+ let step = 0;
1322
+ let savePointIndex = 0;
1323
+ let outputVarIndex = 0;
1324
+ while (step <= lastStep) {
1325
+ model.evalAux();
1326
+ if (time % saveFreq < 1e-6) {
1327
+ outputVarIndex = 0;
1328
+ const storeValue = (value) => {
1329
+ const outputBufferIndex = outputVarIndex * numSavePoints + savePointIndex;
1330
+ outputs[outputBufferIndex] = time <= stopTime ? value : void 0;
1331
+ outputVarIndex++;
1332
+ };
1333
+ if (outputIndices !== void 0) {
1334
+ let indexBufferOffset = 0;
1335
+ const outputCount = outputIndices[indexBufferOffset++];
1336
+ for (let i = 0; i < outputCount; i++) {
1337
+ const varIndex = outputIndices[indexBufferOffset++];
1338
+ const subCount = outputIndices[indexBufferOffset++];
1339
+ let subscriptIndices;
1340
+ if (subCount > 0) {
1341
+ subscriptIndices = outputIndices.subarray(indexBufferOffset, indexBufferOffset + subCount);
1342
+ indexBufferOffset += subCount;
1343
+ }
1344
+ const varSpec = {
1345
+ varIndex,
1346
+ subscriptIndices
1347
+ };
1348
+ model.storeOutput(varSpec, storeValue);
1349
+ }
1350
+ } else {
1351
+ model.storeOutputs(storeValue);
1352
+ }
1353
+ savePointIndex++;
1354
+ }
1355
+ if (step === lastStep) {
1356
+ break;
1357
+ }
1358
+ model.evalLevels();
1359
+ time += timeStep;
1360
+ model.setTime(time);
1361
+ fnContext.currentTime = time;
1362
+ step++;
1363
+ }
1364
+ }
1365
+
1366
+ // src/js-model/exec-js-model.ts
1367
+ function execJsModel(jsModel) {
1368
+ const runnableModel = initJsModel(jsModel);
1369
+ const inputs = [];
1370
+ const outputVarIds = jsModel.outputVarIds;
1371
+ const startTime = jsModel.getInitialTime();
1372
+ const endTime = jsModel.getFinalTime();
1373
+ const saveFreq = jsModel.getSaveFreq();
1374
+ const outputs = new Outputs(outputVarIds, startTime, endTime, saveFreq);
1375
+ const params = new ReferencedRunModelParams();
1376
+ params.updateFromParams(inputs, outputs);
1377
+ runnableModel.runModel(params);
1378
+ const outputVarNames = jsModel.outputVarNames.map((name) => name.replace(/"/g, '\\"'));
1379
+ const header = outputVarNames.join(" ");
1380
+ console.log(header);
1381
+ for (let i = 0; i < outputs.seriesLength; i++) {
1382
+ const rowValues = [];
1383
+ for (const series of outputs.varSeries) {
1384
+ rowValues.push(series.points[i].y);
1385
+ }
1386
+ console.log(rowValues.join(" "));
1387
+ }
1388
+ }
1389
+
1390
+ // src/js-model/_mocks/mock-js-model.ts
1391
+ var MockJsModel = class {
1392
+ constructor(options) {
1393
+ // from JsModel interface
1394
+ this.kind = "js";
1395
+ this.vars = /* @__PURE__ */ new Map();
1396
+ this.lookups = /* @__PURE__ */ new Map();
1397
+ this.outputVarIds = options.outputVarIds;
1398
+ this.outputVarNames = options.outputVarIds;
1399
+ this.initialTime = options.initialTime;
1400
+ this.finalTime = options.finalTime;
1401
+ this.outputVarIds = options.outputVarIds;
1402
+ if (options.listingJson) {
1403
+ this.modelListing = JSON.parse(options.listingJson);
1404
+ this.internalListing = new ModelListing(this.modelListing);
1405
+ }
1406
+ this.onEvalAux = options.onEvalAux;
1407
+ }
1408
+ varIdForSpec(varSpec) {
1409
+ for (const [listingVarId, listingSpec] of this.internalListing.varSpecs) {
1410
+ if (listingSpec.varIndex === varSpec.varIndex) {
1411
+ return listingVarId;
1412
+ }
1413
+ }
1414
+ return void 0;
1415
+ }
1416
+ // from JsModel interface
1417
+ getInitialTime() {
1418
+ return this.initialTime;
1419
+ }
1420
+ // from JsModel interface
1421
+ getFinalTime() {
1422
+ return this.finalTime;
1423
+ }
1424
+ // from JsModel interface
1425
+ getTimeStep() {
1426
+ return 1;
1427
+ }
1428
+ // from JsModel interface
1429
+ getSaveFreq() {
1430
+ return 1;
1431
+ }
1432
+ // from JsModel interface
1433
+ getModelFunctions() {
1434
+ return this.fns;
1435
+ }
1436
+ // from JsModel interface
1437
+ setModelFunctions(fns) {
1438
+ this.fns = fns;
1439
+ }
1440
+ // from JsModel interface
1441
+ setTime(time) {
1442
+ this.vars.set("_time", time);
1443
+ }
1444
+ // from JsModel interface
1445
+ setInputs() {
1446
+ }
1447
+ // from JsModel interface
1448
+ setLookup(varSpec, points) {
1449
+ const varId = this.varIdForSpec(varSpec);
1450
+ if (varId === void 0) {
1451
+ throw new Error(`No lookup variable found for spec ${varSpec}`);
1452
+ }
1453
+ this.lookups.set(varId, new JsModelLookup(points.length / 2, points));
1454
+ }
1455
+ // from JsModel interface
1456
+ storeOutputs(storeValue) {
1457
+ for (const varId of this.outputVarIds) {
1458
+ storeValue(this.vars.get(varId));
1459
+ }
1460
+ }
1461
+ // from JsModel interface
1462
+ storeOutput(varSpec, storeValue) {
1463
+ const varId = this.varIdForSpec(varSpec);
1464
+ if (varId === void 0) {
1465
+ throw new Error(`No output variable found for spec ${varSpec}`);
1466
+ }
1467
+ storeValue(this.vars.get(varId));
1468
+ }
1469
+ // from JsModel interface
1470
+ initConstants() {
1471
+ }
1472
+ // from JsModel interface
1473
+ initLevels() {
1474
+ }
1475
+ // from JsModel interface
1476
+ evalAux() {
1477
+ var _a;
1478
+ (_a = this.onEvalAux) == null ? void 0 : _a.call(this, this.vars, this.lookups);
1479
+ }
1480
+ // from JsModel interface
1481
+ evalLevels() {
1482
+ }
1483
+ };
1484
+
1485
+ // src/wasm-model/wasm-buffer.ts
1486
+ var WasmBuffer = class {
1487
+ /**
1488
+ * @param wasmModule The `WasmModule` used to initialize the memory.
1489
+ * @param numElements The number of elements in the buffer.
1490
+ * @param byteOffset The byte offset within the wasm heap.
1491
+ * @param heapArray The array view on the underlying heap buffer.
1492
+ */
1493
+ constructor(wasmModule, numElements, byteOffset, heapArray) {
1494
+ this.wasmModule = wasmModule;
1495
+ this.numElements = numElements;
1496
+ this.byteOffset = byteOffset;
1497
+ this.heapArray = heapArray;
1498
+ }
1499
+ /**
1500
+ * @return An `ArrType` view on the underlying heap buffer.
1501
+ */
1502
+ getArrayView() {
1503
+ return this.heapArray;
1504
+ }
1505
+ /**
1506
+ * @return The raw address of the underlying heap buffer.
1507
+ * @hidden This is intended for use by `WasmModel` only.
1508
+ */
1509
+ getAddress() {
1510
+ return this.byteOffset;
1511
+ }
1512
+ /**
1513
+ * Dispose the buffer by freeing the allocated heap memory.
1514
+ */
1515
+ dispose() {
1516
+ var _a, _b;
1517
+ if (this.heapArray) {
1518
+ (_b = (_a = this.wasmModule)._free) == null ? void 0 : _b.call(_a, this.byteOffset);
1519
+ this.numElements = void 0;
1520
+ this.heapArray = void 0;
1521
+ this.byteOffset = void 0;
1522
+ }
1523
+ }
1524
+ };
1525
+ function createInt32WasmBuffer(wasmModule, numElements) {
1526
+ const elemSizeInBytes = 4;
1527
+ const lengthInBytes = numElements * elemSizeInBytes;
1528
+ const byteOffset = wasmModule._malloc(lengthInBytes);
1529
+ const elemOffset = byteOffset / elemSizeInBytes;
1530
+ const heapArray = wasmModule.HEAP32.subarray(elemOffset, elemOffset + numElements);
1531
+ return new WasmBuffer(wasmModule, numElements, byteOffset, heapArray);
1532
+ }
1533
+ function createFloat64WasmBuffer(wasmModule, numElements) {
1534
+ const elemSizeInBytes = 8;
1535
+ const lengthInBytes = numElements * elemSizeInBytes;
1536
+ const byteOffset = wasmModule._malloc(lengthInBytes);
1537
+ const elemOffset = byteOffset / elemSizeInBytes;
1538
+ const heapArray = wasmModule.HEAPF64.subarray(elemOffset, elemOffset + numElements);
1539
+ return new WasmBuffer(wasmModule, numElements, byteOffset, heapArray);
1540
+ }
1541
+
1542
+ // src/wasm-model/wasm-model.ts
1543
+ var WasmModel = class {
1544
+ /**
1545
+ * @param wasmModule The `WasmModule` that provides access to the native functions.
1546
+ * @param outputVarIds The output variable IDs for this model.
1547
+ */
1548
+ constructor(wasmModule) {
1549
+ this.wasmModule = wasmModule;
1550
+ function getNumberValue(funcName) {
1551
+ const wasmGetValue = wasmModule.cwrap(funcName, "number", []);
1552
+ return wasmGetValue();
1553
+ }
1554
+ this.startTime = getNumberValue("getInitialTime");
1555
+ this.endTime = getNumberValue("getFinalTime");
1556
+ this.saveFreq = getNumberValue("getSaveper");
1557
+ this.numSavePoints = Math.round((this.endTime - this.startTime) / this.saveFreq) + 1;
1558
+ this.outputVarIds = wasmModule.outputVarIds;
1559
+ this.modelListing = wasmModule.modelListing;
1560
+ this.wasmSetLookup = wasmModule.cwrap("setLookup", null, ["number", "number", "number", "number"]);
1561
+ this.wasmRunModel = wasmModule.cwrap("runModelWithBuffers", null, ["number", "number", "number"]);
1562
+ }
1563
+ // from RunnableModel interface
1564
+ runModel(params) {
1565
+ var _a, _b, _c, _d, _e, _f, _g;
1566
+ const lookups = params.getLookups();
1567
+ if (lookups !== void 0) {
1568
+ for (const lookupDef of lookups) {
1569
+ const varSpec = lookupDef.varRef.varSpec;
1570
+ const numSubElements = ((_a = varSpec.subscriptIndices) == null ? void 0 : _a.length) || 0;
1571
+ let subIndicesAddress;
1572
+ if (numSubElements > 0) {
1573
+ if (this.lookupSubIndicesBuffer === void 0 || this.lookupSubIndicesBuffer.numElements < numSubElements) {
1574
+ (_b = this.lookupSubIndicesBuffer) == null ? void 0 : _b.dispose();
1575
+ this.lookupSubIndicesBuffer = createInt32WasmBuffer(this.wasmModule, numSubElements);
1576
+ }
1577
+ this.lookupSubIndicesBuffer.getArrayView().set(varSpec.subscriptIndices);
1578
+ subIndicesAddress = this.lookupSubIndicesBuffer.getAddress();
1579
+ } else {
1580
+ subIndicesAddress = 0;
1581
+ }
1582
+ const numLookupElements = lookupDef.points.length;
1583
+ if (this.lookupDataBuffer === void 0 || this.lookupDataBuffer.numElements < numLookupElements) {
1584
+ (_c = this.lookupDataBuffer) == null ? void 0 : _c.dispose();
1585
+ this.lookupDataBuffer = createFloat64WasmBuffer(this.wasmModule, numLookupElements);
1586
+ }
1587
+ this.lookupDataBuffer.getArrayView().set(lookupDef.points);
1588
+ const pointsAddress = this.lookupDataBuffer.getAddress();
1589
+ const numPoints = numLookupElements / 2;
1590
+ const varIndex = varSpec.varIndex;
1591
+ this.wasmSetLookup(varIndex, subIndicesAddress, pointsAddress, numPoints);
1592
+ }
1593
+ }
1594
+ params.copyInputs((_d = this.inputsBuffer) == null ? void 0 : _d.getArrayView(), (numElements) => {
1595
+ var _a2;
1596
+ (_a2 = this.inputsBuffer) == null ? void 0 : _a2.dispose();
1597
+ this.inputsBuffer = createFloat64WasmBuffer(this.wasmModule, numElements);
1598
+ return this.inputsBuffer.getArrayView();
1599
+ });
1600
+ let outputIndicesBuffer;
1601
+ if (params.getOutputIndicesLength() > 0) {
1602
+ params.copyOutputIndices((_e = this.outputIndicesBuffer) == null ? void 0 : _e.getArrayView(), (numElements) => {
1603
+ var _a2;
1604
+ (_a2 = this.outputIndicesBuffer) == null ? void 0 : _a2.dispose();
1605
+ this.outputIndicesBuffer = createInt32WasmBuffer(this.wasmModule, numElements);
1606
+ return this.outputIndicesBuffer.getArrayView();
1607
+ });
1608
+ outputIndicesBuffer = this.outputIndicesBuffer;
1609
+ } else {
1610
+ outputIndicesBuffer = void 0;
1611
+ }
1612
+ const outputsLengthInElements = params.getOutputsLength();
1613
+ if (this.outputsBuffer === void 0 || this.outputsBuffer.numElements < outputsLengthInElements) {
1614
+ (_f = this.outputsBuffer) == null ? void 0 : _f.dispose();
1615
+ this.outputsBuffer = createFloat64WasmBuffer(this.wasmModule, outputsLengthInElements);
1616
+ }
1617
+ const t0 = perfNow();
1618
+ this.wasmRunModel(
1619
+ ((_g = this.inputsBuffer) == null ? void 0 : _g.getAddress()) || 0,
1620
+ this.outputsBuffer.getAddress(),
1621
+ (outputIndicesBuffer == null ? void 0 : outputIndicesBuffer.getAddress()) || 0
1622
+ );
1623
+ const elapsed = perfElapsed(t0);
1624
+ params.storeOutputs(this.outputsBuffer.getArrayView());
1625
+ params.storeElapsedTime(elapsed);
1626
+ }
1627
+ // from RunnableModel interface
1628
+ terminate() {
1629
+ var _a, _b, _c;
1630
+ (_a = this.inputsBuffer) == null ? void 0 : _a.dispose();
1631
+ this.inputsBuffer = void 0;
1632
+ (_b = this.outputsBuffer) == null ? void 0 : _b.dispose();
1633
+ this.outputsBuffer = void 0;
1634
+ (_c = this.outputIndicesBuffer) == null ? void 0 : _c.dispose();
1635
+ this.outputIndicesBuffer = void 0;
1636
+ }
1637
+ };
1638
+ function initWasmModel(wasmModule) {
1639
+ return new WasmModel(wasmModule);
1640
+ }
1641
+
1642
+ // src/wasm-model/_mocks/mock-wasm-module.ts
1643
+ var MockWasmModule = class {
1644
+ constructor(options) {
1645
+ // from WasmModule interface
1646
+ this.kind = "wasm";
1647
+ // Start at 8 so that we can treat 0 as NULL
1648
+ this.mallocOffset = 8;
1649
+ this.allocs = /* @__PURE__ */ new Map();
1650
+ this.lookups = /* @__PURE__ */ new Map();
1651
+ this.initialTime = options.initialTime;
1652
+ this.finalTime = options.finalTime;
1653
+ this.outputVarIds = options.outputVarIds;
1654
+ if (options.listingJson) {
1655
+ this.modelListing = JSON.parse(options.listingJson);
1656
+ this.internalListing = new ModelListing(this.modelListing);
1657
+ }
1658
+ this.onRunModel = options.onRunModel;
1659
+ this.heap = new ArrayBuffer(8192);
1660
+ this.HEAP32 = new Int32Array(this.heap);
1661
+ this.HEAPF64 = new Float64Array(this.heap);
1662
+ }
1663
+ varIdForSpec(varSpec) {
1664
+ for (const [listingVarId, listingSpec] of this.internalListing.varSpecs) {
1665
+ if (listingSpec.varIndex === varSpec.varIndex) {
1666
+ return listingVarId;
1667
+ }
1668
+ }
1669
+ return void 0;
1670
+ }
1671
+ // from WasmModule interface
1672
+ cwrap(fname) {
1673
+ switch (fname) {
1674
+ case "getInitialTime":
1675
+ return () => this.initialTime;
1676
+ case "getFinalTime":
1677
+ return () => this.finalTime;
1678
+ case "getSaveper":
1679
+ return () => 1;
1680
+ case "setLookup":
1681
+ return (varIndex, _subIndicesAddress, pointsAddress, numPoints) => {
1682
+ const varId = this.varIdForSpec({ varIndex });
1683
+ if (varId === void 0) {
1684
+ throw new Error(`No lookup variable found for var index ${varIndex}`);
1685
+ }
1686
+ const points = new Float64Array(this.getHeapView("float64", pointsAddress));
1687
+ this.lookups.set(varId, new JsModelLookup(numPoints, points));
1688
+ };
1689
+ case "runModelWithBuffers":
1690
+ return (inputsAddress, outputsAddress, outputIndicesAddress) => {
1691
+ const inputs = this.getHeapView("float64", inputsAddress);
1692
+ const outputs = this.getHeapView("float64", outputsAddress);
1693
+ const outputIndices = this.getHeapView("int32", outputIndicesAddress);
1694
+ this.onRunModel(inputs, outputs, this.lookups, outputIndices);
1695
+ };
1696
+ default:
1697
+ throw new Error(`Unhandled call to cwrap with function name '${fname}'`);
1698
+ }
1699
+ }
1700
+ // from WasmModule interface
1701
+ _malloc(lengthInBytes) {
1702
+ const currentOffset = this.mallocOffset;
1703
+ this.allocs.set(currentOffset, lengthInBytes);
1704
+ if (lengthInBytes > 0) {
1705
+ this.mallocOffset += lengthInBytes;
1706
+ } else {
1707
+ this.mallocOffset += 8;
1708
+ }
1709
+ return currentOffset;
1710
+ }
1711
+ // from WasmModule interface
1712
+ _free() {
1713
+ }
1714
+ getHeapView(kind, address) {
1715
+ if (address === 0) {
1716
+ return void 0;
1717
+ }
1718
+ const lengthInBytes = this.allocs.get(address);
1719
+ if (lengthInBytes === void 0) {
1720
+ throw new Error("Failed to locate heap allocation");
1721
+ }
1722
+ if (kind === "float64") {
1723
+ const offset = address / 8;
1724
+ return this.HEAPF64.subarray(offset, offset + lengthInBytes / 8);
1725
+ } else {
1726
+ const offset = address / 4;
1727
+ return this.HEAP32.subarray(offset, offset + lengthInBytes / 4);
1728
+ }
1729
+ }
1730
+ };
1731
+
1732
+ // src/model-runner/synchronous-model-runner.ts
1733
+ function createRunnableModel(generatedModel) {
1734
+ switch (generatedModel.kind) {
1735
+ case "js":
1736
+ return initJsModel(generatedModel);
1737
+ case "wasm":
1738
+ return initWasmModel(generatedModel);
1739
+ default:
1740
+ throw new Error(`Unable to identify generated model kind`);
1741
+ }
1742
+ }
1743
+ function createSynchronousModelRunner(generatedModel) {
1744
+ const runnableModel = createRunnableModel(generatedModel);
1745
+ return createRunnerFromRunnableModel(runnableModel);
1746
+ }
1747
+ function createRunnerFromRunnableModel(model) {
1748
+ const listing = model.modelListing ? new ModelListing(model.modelListing) : void 0;
1749
+ const params = new ReferencedRunModelParams(listing);
1750
+ let terminated = false;
1751
+ const runModelSync = (inputs, outputs, options) => {
1752
+ params.updateFromParams(inputs, outputs, options);
1753
+ model.runModel(params);
1754
+ return outputs;
1755
+ };
1756
+ return {
1757
+ createOutputs: () => {
1758
+ return new Outputs(model.outputVarIds, model.startTime, model.endTime, model.saveFreq);
1759
+ },
1760
+ runModel: (inputs, outputs, options) => {
1761
+ if (terminated) {
1762
+ return Promise.reject(new Error("Model runner has already been terminated"));
1763
+ }
1764
+ return Promise.resolve(runModelSync(inputs, outputs, options));
1765
+ },
1766
+ runModelSync: (inputs, outputs, options) => {
1767
+ if (terminated) {
1768
+ throw new Error("Model runner has already been terminated");
1769
+ }
1770
+ return runModelSync(inputs, outputs, options);
1771
+ },
1772
+ terminate: () => __async(this, null, function* () {
1773
+ if (!terminated) {
1774
+ model.terminate();
1775
+ terminated = true;
1776
+ }
1777
+ })
1778
+ };
1779
+ }
483
1780
 
484
1781
  // src/model-scheduler/model-scheduler.ts
485
1782
  var ModelScheduler = class {
@@ -563,19 +1860,26 @@ function createSimpleInputValue(varId) {
563
1860
  return { varId, get, set, reset, callbacks: {} };
564
1861
  }
565
1862
  export {
1863
+ BufferedRunModelParams,
1864
+ MockJsModel,
1865
+ MockWasmModule,
566
1866
  ModelListing,
567
1867
  ModelScheduler,
568
1868
  Outputs,
1869
+ ReferencedRunModelParams,
569
1870
  Series,
570
- WasmBuffer,
571
- WasmModel,
572
- createFloat64WasmBuffer,
573
1871
  createInputValue,
574
- createInt32WasmBuffer,
575
- createWasmModelRunner,
576
- initWasmModelAndBuffers,
577
- perfElapsed,
578
- perfNow,
579
- updateOutputIndices
1872
+ createLookupDef,
1873
+ createRunnableModel,
1874
+ createSynchronousModelRunner,
1875
+ decodeLookups,
1876
+ encodeLookups,
1877
+ encodeVarIndices,
1878
+ execJsModel,
1879
+ getEncodedLookupBufferLengths,
1880
+ getEncodedVarIndicesLength,
1881
+ getJsModelFunctions,
1882
+ initJsModel,
1883
+ initWasmModel
580
1884
  };
581
1885
  //# sourceMappingURL=index.js.map