@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.
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/wasm-model/wasm-buffer.ts","../src/wasm-model/wasm-model.ts","../src/model-runner/inputs.ts","../src/model-runner/outputs.ts","../src/model-runner/perf.ts","../src/model-runner/model-runner.ts","../src/model-scheduler/model-scheduler.ts"],"sourcesContent":["// Copyright (c) 2020-2022 Climate Interactive / New Venture Fund\n\nexport type { InputVarId, OutputVarId } from './_shared'\nexport * from './wasm-model'\nexport * from './model-runner'\nexport * from './model-scheduler'\n","// Copyright (c) 2020-2022 Climate Interactive / New Venture Fund\n\nimport type { WasmModule } from './wasm-module'\n\n/**\n * Wraps a `WebAssembly.Memory` buffer allocated on the wasm heap.\n *\n * When this is used synchronously (in the browser's normal JavaScript thread),\n * the client can use `getArrayView` to write directly into the underlying memory.\n *\n * Note, however, that `WebAssembly.Memory` buffers cannot be transferred to/from\n * a Web Worker. When using this class in a worker thread, create a separate\n * `Float64Array` that can be transferred between the worker and the client running\n * in the browser's normal JS thread, and then use `getArrayView` to copy into and\n * out of the wasm buffer.\n */\nexport class WasmBuffer {\n private byteOffset: number\n private heapArray: Float64Array\n\n /**\n * @param wasmModule The `WasmModule` used to initialize the memory.\n * @param numElements The number of 64-bit `double` elements in the buffer.\n */\n constructor(private readonly wasmModule: WasmModule, numElements: number) {\n const sizeOfFloat64 = 8\n const lengthInBytes = numElements * sizeOfFloat64\n this.byteOffset = wasmModule._malloc(lengthInBytes)\n const float64Offset = this.byteOffset / sizeOfFloat64\n this.heapArray = wasmModule.HEAPF64.subarray(float64Offset, float64Offset + numElements)\n }\n\n /**\n * @return A new `Float64Array` view on the underlying heap buffer.\n */\n getArrayView(): Float64Array {\n return this.heapArray\n }\n\n /**\n * @return The raw address of the underlying heap buffer.\n * @hidden This is intended for use by `WasmModel` only.\n */\n getAddress(): number {\n return this.byteOffset\n }\n\n /**\n * Dispose the buffer by freeing the allocated heap memory.\n */\n dispose(): void {\n if (this.heapArray) {\n this.wasmModule._free(this.byteOffset)\n this.heapArray = undefined\n this.byteOffset = undefined\n }\n }\n}\n","// Copyright (c) 2020-2022 Climate Interactive / New Venture Fund\n\nimport type { OutputVarId } from '../_shared'\nimport { WasmBuffer } from './wasm-buffer'\nimport type { WasmModule } from './wasm-module'\n\n/**\n * An interface to the generated WebAssembly model. Allows for running the model with\n * a given set of input values, producing a set of output values.\n */\nexport class WasmModel {\n /** The start time for the model (aka `INITIAL TIME`). */\n public readonly startTime: number\n /** The end time for the model (aka `FINAL TIME`). */\n public readonly endTime: number\n /** The frequency with which output values are saved (aka `SAVEPER`). */\n public readonly saveFreq: number\n /** The number of save points for each output. */\n public readonly numSavePoints: number\n\n private readonly wasmRunModel: (inputsAddress: number, outputsAddress: number) => void\n\n /**\n * @param wasmModule The `WasmModule` that provides access to the native functions.\n */\n constructor(wasmModule: WasmModule) {\n function getNumberValue(funcName: string): number {\n const wasmGetValue: () => number = wasmModule.cwrap(funcName, 'number', [])\n return wasmGetValue()\n }\n this.startTime = getNumberValue('getInitialTime')\n this.endTime = getNumberValue('getFinalTime')\n this.saveFreq = getNumberValue('getSaveper')\n\n // Each series will include one data point per \"save\", inclusive of the\n // start and end times\n this.numSavePoints = Math.round((this.endTime - this.startTime) / this.saveFreq) + 1\n\n this.wasmRunModel = wasmModule.cwrap('runModelWithBuffers', null, ['number', 'number'])\n }\n\n /**\n * Run the model, using inputs from the `inputs` buffer, and writing outputs into\n * the `outputs` buffer.\n *\n * @param inputs The buffer containing inputs in the order expected by the model.\n * @param outputs The buffer into which the model will store output values.\n */\n runModel(inputs: WasmBuffer, outputs: WasmBuffer): void {\n this.wasmRunModel(inputs.getAddress(), outputs.getAddress())\n }\n}\n\n/**\n * The result of model initialization.\n */\nexport interface WasmModelInitResult {\n /** The wasm model. */\n model: WasmModel\n /** The buffer used to pass input values to the model. */\n inputsBuffer: WasmBuffer\n /** The buffer used to receive output values from the model. */\n outputsBuffer: WasmBuffer\n /** The output variable IDs. */\n outputVarIds: OutputVarId[]\n}\n\n/**\n * Initialize the wasm model and buffers.\n *\n * @param wasmModule The `WasmModule` that wraps the `wasm` binary.\n * @param numInputs The number of input variables, per the spec file passed to `sde`.\n * @param outputVarIds The output variable IDs, per the spec file passed to `sde`.\n */\nexport function initWasmModelAndBuffers(\n wasmModule: WasmModule,\n numInputs: number,\n outputVarIds: OutputVarId[]\n): WasmModelInitResult {\n // Wrap the native C `runModelWithBuffers` function in a JS function that we can call\n const model = new WasmModel(wasmModule)\n\n // Allocate a buffer that is large enough to hold the input values\n const inputsBuffer = new WasmBuffer(wasmModule, numInputs)\n\n // Allocate a buffer that is large enough to hold the series data for\n // each output variable\n const outputsBuffer = new WasmBuffer(wasmModule, outputVarIds.length * model.numSavePoints)\n\n return {\n model,\n inputsBuffer,\n outputsBuffer,\n outputVarIds\n }\n}\n","// Copyright (c) 2020-2022 Climate Interactive / New Venture Fund\n\nimport type { InputVarId } from '../_shared'\n\n/** Callback functions that are called when the input value is changed. */\nexport interface InputCallbacks {\n /** Called after a new value is set. */\n onSet?: () => void\n}\n\n/**\n * Represents a writable model input.\n */\nexport interface InputValue {\n /** The ID of the associated input variable, as used in SDEverywhere. */\n varId: InputVarId\n /** Get the current value of the input. */\n get: () => number\n /** Set the input to the given value. */\n set: (value: number) => void\n /** Reset the input to its default value. */\n reset: () => void\n /** Callback functions that are called when the input value is changed. */\n callbacks: InputCallbacks\n}\n\n/**\n * Create a basic `InputValue` instance that notifies when a new value is set.\n *\n * @param varId The input variable ID, as used in SDEverywhere.\n * @param defaultValue The default value of the input.\n * @param initialValue The inital value of the input; if undefined, will use `defaultValue`.\n */\nexport function createInputValue(varId: InputVarId, defaultValue: number, initialValue?: number): InputValue {\n let currentValue = initialValue !== undefined ? initialValue : defaultValue\n\n // The `onSet` callback is initially undefined but will be installed by `ModelScheduler`\n const callbacks: InputCallbacks = {}\n\n const get = () => {\n return currentValue\n }\n\n const set = (newValue: number) => {\n if (newValue !== currentValue) {\n currentValue = newValue\n callbacks.onSet?.()\n }\n }\n\n const reset = () => {\n set(defaultValue)\n }\n\n return { varId, get, set, reset, callbacks }\n}\n","// Copyright (c) 2020-2022 Climate Interactive / New Venture Fund\n\nimport type { Result } from 'neverthrow'\nimport { ok, err } from 'neverthrow'\nimport type { OutputVarId } from '../_shared'\n\n/** Indicates the type of error encountered when parsing an outputs buffer. */\nexport type ParseError = 'invalid-point-count'\n\n/** A data point. */\nexport interface Point {\n /** The x value (typically a time value). */\n x: number\n /** The y value. */\n y: number\n}\n\n/**\n * A time series of data points for an output variable.\n */\nexport class Series {\n /**\n * @param varId The ID for the output variable (as used by SDEverywhere).\n * @param points The data points for the variable, one point per time increment.\n */\n constructor(public readonly varId: OutputVarId, public readonly points: Point[]) {}\n\n /**\n * Return the Y value at the given time. Note that this does not attempt to interpolate\n * if there is no data point defined for the given time and will return undefined in\n * that case.\n *\n * @param time The x (time) value.\n * @return The y value for the given time, or undefined if there is no data point defined\n * for the given time.\n */\n getValueAtTime(time: number): number | undefined {\n // TODO: Add option to allow interpolation if the given time value is in between points\n // TODO: Use binary search to make lookups faster\n return this.points.find(p => p.x === time)?.y\n }\n\n /**\n * Create a new `Series` instance that is a copy of this one.\n */\n copy(): Series {\n // Create a deep copy\n const pointsCopy = this.points.map(p => ({ ...p }))\n return new Series(this.varId, pointsCopy)\n }\n}\n\n/** Represents the outputs from a model run. */\nexport class Outputs {\n /** The number of data points in each series. */\n public readonly seriesLength: number\n /** The array of series, one for each output variable. */\n public readonly varSeries: Series[]\n\n /**\n * The latest model run time, in milliseconds.\n * @hidden This is not yet part of the public API; it is exposed here for use\n * in performance testing tools.\n */\n public runTimeInMillis: number\n\n /**\n * @param varIds The output variable identifiers.\n * @param startTime The start time for the model.\n * @param endTime The end time for the model.\n * @param saveFreq The frequency with which output values are saved (aka `SAVEPER`).\n */\n constructor(\n public readonly varIds: OutputVarId[],\n public readonly startTime: number,\n public readonly endTime: number,\n public readonly saveFreq = 1\n ) {\n // Each series will include one data point per \"save\", inclusive of the\n // start and end times\n this.seriesLength = Math.round((endTime - startTime) / saveFreq) + 1\n\n // Create an array of arrays, one for each output variable\n this.varSeries = new Array(varIds.length)\n\n // Populate the arrays, filling in the time for each point\n for (let i = 0; i < varIds.length; i++) {\n const points: Point[] = new Array(this.seriesLength)\n for (let j = 0; j < this.seriesLength; j++) {\n points[j] = { x: startTime + j * saveFreq, y: 0 }\n }\n const varId = varIds[i]\n this.varSeries[i] = new Series(varId, points)\n }\n }\n\n /**\n * Parse the given raw float buffer (produced by the model) and store the values\n * into this `Outputs` instance.\n *\n * Note that the length of `outputsBuffer` must be greater than or equal to\n * the capacity of this `Outputs` instance. The `Outputs` instance is allowed\n * to be smaller to support the case where you want to extract a subset of\n * the time range in the buffer produced by the model.\n *\n * @param outputsBuffer The raw outputs buffer produced by the model.\n * @param rowLength The number of elements per row (one element per save point).\n * @return An `ok` result if the buffer is valid, otherwise an `err` result.\n */\n updateFromBuffer(outputsBuffer: Float64Array, rowLength: number): Result<void, ParseError> {\n const result = parseOutputsBuffer(outputsBuffer, rowLength, this)\n if (result.isOk()) {\n return ok(undefined)\n } else {\n return err(result.error)\n }\n }\n\n /**\n * Return the series for the given output variable.\n *\n * @param varId The ID of the output variable (as used by SDEverywhere).\n */\n getSeriesForVar(varId: OutputVarId): Series | undefined {\n const seriesIndex = this.varIds.indexOf(varId)\n if (seriesIndex >= 0) {\n return this.varSeries[seriesIndex]\n } else {\n // TODO: Error\n return undefined\n }\n }\n}\n\n/**\n * Parse the raw buffer produced by the model and store the values in the\n * given (reused) `Outputs` object.\n *\n * @param outputsBuffer The raw outputs buffer produced by the model.\n * @param rowLength The number of elements per row (one element per year or save point).\n * @return An `ok` result if the buffer is valid, otherwise an `err` result.\n * @hidden\n */\nfunction parseOutputsBuffer(\n outputsBuffer: Float64Array,\n rowLength: number,\n outputs: Outputs\n): Result<Outputs, ParseError> {\n const varCount = outputs.varIds.length\n const seriesLength = outputs.seriesLength\n if (rowLength < seriesLength || outputsBuffer.length < varCount * seriesLength) {\n return err('invalid-point-count')\n }\n\n // The buffer populated by the C `runModelWithBuffers` function is already\n // transposed, so the first \"row\" contains the values for the first output\n // variable (from start time to end time), and so on.\n for (let outputVarIndex = 0; outputVarIndex < varCount; outputVarIndex++) {\n const series = outputs.varSeries[outputVarIndex]\n let sourceIndex = rowLength * outputVarIndex\n for (let valueIndex = 0; valueIndex < seriesLength; valueIndex++) {\n series.points[valueIndex].y = validateNumber(outputsBuffer[sourceIndex])\n sourceIndex++\n }\n }\n\n return ok(outputs)\n}\n\n/**\n * Return the given number if it is valid, or undefined if it is invalid.\n *\n * SDE converts Vensim's `:NA:` values to `-DBL_MAX`, so if we see a very large negative\n * value, convert it to `undefined`. This is preferable to including extreme values\n * because some charting libraries (e.g. Chart.js) appear to choke on these large values\n * in certain browsers (e.g. Safari), but `undefined` appears to be handled better and\n * does a better job of signaling that the data point is undefined.\n *\n * @hidden\n */\nfunction validateNumber(x: number): number | undefined {\n if (!isNaN(x) && x > -1e32) {\n return x\n } else {\n return undefined\n }\n}\n","// Copyright (c) 2021-2022 Climate Interactive / New Venture Fund\n\nlet isWeb: boolean\n\n/**\n * Return a timestamp that can be passed to `perfElapsed` for calculating the elapsed\n * time of an operation.\n *\n * @hidden This is not part of the public API; exposed only for use in performance testing.\n */\nexport function perfNow(): unknown {\n // Note that `self` resolves to the window (in browser context) or the worker global scope\n // (in a Web Worker context)\n if (isWeb === undefined) {\n isWeb = typeof self !== 'undefined' && self?.performance !== undefined\n }\n if (isWeb) {\n return self.performance.now()\n } else {\n // XXX: We only use `process` in two places; we bypass type checking instead of\n // setting up type declarations\n // eslint-disable-next-line @typescript-eslint/ban-ts-comment\n // @ts-ignore\n return process?.hrtime()\n }\n}\n\n/**\n * Return the elapsed time between the given timestamp (created by `perfNow`) and now.\n *\n * @hidden This is not part of the public API; exposed only for use in performance testing.\n */\nexport function perfElapsed(t0: unknown): number {\n if (isWeb) {\n const t1 = self.performance.now()\n return (t1 as number) - (t0 as number)\n } else {\n // eslint-disable-next-line @typescript-eslint/ban-ts-comment\n // @ts-ignore\n const elapsed = process.hrtime(t0) as number[]\n // Convert from nanoseconds to milliseconds\n return (elapsed[0] * 1000000000 + elapsed[1]) / 1000000\n }\n}\n","// Copyright (c) 2020-2022 Climate Interactive / New Venture Fund\n\nimport type { WasmModelInitResult } from '../wasm-model'\nimport type { InputValue } from './inputs'\nimport { Outputs } from './outputs'\nimport { perfElapsed, perfNow } from './perf'\n\n/**\n * Abstraction that allows for running the wasm model on the JS thread\n * or asynchronously (e.g. in a Web Worker), depending on the implementation.\n */\nexport interface ModelRunner {\n /**\n * Create an `Outputs` instance that is sized to accommodate the output variable\n * data stored by the model.\n *\n * @return A new `Outputs` instance.\n */\n createOutputs(): Outputs\n\n /**\n * Run the model.\n *\n * @param inputs The model input values (must be in the same order as in the spec file).\n * @param outputs The structure into which the model outputs will be stored.\n * @return A promise that resolves with the outputs when the model run is complete.\n */\n runModel(inputs: InputValue[], outputs: Outputs): Promise<Outputs>\n\n /**\n * Run the model synchronously.\n *\n * @param inputs The model input values (must be in the same order as in the spec file).\n * @param outputs The structure into which the model outputs will be stored.\n * @return The outputs of the run.\n *\n * @hidden This is only intended for internal use; some implementations may not support\n * running the model synchronously, in which case this will be undefined.\n */\n runModelSync?(inputs: InputValue[], outputs: Outputs): Outputs\n\n /**\n * Terminate the runner by releasing underlying resources (e.g., the worker thread or\n * Wasm module/buffers).\n */\n terminate(): Promise<void>\n}\n\n/**\n * Create a `ModelRunner` that runs the given wasm model on the JS thread.\n *\n * @param wasmResult The result of initializing the wasm model.\n */\nexport function createWasmModelRunner(wasmResult: WasmModelInitResult): ModelRunner {\n // Create views on the wasm buffers\n const wasmModel = wasmResult.model\n const inputsBuffer = wasmResult.inputsBuffer\n const inputsArray = inputsBuffer.getArrayView()\n const outputsBuffer = wasmResult.outputsBuffer\n const outputsArray = outputsBuffer.getArrayView()\n const rowLength = wasmModel.numSavePoints\n\n // Disallow `runModel` after the runner has been terminated\n let terminated = false\n\n const runModelSync = (inputs: InputValue[], outputs: Outputs) => {\n // Capture the current set of input values into the reusable buffer\n let i = 0\n for (const input of inputs) {\n inputsArray[i++] = input.get()\n }\n\n // Run the model\n const t0 = perfNow()\n wasmModel.runModel(inputsBuffer, outputsBuffer)\n outputs.runTimeInMillis = perfElapsed(t0)\n\n // Capture the outputs array by copying the data into the given `Outputs`\n // data structure\n outputs.updateFromBuffer(outputsArray, rowLength)\n\n return outputs\n }\n\n return {\n createOutputs: () => {\n return new Outputs(wasmResult.outputVarIds, wasmModel.startTime, wasmModel.endTime, wasmModel.saveFreq)\n },\n\n runModel: (inputs, outputs) => {\n if (terminated) {\n return Promise.reject(new Error('Model runner has already been terminated'))\n }\n return Promise.resolve(runModelSync(inputs, outputs))\n },\n\n runModelSync: (inputs, outputs) => {\n if (terminated) {\n throw new Error('Model runner has already been terminated')\n }\n return runModelSync(inputs, outputs)\n },\n\n terminate: () => {\n if (!terminated) {\n // TODO: Release wasm-related resources (module or buffers)\n terminated = true\n }\n return Promise.resolve()\n }\n }\n}\n","// Copyright (c) 2020-2022 Climate Interactive / New Venture Fund\n\nimport type { InputVarId } from '../_shared'\nimport type { InputValue, ModelRunner, Outputs } from '../model-runner'\n\n/**\n * A high-level interface that schedules running of the underlying `WasmModel`.\n *\n * When one or more input values are changed, this class will schedule a model\n * run to be completed as soon as possible. When the model run has completed,\n * `onOutputsChanged` is called to notify that new output data is available.\n *\n * The `ModelRunner` is pluggable to allow for running the model synchronously\n * (on the main JavaScript thread) or asynchronously (in a Web Worker or Node.js\n * worker thread).\n */\nexport class ModelScheduler {\n /** The second array that holds a stable copy of the user inputs. */\n private readonly currentInputs: InputValue[]\n\n /** Whether a model run has been scheduled. */\n private runNeeded = false\n\n /** Whether a model run is in progress. */\n private runInProgress = false\n\n /** Called when `outputs` has been updated after a model run. */\n public onOutputsChanged?: (outputs: Outputs) => void\n\n /**\n * @param runner The model runner.\n * @param userInputs The input values, in the same order as in the spec file passed to `sde`.\n * @param outputs The structure into which the model outputs will be stored.\n */\n constructor(\n private readonly runner: ModelRunner,\n private readonly userInputs: InputValue[],\n private outputs: Outputs\n ) {\n // When any input has an updated value, schedule a model run on the next tick\n const afterSet = () => {\n this.runWasmModelIfNeeded()\n }\n for (const userInput of userInputs) {\n userInput.callbacks.onSet = afterSet\n }\n\n // Create a second array to hold a stable copy of the user inputs during model runs\n this.currentInputs = []\n for (const userInput of userInputs) {\n this.currentInputs.push(createSimpleInputValue(userInput.varId))\n }\n }\n\n /**\n * Schedule a wasm model run (if not already pending). When the run is\n * complete, save the outputs and call the `onOutputsChanged` callback.\n */\n private runWasmModelIfNeeded(): void {\n // Set a flag indicating that a new run is needed (even if one is already\n // in progress)\n this.runNeeded = true\n\n if (this.runInProgress) {\n // A run is already in progress; let it finish first\n return\n } else {\n // A run is not already in progress, so schedule it now. We use\n // `setTimeout` so that if a lot of inputs are all changing at once\n // (like after a reset), we wait for all those `set` or `reset`\n // calls to finish before gathering the input values into an array\n // and initiating the run on the next tick.\n this.runInProgress = true\n setTimeout(() => {\n // Kick off the (possibly asynchronous) model run\n this.runWasmModelNow()\n }, 0)\n }\n }\n\n /**\n * Run the wasm model asynchronously using the current set of input values.\n */\n private async runWasmModelNow(): Promise<void> {\n // Copy the current inputs into a separate array; this ensures that the\n // model run uses a stable set of inputs, even if the user continues to\n // change the inputs while the model is being run asynchronously\n for (let i = 0; i < this.userInputs.length; i++) {\n this.currentInputs[i].set(this.userInputs[i].get())\n }\n\n // Run the model with the current set of input values and save the outputs\n try {\n this.outputs = await this.runner.runModel(this.currentInputs, this.outputs)\n this.onOutputsChanged?.(this.outputs)\n } catch (e) {\n console.error(`ERROR: Failed to run model: ${e.message}`)\n }\n\n // See if another run is needed\n if (this.runNeeded) {\n // Keep `runInProgress` set, but clear the `runNeeded` flag\n this.runNeeded = false\n setTimeout(() => {\n this.runWasmModelNow()\n }, 0)\n } else {\n // No run needed, so clear both flags\n this.runNeeded = false\n this.runInProgress = false\n }\n }\n}\n\n/**\n * Create an `InputValue` that is only used to hold a copy of another input (no callbacks).\n * @hidden\n */\nfunction createSimpleInputValue(varId: InputVarId): InputValue {\n let currentValue = 0\n const get = () => {\n return currentValue\n }\n const set = (newValue: number) => {\n currentValue = newValue\n }\n const reset = () => {\n set(0)\n }\n return { varId, get, set, reset, callbacks: {} }\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../src/index.ts","../src/wasm-model/wasm-buffer.ts","../src/wasm-model/wasm-model.ts","../src/model-runner/inputs.ts","../src/model-runner/outputs.ts","../src/model-runner/perf.ts","../src/model-runner/model-runner.ts","../src/model-runner/model-listing.ts","../src/model-scheduler/model-scheduler.ts"],"sourcesContent":["// Copyright (c) 2020-2022 Climate Interactive / New Venture Fund\n\nexport type { InputVarId, OutputVarId, OutputVarSpec } from './_shared'\nexport * from './wasm-model'\nexport * from './model-runner'\nexport * from './model-scheduler'\n","// Copyright (c) 2020-2022 Climate Interactive / New Venture Fund\n\nimport type { WasmModule } from './wasm-module'\n\n/**\n * Wraps a `WebAssembly.Memory` buffer allocated on the wasm heap.\n *\n * When this is used synchronously (in the browser's normal JavaScript thread),\n * the client can use `getArrayView` to write directly into the underlying memory.\n *\n * Note, however, that `WebAssembly.Memory` buffers cannot be transferred to/from\n * a Web Worker. When using this class in a worker thread, create a separate\n * `Float64Array` that can be transferred between the worker and the client running\n * in the browser's normal JS thread, and then use `getArrayView` to copy into and\n * out of the wasm buffer.\n */\nexport class WasmBuffer<ArrType> {\n /**\n * @param wasmModule The `WasmModule` used to initialize the memory.\n * @param byteOffset The byte offset within the wasm heap.\n * @param heapArray The array view on the underlying heap buffer.\n */\n constructor(private readonly wasmModule: WasmModule, private byteOffset: number, private heapArray: ArrType) {}\n\n /**\n * @return An `ArrType` view on the underlying heap buffer.\n */\n getArrayView(): ArrType {\n return this.heapArray\n }\n\n /**\n * @return The raw address of the underlying heap buffer.\n * @hidden This is intended for use by `WasmModel` only.\n */\n getAddress(): number {\n return this.byteOffset\n }\n\n /**\n * Dispose the buffer by freeing the allocated heap memory.\n */\n dispose(): void {\n if (this.heapArray) {\n this.wasmModule._free(this.byteOffset)\n this.heapArray = undefined\n this.byteOffset = undefined\n }\n }\n}\n\n/**\n * Return a `WasmBuffer` that holds int32 elements.\n *\n * @hidden For internal use only.\n *\n * @param wasmModule The `WasmModule` used to initialize the memory.\n * @param numElements The number of elements in the buffer.\n */\nexport function createInt32WasmBuffer(wasmModule: WasmModule, numElements: number): WasmBuffer<Int32Array> {\n const elemSizeInBytes = 4\n const lengthInBytes = numElements * elemSizeInBytes\n const byteOffset = wasmModule._malloc(lengthInBytes)\n const elemOffset = byteOffset / elemSizeInBytes\n const heapArray = wasmModule.HEAP32.subarray(elemOffset, elemOffset + numElements)\n return new WasmBuffer<Int32Array>(wasmModule, byteOffset, heapArray)\n}\n\n/**\n * Return a `WasmBuffer` that holds float64 elements.\n *\n * @hidden For internal use only.\n *\n * @param wasmModule The `WasmModule` used to initialize the memory.\n * @param numElements The number of elements in the buffer.\n */\nexport function createFloat64WasmBuffer(wasmModule: WasmModule, numElements: number): WasmBuffer<Float64Array> {\n const elemSizeInBytes = 8\n const lengthInBytes = numElements * elemSizeInBytes\n const byteOffset = wasmModule._malloc(lengthInBytes)\n const elemOffset = byteOffset / elemSizeInBytes\n const heapArray = wasmModule.HEAPF64.subarray(elemOffset, elemOffset + numElements)\n return new WasmBuffer<Float64Array>(wasmModule, byteOffset, heapArray)\n}\n","// Copyright (c) 2020-2022 Climate Interactive / New Venture Fund\n\nimport type { OutputVarId, OutputVarSpec } from '../_shared'\nimport type { WasmBuffer } from './wasm-buffer'\nimport { createFloat64WasmBuffer, createInt32WasmBuffer } from './wasm-buffer'\nimport type { WasmModule } from './wasm-module'\n\n// For each output variable specified in the indices buffer, there\n// are 4 index values:\n// varIndex\n// subIndex0\n// subIndex1\n// subIndex2\n// NOTE: This value needs to match `INDICES_PER_OUTPUT` as defined in SDE's `model.c`\nconst indicesPerOutput = 4\n\n/**\n * An interface to the generated WebAssembly model. Allows for running the model with\n * a given set of input values, producing a set of output values.\n */\nexport class WasmModel {\n /** The start time for the model (aka `INITIAL TIME`). */\n public readonly startTime: number\n /** The end time for the model (aka `FINAL TIME`). */\n public readonly endTime: number\n /** The frequency with which output values are saved (aka `SAVEPER`). */\n public readonly saveFreq: number\n /** The number of save points for each output. */\n public readonly numSavePoints: number\n /**\n * The maximum number of output indices that can be passed for each run.\n * @hidden This is not yet part of the public API; it is exposed here for use\n * in experimental testing tools.\n */\n public readonly maxOutputIndices: number\n\n private readonly wasmRunModel: (inputsAddress: number, outputsAddress: number, outputIndicesAddress: number) => void\n\n /**\n * @param wasmModule The `WasmModule` that provides access to the native functions.\n */\n constructor(wasmModule: WasmModule) {\n function getNumberValue(funcName: string): number {\n const wasmGetValue: () => number = wasmModule.cwrap(funcName, 'number', [])\n return wasmGetValue()\n }\n this.startTime = getNumberValue('getInitialTime')\n this.endTime = getNumberValue('getFinalTime')\n this.saveFreq = getNumberValue('getSaveper')\n\n // Note that `getMaxOutputIndices` is not yet official, so proceed if it is not exposed\n try {\n this.maxOutputIndices = getNumberValue('getMaxOutputIndices')\n } catch (e) {\n this.maxOutputIndices = 0\n }\n\n // Each series will include one data point per \"save\", inclusive of the\n // start and end times\n this.numSavePoints = Math.round((this.endTime - this.startTime) / this.saveFreq) + 1\n\n this.wasmRunModel = wasmModule.cwrap('runModelWithBuffers', null, ['number', 'number', 'number'])\n }\n\n /**\n * Run the model, using inputs from the `inputs` buffer, and writing outputs into\n * the `outputs` buffer.\n *\n * @param inputs The buffer containing inputs in the order expected by the model.\n * @param outputs The buffer into which the model will store output values.\n * @param outputIndices The buffer used to control which variables are written to `outputs`.\n */\n runModel(\n inputs: WasmBuffer<Float64Array>,\n outputs: WasmBuffer<Float64Array>,\n outputIndices?: WasmBuffer<Int32Array>\n ): void {\n this.wasmRunModel(inputs.getAddress(), outputs.getAddress(), outputIndices?.getAddress() || 0)\n }\n}\n\n/**\n * The result of model initialization.\n */\nexport interface WasmModelInitResult {\n /** The wasm model. */\n model: WasmModel\n /** The buffer used to pass input values to the model. */\n inputsBuffer: WasmBuffer<Float64Array>\n /** The buffer used to receive output values from the model. */\n outputsBuffer: WasmBuffer<Float64Array>\n /**\n * The buffer used to control which variables are written to `outputsBuffer`.\n * @hidden This is not yet part of the public API; it is exposed here for use\n * in experimental testing tools.\n */\n outputIndicesBuffer?: WasmBuffer<Int32Array>\n /** The output variable IDs. */\n outputVarIds: OutputVarId[]\n}\n\n/**\n * Initialize the wasm model and buffers.\n *\n * @param wasmModule The `WasmModule` that wraps the `wasm` binary.\n * @param numInputs The number of input variables, per the spec file passed to `sde`.\n * @param outputVarIds The output variable IDs, per the spec file passed to `sde`.\n */\nexport function initWasmModelAndBuffers(\n wasmModule: WasmModule,\n numInputs: number,\n outputVarIds: OutputVarId[]\n): WasmModelInitResult {\n // Wrap the native C `runModelWithBuffers` function in a JS function that we can call\n const model = new WasmModel(wasmModule)\n\n // Allocate a buffer that is large enough to hold the input values\n const inputsBuffer = createFloat64WasmBuffer(wasmModule, numInputs)\n\n // Allocate a buffer that is large enough to hold the series data for each output variable\n const outputVarCount = Math.max(outputVarIds.length, model.maxOutputIndices)\n const outputsBuffer = createFloat64WasmBuffer(wasmModule, outputVarCount * model.numSavePoints)\n\n // Allocate a buffer for the output indices, if requested (for accessing internal variables)\n let outputIndicesBuffer: WasmBuffer<Int32Array>\n if (model.maxOutputIndices > 0) {\n outputIndicesBuffer = createInt32WasmBuffer(wasmModule, model.maxOutputIndices * indicesPerOutput)\n }\n\n return {\n model,\n inputsBuffer,\n outputsBuffer,\n outputIndicesBuffer,\n outputVarIds\n }\n}\n\n/**\n * @hidden This is not part of the public API; it is exposed here for use by\n * the synchronous and asynchronous model runner implementations.\n */\nexport function updateOutputIndices(indicesArray: Int32Array, outputVarSpecs: OutputVarSpec[]): void {\n if (indicesArray.length < outputVarSpecs.length * indicesPerOutput) {\n throw new Error('Length of indicesArray must be large enough to accommodate the given outputVarSpecs')\n }\n\n // Write the indices to the buffer\n let offset = 0\n for (const outputVarSpec of outputVarSpecs) {\n const subCount = outputVarSpec.subscriptIndices?.length || 0\n indicesArray[offset + 0] = outputVarSpec.varIndex\n indicesArray[offset + 1] = subCount > 0 ? outputVarSpec.subscriptIndices[0] : 0\n indicesArray[offset + 2] = subCount > 1 ? outputVarSpec.subscriptIndices[1] : 0\n indicesArray[offset + 3] = subCount > 2 ? outputVarSpec.subscriptIndices[2] : 0\n offset += indicesPerOutput\n }\n\n // Fill the remainder of the buffer with zeros\n indicesArray.fill(0, offset)\n}\n","// Copyright (c) 2020-2022 Climate Interactive / New Venture Fund\n\nimport type { InputVarId } from '../_shared'\n\n/** Callback functions that are called when the input value is changed. */\nexport interface InputCallbacks {\n /** Called after a new value is set. */\n onSet?: () => void\n}\n\n/**\n * Represents a writable model input.\n */\nexport interface InputValue {\n /** The ID of the associated input variable, as used in SDEverywhere. */\n varId: InputVarId\n /** Get the current value of the input. */\n get: () => number\n /** Set the input to the given value. */\n set: (value: number) => void\n /** Reset the input to its default value. */\n reset: () => void\n /** Callback functions that are called when the input value is changed. */\n callbacks: InputCallbacks\n}\n\n/**\n * Create a basic `InputValue` instance that notifies when a new value is set.\n *\n * @param varId The input variable ID, as used in SDEverywhere.\n * @param defaultValue The default value of the input.\n * @param initialValue The inital value of the input; if undefined, will use `defaultValue`.\n */\nexport function createInputValue(varId: InputVarId, defaultValue: number, initialValue?: number): InputValue {\n let currentValue = initialValue !== undefined ? initialValue : defaultValue\n\n // The `onSet` callback is initially undefined but will be installed by `ModelScheduler`\n const callbacks: InputCallbacks = {}\n\n const get = () => {\n return currentValue\n }\n\n const set = (newValue: number) => {\n if (newValue !== currentValue) {\n currentValue = newValue\n callbacks.onSet?.()\n }\n }\n\n const reset = () => {\n set(defaultValue)\n }\n\n return { varId, get, set, reset, callbacks }\n}\n","// Copyright (c) 2020-2022 Climate Interactive / New Venture Fund\n\nimport type { Result } from 'neverthrow'\nimport { ok, err } from 'neverthrow'\nimport type { OutputVarId, OutputVarSpec } from '../_shared'\n\n/** Indicates the type of error encountered when parsing an outputs buffer. */\nexport type ParseError = 'invalid-point-count'\n\n/** A data point. */\nexport interface Point {\n /** The x value (typically a time value). */\n x: number\n /** The y value. */\n y: number\n}\n\n/**\n * A time series of data points for an output variable.\n */\nexport class Series {\n /**\n * @param varId The ID for the output variable (as used by SDEverywhere).\n * @param points The data points for the variable, one point per time increment.\n */\n constructor(public readonly varId: OutputVarId, public readonly points: Point[]) {}\n\n /**\n * Return the Y value at the given time. Note that this does not attempt to interpolate\n * if there is no data point defined for the given time and will return undefined in\n * that case.\n *\n * @param time The x (time) value.\n * @return The y value for the given time, or undefined if there is no data point defined\n * for the given time.\n */\n getValueAtTime(time: number): number | undefined {\n // TODO: Add option to allow interpolation if the given time value is in between points\n // TODO: Use binary search to make lookups faster\n return this.points.find(p => p.x === time)?.y\n }\n\n /**\n * Create a new `Series` instance that is a copy of this one.\n */\n copy(): Series {\n // Create a deep copy\n const pointsCopy = this.points.map(p => ({ ...p }))\n return new Series(this.varId, pointsCopy)\n }\n}\n\n/** Represents the outputs from a model run. */\nexport class Outputs {\n /** The number of data points in each series. */\n public readonly seriesLength: number\n /** The array of series, one for each output variable. */\n public readonly varSeries: Series[]\n\n /**\n * The latest model run time, in milliseconds.\n * @hidden This is not yet part of the public API; it is exposed here for use\n * in performance testing tools.\n */\n public runTimeInMillis: number\n\n /**\n * The optional set of specs that dictate which variables from the model will be\n * stored in this `Outputs` instance. If undefined, the default set of outputs\n * will be stored (as configured in `varIds`).\n * @hidden This is not yet part of the public API; it is exposed here for use\n * in experimental testing tools.\n */\n public varSpecs?: OutputVarSpec[]\n\n /**\n * @param varIds The output variable identifiers.\n * @param startTime The start time for the model.\n * @param endTime The end time for the model.\n * @param saveFreq The frequency with which output values are saved (aka `SAVEPER`).\n */\n constructor(\n public readonly varIds: OutputVarId[],\n public readonly startTime: number,\n public readonly endTime: number,\n public readonly saveFreq = 1\n ) {\n // Each series will include one data point per \"save\", inclusive of the\n // start and end times\n this.seriesLength = Math.round((endTime - startTime) / saveFreq) + 1\n\n // Create an array of arrays, one for each output variable\n this.varSeries = new Array(varIds.length)\n\n // Populate the arrays, filling in the time for each point\n for (let i = 0; i < varIds.length; i++) {\n const points: Point[] = new Array(this.seriesLength)\n for (let j = 0; j < this.seriesLength; j++) {\n points[j] = { x: startTime + j * saveFreq, y: 0 }\n }\n const varId = varIds[i]\n this.varSeries[i] = new Series(varId, points)\n }\n }\n\n /**\n * The optional set of specs that dictate which variables from the model will be\n * stored in this `Outputs` instance. If undefined, the default set of outputs\n * will be stored (as configured in `varIds`).\n * @hidden This is not yet part of the public API; it is exposed here for use\n * in experimental testing tools.\n */\n setVarSpecs(varSpecs: OutputVarSpec[]) {\n if (varSpecs.length !== this.varIds.length) {\n throw new Error('Length of output varSpecs must match that of varIds')\n }\n this.varSpecs = varSpecs\n }\n\n /**\n * Parse the given raw float buffer (produced by the model) and store the values\n * into this `Outputs` instance.\n *\n * Note that the length of `outputsBuffer` must be greater than or equal to\n * the capacity of this `Outputs` instance. The `Outputs` instance is allowed\n * to be smaller to support the case where you want to extract a subset of\n * the time range in the buffer produced by the model.\n *\n * @param outputsBuffer The raw outputs buffer produced by the model.\n * @param rowLength The number of elements per row (one element per save point).\n * @return An `ok` result if the buffer is valid, otherwise an `err` result.\n */\n updateFromBuffer(outputsBuffer: Float64Array, rowLength: number): Result<void, ParseError> {\n const result = parseOutputsBuffer(outputsBuffer, rowLength, this)\n if (result.isOk()) {\n return ok(undefined)\n } else {\n return err(result.error)\n }\n }\n\n /**\n * Return the series for the given output variable.\n *\n * @param varId The ID of the output variable (as used by SDEverywhere).\n */\n getSeriesForVar(varId: OutputVarId): Series | undefined {\n const seriesIndex = this.varIds.indexOf(varId)\n if (seriesIndex >= 0) {\n return this.varSeries[seriesIndex]\n } else {\n // TODO: Error\n return undefined\n }\n }\n}\n\n/**\n * Parse the raw buffer produced by the model and store the values in the\n * given (reused) `Outputs` object.\n *\n * @param outputsBuffer The raw outputs buffer produced by the model.\n * @param rowLength The number of elements per row (one element per year or save point).\n * @return An `ok` result if the buffer is valid, otherwise an `err` result.\n * @hidden\n */\nfunction parseOutputsBuffer(\n outputsBuffer: Float64Array,\n rowLength: number,\n outputs: Outputs\n): Result<Outputs, ParseError> {\n const varCount = outputs.varIds.length\n const seriesLength = outputs.seriesLength\n if (rowLength < seriesLength || outputsBuffer.length < varCount * seriesLength) {\n return err('invalid-point-count')\n }\n\n // The buffer populated by the C `runModelWithBuffers` function is already\n // transposed, so the first \"row\" contains the values for the first output\n // variable (from start time to end time), and so on.\n for (let outputVarIndex = 0; outputVarIndex < varCount; outputVarIndex++) {\n const series = outputs.varSeries[outputVarIndex]\n let sourceIndex = rowLength * outputVarIndex\n for (let valueIndex = 0; valueIndex < seriesLength; valueIndex++) {\n series.points[valueIndex].y = validateNumber(outputsBuffer[sourceIndex])\n sourceIndex++\n }\n }\n\n return ok(outputs)\n}\n\n/**\n * Return the given number if it is valid, or undefined if it is invalid.\n *\n * SDE converts Vensim's `:NA:` values to `-DBL_MAX`, so if we see a very large negative\n * value, convert it to `undefined`. This is preferable to including extreme values\n * because some charting libraries (e.g. Chart.js) appear to choke on these large values\n * in certain browsers (e.g. Safari), but `undefined` appears to be handled better and\n * does a better job of signaling that the data point is undefined.\n *\n * @hidden\n */\nfunction validateNumber(x: number): number | undefined {\n if (!isNaN(x) && x > -1e32) {\n return x\n } else {\n return undefined\n }\n}\n","// Copyright (c) 2021-2022 Climate Interactive / New Venture Fund\n\nlet isWeb: boolean\n\n/**\n * Return a timestamp that can be passed to `perfElapsed` for calculating the elapsed\n * time of an operation.\n *\n * @hidden This is not part of the public API; exposed only for use in performance testing.\n */\nexport function perfNow(): unknown {\n // Note that `self` resolves to the window (in browser context) or the worker global scope\n // (in a Web Worker context)\n if (isWeb === undefined) {\n isWeb = typeof self !== 'undefined' && self?.performance !== undefined\n }\n if (isWeb) {\n return self.performance.now()\n } else {\n // XXX: We only use `process` in two places; we bypass type checking instead of\n // setting up type declarations\n // eslint-disable-next-line @typescript-eslint/ban-ts-comment\n // @ts-ignore\n return process?.hrtime()\n }\n}\n\n/**\n * Return the elapsed time between the given timestamp (created by `perfNow`) and now.\n *\n * @hidden This is not part of the public API; exposed only for use in performance testing.\n */\nexport function perfElapsed(t0: unknown): number {\n if (isWeb) {\n const t1 = self.performance.now()\n return (t1 as number) - (t0 as number)\n } else {\n // eslint-disable-next-line @typescript-eslint/ban-ts-comment\n // @ts-ignore\n const elapsed = process.hrtime(t0) as number[]\n // Convert from nanoseconds to milliseconds\n return (elapsed[0] * 1000000000 + elapsed[1]) / 1000000\n }\n}\n","// Copyright (c) 2020-2022 Climate Interactive / New Venture Fund\n\nimport type { WasmModelInitResult } from '../wasm-model'\nimport { updateOutputIndices } from '../wasm-model'\nimport type { InputValue } from './inputs'\nimport { Outputs } from './outputs'\nimport { perfElapsed, perfNow } from './perf'\n\n/**\n * Abstraction that allows for running the wasm model on the JS thread\n * or asynchronously (e.g. in a Web Worker), depending on the implementation.\n */\nexport interface ModelRunner {\n /**\n * Create an `Outputs` instance that is sized to accommodate the output variable\n * data stored by the model.\n *\n * @return A new `Outputs` instance.\n */\n createOutputs(): Outputs\n\n /**\n * Run the model.\n *\n * @param inputs The model input values (must be in the same order as in the spec file).\n * @param outputs The structure into which the model outputs will be stored.\n * @return A promise that resolves with the outputs when the model run is complete.\n */\n runModel(inputs: InputValue[], outputs: Outputs): Promise<Outputs>\n\n /**\n * Run the model synchronously.\n *\n * @param inputs The model input values (must be in the same order as in the spec file).\n * @param outputs The structure into which the model outputs will be stored.\n * @return The outputs of the run.\n *\n * @hidden This is only intended for internal use; some implementations may not support\n * running the model synchronously, in which case this will be undefined.\n */\n runModelSync?(inputs: InputValue[], outputs: Outputs): Outputs\n\n /**\n * Terminate the runner by releasing underlying resources (e.g., the worker thread or\n * Wasm module/buffers).\n */\n terminate(): Promise<void>\n}\n\n/**\n * Create a `ModelRunner` that runs the given wasm model on the JS thread.\n *\n * @param wasmResult The result of initializing the wasm model.\n */\nexport function createWasmModelRunner(wasmResult: WasmModelInitResult): ModelRunner {\n // Create views on the wasm buffers\n const wasmModel = wasmResult.model\n const inputsBuffer = wasmResult.inputsBuffer\n const inputsArray = inputsBuffer.getArrayView()\n const outputsBuffer = wasmResult.outputsBuffer\n const outputsArray = outputsBuffer.getArrayView()\n const outputIndicesBuffer = wasmResult.outputIndicesBuffer\n const outputIndicesArray = outputIndicesBuffer?.getArrayView()\n const rowLength = wasmModel.numSavePoints\n\n // Disallow `runModel` after the runner has been terminated\n let terminated = false\n\n const runModelSync = (inputs: InputValue[], outputs: Outputs) => {\n // Capture the current set of input values into the reusable buffer\n let i = 0\n for (const input of inputs) {\n inputsArray[i++] = input.get()\n }\n\n // Update the output indices, if needed\n const outputSpecs = outputs.varSpecs\n let useIndices: boolean\n if (outputIndicesArray && outputSpecs !== undefined && outputSpecs.length > 0) {\n updateOutputIndices(outputIndicesArray, outputSpecs)\n useIndices = true\n } else {\n useIndices = false\n }\n\n // Run the model\n const t0 = perfNow()\n wasmModel.runModel(inputsBuffer, outputsBuffer, useIndices ? outputIndicesBuffer : undefined)\n outputs.runTimeInMillis = perfElapsed(t0)\n\n // Capture the outputs array by copying the data into the given `Outputs`\n // data structure\n outputs.updateFromBuffer(outputsArray, rowLength)\n\n return outputs\n }\n\n return {\n createOutputs: () => {\n return new Outputs(wasmResult.outputVarIds, wasmModel.startTime, wasmModel.endTime, wasmModel.saveFreq)\n },\n\n runModel: (inputs, outputs) => {\n if (terminated) {\n return Promise.reject(new Error('Model runner has already been terminated'))\n }\n return Promise.resolve(runModelSync(inputs, outputs))\n },\n\n runModelSync: (inputs, outputs) => {\n if (terminated) {\n throw new Error('Model runner has already been terminated')\n }\n return runModelSync(inputs, outputs)\n },\n\n terminate: () => {\n if (!terminated) {\n // TODO: Release wasm-related resources (module or buffers)\n terminated = true\n }\n return Promise.resolve()\n }\n }\n}\n","// Copyright (c) 2023 Climate Interactive / New Venture Fund\n\nimport type { OutputVarId, OutputVarSpec } from '../_shared'\nimport { Outputs } from './outputs'\n\ntype SubscriptId = string\ntype DimensionId = string\n\n/**\n * Holds information about a subscript used in the model.\n */\ninterface Subscript {\n /** The subscript identifier, as used in SDE. */\n id: SubscriptId\n // /** The original subscript name, as used in the modeling tool. */\n // name: string\n /* The zero-based index for the subscript within the dimension. */\n index: number\n}\n\n/**\n * Holds information about a dimension (subscript family) used in the model.\n */\ninterface Dimension {\n /** The dimension identifier, as used in SDE. */\n id: DimensionId\n // /** The original dimension name, as used in the modeling tool. */\n // name: string\n /** The set of subscripts in this dimension. */\n subscripts: Subscript[]\n}\n\n/**\n * @hidden This is not yet part of the public API; it is exposed here for use\n * in experimental testing tools.\n */\nexport class ModelListing {\n public readonly varSpecs: Map<OutputVarId, OutputVarSpec> = new Map()\n\n constructor(modelJsonString: string) {\n // Parse the model listing JSON (as written by `sde generate --list`)\n const modelJson = JSON.parse(modelJsonString)\n\n // Put dimension info into a map for easier access\n const dimensions: Map<DimensionId, Dimension> = new Map()\n for (const dimInfo of modelJson.dimensions) {\n const dimId = dimInfo.name\n const subscripts: Subscript[] = []\n for (let i = 0; i < dimInfo.value.length; i++) {\n subscripts.push({\n id: dimInfo.value[i],\n // name: dimInfo.modelValue[i]\n index: i\n })\n }\n dimensions.set(dimId, {\n id: dimId,\n // name: dimInfo.modelName,\n subscripts\n })\n }\n\n function dimensionForId(dimId: DimensionId): Dimension {\n const dim = dimensions.get(dimId)\n if (dim === undefined) {\n throw new Error(`No dimension info found for id=${dimId}`)\n }\n return dim\n }\n\n // Gather the set of unique variables (consolidating refs that have different\n // subscripts but refer to the same variable)\n const baseVarIds: Set<OutputVarId> = new Set()\n for (const v of modelJson.variables) {\n // Get the base name of the variable (without subscripts)\n const baseVarId = varIdWithoutSubscripts(v.varName)\n\n if (!baseVarIds.has(baseVarId)) {\n // Look up dimensions from the map\n const dimIds: DimensionId[] = v.families || []\n const dimensions = dimIds.map(dimensionForId)\n\n // Expand and add all combinations of subscripts\n if (dimensions.length > 0) {\n // The variable is subscripted\n if (dimensions.length > 3) {\n // TODO: Add support for variables with more than 3 dimensions\n throw new Error('Variables with more than 3 dimensions not currently supported')\n }\n const dimSubs: Subscript[][] = []\n for (const dim of dimensions) {\n // For each dimension, get the array of subscripts;\n // for example, if the dimension has 3 subscripts, push\n // an array like ['_a1', '_a2', '_a3']\n dimSubs.push(dim.subscripts)\n }\n const combos = cartesianProductOf(dimSubs)\n for (const combo of combos) {\n // Add a spec for this var+subscript(s)\n const subs = combo.map(sub => sub.id).join(',')\n const subIndices = combo.map(sub => sub.index)\n const fullVarId = `${baseVarId}[${subs}]`\n this.varSpecs.set(fullVarId, {\n varIndex: v.varIndex,\n subscriptIndices: subIndices\n })\n }\n } else {\n // The variable is not subscripted\n this.varSpecs.set(baseVarId, {\n varIndex: v.varIndex\n })\n }\n\n // Mark this variable as visited\n baseVarIds.add(baseVarId)\n }\n }\n }\n\n /**\n * Create a new `Outputs` instance that uses the same start/end years as the given \"normal\"\n * `Outputs` instance but is prepared for reading the specified internal variables from the model.\n *\n * @param normalOutputs The `Outputs` that is used to access normal output variables from the model.\n * @param varIds The variable IDs to include with the new `Outputs` instance.\n */\n deriveOutputs(normalOutputs: Outputs, varIds: OutputVarId[]): Outputs {\n // Look up an `OutputVarSpec` for each variable ID\n const varSpecs: OutputVarSpec[] = []\n for (const varId of varIds) {\n const varSpec = this.varSpecs.get(varId)\n if (varSpec !== undefined) {\n varSpecs.push(varSpec)\n } else {\n // TODO: Throw error or just log warning?\n console.warn(`WARNING: No output var spec found for id=${varId}`)\n }\n }\n\n // Create a new `Outputs` instance that accepts the internal variables\n const newOutputs = new Outputs(varIds, normalOutputs.startTime, normalOutputs.endTime, normalOutputs.saveFreq)\n newOutputs.varSpecs = varSpecs\n return newOutputs\n }\n}\n\n/**\n * Helper function that returns the base name of a variable without the subscripts.\n */\nfunction varIdWithoutSubscripts(fullVarId: string): string {\n const bracketIndex = fullVarId.indexOf('[')\n if (bracketIndex >= 0) {\n return fullVarId.substring(0, bracketIndex)\n } else {\n return fullVarId\n }\n}\n\n/**\n * Return the cartesian product of the given array of arrays.\n *\n * For example, if we have an array that lists out two dimensions:\n * [ ['a1','a2'], ['b1','b2','b3'] ]\n * this function will return all the combinations, e.g.:\n * [ ['a1', 'b1'], ['a1', 'b2'], ['a1', 'b3'], ['a2', 'b1'], ... ]\n *\n * This can be used in place of nested for loops and has the benefit of working\n * for multi-dimensional inputs.\n */\nfunction cartesianProductOf<T>(arr: T[][]): T[][] {\n // Implementation based on: https://stackoverflow.com/a/36234242\n return arr.reduce(\n (a, b) => {\n return a.map(x => b.map(y => x.concat([y]))).reduce((v, w) => v.concat(w), [])\n },\n [[]]\n )\n}\n","// Copyright (c) 2020-2022 Climate Interactive / New Venture Fund\n\nimport type { InputVarId } from '../_shared'\nimport type { InputValue, ModelRunner, Outputs } from '../model-runner'\n\n/**\n * A high-level interface that schedules running of the underlying `WasmModel`.\n *\n * When one or more input values are changed, this class will schedule a model\n * run to be completed as soon as possible. When the model run has completed,\n * `onOutputsChanged` is called to notify that new output data is available.\n *\n * The `ModelRunner` is pluggable to allow for running the model synchronously\n * (on the main JavaScript thread) or asynchronously (in a Web Worker or Node.js\n * worker thread).\n */\nexport class ModelScheduler {\n /** The second array that holds a stable copy of the user inputs. */\n private readonly currentInputs: InputValue[]\n\n /** Whether a model run has been scheduled. */\n private runNeeded = false\n\n /** Whether a model run is in progress. */\n private runInProgress = false\n\n /** Called when `outputs` has been updated after a model run. */\n public onOutputsChanged?: (outputs: Outputs) => void\n\n /**\n * @param runner The model runner.\n * @param userInputs The input values, in the same order as in the spec file passed to `sde`.\n * @param outputs The structure into which the model outputs will be stored.\n */\n constructor(\n private readonly runner: ModelRunner,\n private readonly userInputs: InputValue[],\n private outputs: Outputs\n ) {\n // When any input has an updated value, schedule a model run on the next tick\n const afterSet = () => {\n this.runWasmModelIfNeeded()\n }\n for (const userInput of userInputs) {\n userInput.callbacks.onSet = afterSet\n }\n\n // Create a second array to hold a stable copy of the user inputs during model runs\n this.currentInputs = []\n for (const userInput of userInputs) {\n this.currentInputs.push(createSimpleInputValue(userInput.varId))\n }\n }\n\n /**\n * Schedule a wasm model run (if not already pending). When the run is\n * complete, save the outputs and call the `onOutputsChanged` callback.\n */\n private runWasmModelIfNeeded(): void {\n // Set a flag indicating that a new run is needed (even if one is already\n // in progress)\n this.runNeeded = true\n\n if (this.runInProgress) {\n // A run is already in progress; let it finish first\n return\n } else {\n // A run is not already in progress, so schedule it now. We use\n // `setTimeout` so that if a lot of inputs are all changing at once\n // (like after a reset), we wait for all those `set` or `reset`\n // calls to finish before gathering the input values into an array\n // and initiating the run on the next tick.\n this.runInProgress = true\n setTimeout(() => {\n // Kick off the (possibly asynchronous) model run\n this.runWasmModelNow()\n }, 0)\n }\n }\n\n /**\n * Run the wasm model asynchronously using the current set of input values.\n */\n private async runWasmModelNow(): Promise<void> {\n // Copy the current inputs into a separate array; this ensures that the\n // model run uses a stable set of inputs, even if the user continues to\n // change the inputs while the model is being run asynchronously\n for (let i = 0; i < this.userInputs.length; i++) {\n this.currentInputs[i].set(this.userInputs[i].get())\n }\n\n // Run the model with the current set of input values and save the outputs\n try {\n this.outputs = await this.runner.runModel(this.currentInputs, this.outputs)\n this.onOutputsChanged?.(this.outputs)\n } catch (e) {\n console.error(`ERROR: Failed to run model: ${e.message}`)\n }\n\n // See if another run is needed\n if (this.runNeeded) {\n // Keep `runInProgress` set, but clear the `runNeeded` flag\n this.runNeeded = false\n setTimeout(() => {\n this.runWasmModelNow()\n }, 0)\n } else {\n // No run needed, so clear both flags\n this.runNeeded = false\n this.runInProgress = false\n }\n }\n}\n\n/**\n * Create an `InputValue` that is only used to hold a copy of another input (no callbacks).\n * @hidden\n */\nfunction createSimpleInputValue(varId: InputVarId): InputValue {\n let currentValue = 0\n const get = () => {\n return currentValue\n }\n const set = (newValue: number) => {\n currentValue = newValue\n }\n const reset = () => {\n set(0)\n }\n return { varId, get, set, reset, callbacks: {} }\n}\n"],"mappings":"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","names":["dimensions"]}
@@ -0,0 +1,398 @@
1
+ import { Result } from 'neverthrow';
2
+
3
+ /** An input variable identifier string, as used in SDEverywhere. */
4
+ type InputVarId = string;
5
+ /** An output variable identifier string, as used in SDEverywhere. */
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
+ }
17
+
18
+ /**
19
+ * Type declaration for a WebAssembly module wrapper produced
20
+ * by the Emscripten compiler. This only declares the minimal
21
+ * set of fields needed by `WasmModel` and `WasmBuffer`.
22
+ */
23
+ interface WasmModule {
24
+ /** @hidden */
25
+ cwrap: (fname: string, rettype: string, argtypes: string[]) => any;
26
+ /** @hidden */
27
+ _malloc: (numBytes: number) => number;
28
+ /** @hidden */
29
+ _free: (byteOffset: number) => void;
30
+ /** @hidden */
31
+ HEAP32: Int32Array;
32
+ /** @hidden */
33
+ HEAPF64: Float64Array;
34
+ }
35
+
36
+ /**
37
+ * Wraps a `WebAssembly.Memory` buffer allocated on the wasm heap.
38
+ *
39
+ * When this is used synchronously (in the browser's normal JavaScript thread),
40
+ * the client can use `getArrayView` to write directly into the underlying memory.
41
+ *
42
+ * Note, however, that `WebAssembly.Memory` buffers cannot be transferred to/from
43
+ * a Web Worker. When using this class in a worker thread, create a separate
44
+ * `Float64Array` that can be transferred between the worker and the client running
45
+ * in the browser's normal JS thread, and then use `getArrayView` to copy into and
46
+ * out of the wasm buffer.
47
+ */
48
+ declare class WasmBuffer<ArrType> {
49
+ private readonly wasmModule;
50
+ private byteOffset;
51
+ private heapArray;
52
+ /**
53
+ * @param wasmModule The `WasmModule` used to initialize the memory.
54
+ * @param byteOffset The byte offset within the wasm heap.
55
+ * @param heapArray The array view on the underlying heap buffer.
56
+ */
57
+ constructor(wasmModule: WasmModule, byteOffset: number, heapArray: ArrType);
58
+ /**
59
+ * @return An `ArrType` view on the underlying heap buffer.
60
+ */
61
+ getArrayView(): ArrType;
62
+ /**
63
+ * @return The raw address of the underlying heap buffer.
64
+ * @hidden This is intended for use by `WasmModel` only.
65
+ */
66
+ getAddress(): number;
67
+ /**
68
+ * Dispose the buffer by freeing the allocated heap memory.
69
+ */
70
+ dispose(): void;
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>;
90
+
91
+ /**
92
+ * An interface to the generated WebAssembly model. Allows for running the model with
93
+ * a given set of input values, producing a set of output values.
94
+ */
95
+ declare class WasmModel {
96
+ /** The start time for the model (aka `INITIAL TIME`). */
97
+ readonly startTime: number;
98
+ /** The end time for the model (aka `FINAL TIME`). */
99
+ readonly endTime: number;
100
+ /** The frequency with which output values are saved (aka `SAVEPER`). */
101
+ readonly saveFreq: number;
102
+ /** The number of save points for each output. */
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;
110
+ private readonly wasmRunModel;
111
+ /**
112
+ * @param wasmModule The `WasmModule` that provides access to the native functions.
113
+ */
114
+ constructor(wasmModule: WasmModule);
115
+ /**
116
+ * Run the model, using inputs from the `inputs` buffer, and writing outputs into
117
+ * the `outputs` buffer.
118
+ *
119
+ * @param inputs The buffer containing inputs in the order expected by the model.
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`.
122
+ */
123
+ runModel(inputs: WasmBuffer<Float64Array>, outputs: WasmBuffer<Float64Array>, outputIndices?: WasmBuffer<Int32Array>): void;
124
+ }
125
+ /**
126
+ * The result of model initialization.
127
+ */
128
+ interface WasmModelInitResult {
129
+ /** The wasm model. */
130
+ model: WasmModel;
131
+ /** The buffer used to pass input values to the model. */
132
+ inputsBuffer: WasmBuffer<Float64Array>;
133
+ /** The buffer used to receive output values from the model. */
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>;
141
+ /** The output variable IDs. */
142
+ outputVarIds: OutputVarId[];
143
+ }
144
+ /**
145
+ * Initialize the wasm model and buffers.
146
+ *
147
+ * @param wasmModule The `WasmModule` that wraps the `wasm` binary.
148
+ * @param numInputs The number of input variables, per the spec file passed to `sde`.
149
+ * @param outputVarIds The output variable IDs, per the spec file passed to `sde`.
150
+ */
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;
157
+
158
+ /** Callback functions that are called when the input value is changed. */
159
+ interface InputCallbacks {
160
+ /** Called after a new value is set. */
161
+ onSet?: () => void;
162
+ }
163
+ /**
164
+ * Represents a writable model input.
165
+ */
166
+ interface InputValue {
167
+ /** The ID of the associated input variable, as used in SDEverywhere. */
168
+ varId: InputVarId;
169
+ /** Get the current value of the input. */
170
+ get: () => number;
171
+ /** Set the input to the given value. */
172
+ set: (value: number) => void;
173
+ /** Reset the input to its default value. */
174
+ reset: () => void;
175
+ /** Callback functions that are called when the input value is changed. */
176
+ callbacks: InputCallbacks;
177
+ }
178
+ /**
179
+ * Create a basic `InputValue` instance that notifies when a new value is set.
180
+ *
181
+ * @param varId The input variable ID, as used in SDEverywhere.
182
+ * @param defaultValue The default value of the input.
183
+ * @param initialValue The inital value of the input; if undefined, will use `defaultValue`.
184
+ */
185
+ declare function createInputValue(varId: InputVarId, defaultValue: number, initialValue?: number): InputValue;
186
+
187
+ /** Indicates the type of error encountered when parsing an outputs buffer. */
188
+ type ParseError = 'invalid-point-count';
189
+ /** A data point. */
190
+ interface Point {
191
+ /** The x value (typically a time value). */
192
+ x: number;
193
+ /** The y value. */
194
+ y: number;
195
+ }
196
+ /**
197
+ * A time series of data points for an output variable.
198
+ */
199
+ declare class Series {
200
+ readonly varId: OutputVarId;
201
+ readonly points: Point[];
202
+ /**
203
+ * @param varId The ID for the output variable (as used by SDEverywhere).
204
+ * @param points The data points for the variable, one point per time increment.
205
+ */
206
+ constructor(varId: OutputVarId, points: Point[]);
207
+ /**
208
+ * Return the Y value at the given time. Note that this does not attempt to interpolate
209
+ * if there is no data point defined for the given time and will return undefined in
210
+ * that case.
211
+ *
212
+ * @param time The x (time) value.
213
+ * @return The y value for the given time, or undefined if there is no data point defined
214
+ * for the given time.
215
+ */
216
+ getValueAtTime(time: number): number | undefined;
217
+ /**
218
+ * Create a new `Series` instance that is a copy of this one.
219
+ */
220
+ copy(): Series;
221
+ }
222
+ /** Represents the outputs from a model run. */
223
+ declare class Outputs {
224
+ readonly varIds: OutputVarId[];
225
+ readonly startTime: number;
226
+ readonly endTime: number;
227
+ readonly saveFreq: number;
228
+ /** The number of data points in each series. */
229
+ readonly seriesLength: number;
230
+ /** The array of series, one for each output variable. */
231
+ readonly varSeries: Series[];
232
+ /**
233
+ * The latest model run time, in milliseconds.
234
+ * @hidden This is not yet part of the public API; it is exposed here for use
235
+ * in performance testing tools.
236
+ */
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[];
246
+ /**
247
+ * @param varIds The output variable identifiers.
248
+ * @param startTime The start time for the model.
249
+ * @param endTime The end time for the model.
250
+ * @param saveFreq The frequency with which output values are saved (aka `SAVEPER`).
251
+ */
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;
261
+ /**
262
+ * Parse the given raw float buffer (produced by the model) and store the values
263
+ * into this `Outputs` instance.
264
+ *
265
+ * Note that the length of `outputsBuffer` must be greater than or equal to
266
+ * the capacity of this `Outputs` instance. The `Outputs` instance is allowed
267
+ * to be smaller to support the case where you want to extract a subset of
268
+ * the time range in the buffer produced by the model.
269
+ *
270
+ * @param outputsBuffer The raw outputs buffer produced by the model.
271
+ * @param rowLength The number of elements per row (one element per save point).
272
+ * @return An `ok` result if the buffer is valid, otherwise an `err` result.
273
+ */
274
+ updateFromBuffer(outputsBuffer: Float64Array, rowLength: number): Result<void, ParseError>;
275
+ /**
276
+ * Return the series for the given output variable.
277
+ *
278
+ * @param varId The ID of the output variable (as used by SDEverywhere).
279
+ */
280
+ getSeriesForVar(varId: OutputVarId): Series | undefined;
281
+ }
282
+
283
+ /**
284
+ * Abstraction that allows for running the wasm model on the JS thread
285
+ * or asynchronously (e.g. in a Web Worker), depending on the implementation.
286
+ */
287
+ interface ModelRunner {
288
+ /**
289
+ * Create an `Outputs` instance that is sized to accommodate the output variable
290
+ * data stored by the model.
291
+ *
292
+ * @return A new `Outputs` instance.
293
+ */
294
+ createOutputs(): Outputs;
295
+ /**
296
+ * Run the model.
297
+ *
298
+ * @param inputs The model input values (must be in the same order as in the spec file).
299
+ * @param outputs The structure into which the model outputs will be stored.
300
+ * @return A promise that resolves with the outputs when the model run is complete.
301
+ */
302
+ runModel(inputs: InputValue[], outputs: Outputs): Promise<Outputs>;
303
+ /**
304
+ * Run the model synchronously.
305
+ *
306
+ * @param inputs The model input values (must be in the same order as in the spec file).
307
+ * @param outputs The structure into which the model outputs will be stored.
308
+ * @return The outputs of the run.
309
+ *
310
+ * @hidden This is only intended for internal use; some implementations may not support
311
+ * running the model synchronously, in which case this will be undefined.
312
+ */
313
+ runModelSync?(inputs: InputValue[], outputs: Outputs): Outputs;
314
+ /**
315
+ * Terminate the runner by releasing underlying resources (e.g., the worker thread or
316
+ * Wasm module/buffers).
317
+ */
318
+ terminate(): Promise<void>;
319
+ }
320
+ /**
321
+ * Create a `ModelRunner` that runs the given wasm model on the JS thread.
322
+ *
323
+ * @param wasmResult The result of initializing the wasm model.
324
+ */
325
+ declare function createWasmModelRunner(wasmResult: WasmModelInitResult): ModelRunner;
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
+
344
+ /**
345
+ * Return a timestamp that can be passed to `perfElapsed` for calculating the elapsed
346
+ * time of an operation.
347
+ *
348
+ * @hidden This is not part of the public API; exposed only for use in performance testing.
349
+ */
350
+ declare function perfNow(): unknown;
351
+ /**
352
+ * Return the elapsed time between the given timestamp (created by `perfNow`) and now.
353
+ *
354
+ * @hidden This is not part of the public API; exposed only for use in performance testing.
355
+ */
356
+ declare function perfElapsed(t0: unknown): number;
357
+
358
+ /**
359
+ * A high-level interface that schedules running of the underlying `WasmModel`.
360
+ *
361
+ * When one or more input values are changed, this class will schedule a model
362
+ * run to be completed as soon as possible. When the model run has completed,
363
+ * `onOutputsChanged` is called to notify that new output data is available.
364
+ *
365
+ * The `ModelRunner` is pluggable to allow for running the model synchronously
366
+ * (on the main JavaScript thread) or asynchronously (in a Web Worker or Node.js
367
+ * worker thread).
368
+ */
369
+ declare class ModelScheduler {
370
+ private readonly runner;
371
+ private readonly userInputs;
372
+ private outputs;
373
+ /** The second array that holds a stable copy of the user inputs. */
374
+ private readonly currentInputs;
375
+ /** Whether a model run has been scheduled. */
376
+ private runNeeded;
377
+ /** Whether a model run is in progress. */
378
+ private runInProgress;
379
+ /** Called when `outputs` has been updated after a model run. */
380
+ onOutputsChanged?: (outputs: Outputs) => void;
381
+ /**
382
+ * @param runner The model runner.
383
+ * @param userInputs The input values, in the same order as in the spec file passed to `sde`.
384
+ * @param outputs The structure into which the model outputs will be stored.
385
+ */
386
+ constructor(runner: ModelRunner, userInputs: InputValue[], outputs: Outputs);
387
+ /**
388
+ * Schedule a wasm model run (if not already pending). When the run is
389
+ * complete, save the outputs and call the `onOutputsChanged` callback.
390
+ */
391
+ private runWasmModelIfNeeded;
392
+ /**
393
+ * Run the wasm model asynchronously using the current set of input values.
394
+ */
395
+ private runWasmModelNow;
396
+ }
397
+
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 };