@sdeverywhere/runtime 0.2.1 → 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/README.md CHANGED
@@ -1,6 +1,16 @@
1
1
  # @sdeverywhere/runtime
2
2
 
3
- This package provides a simplified API around a System Dynamics model as generated by [SDEverywhere](https://github.com/climateinteractive/SDEverywhere) and compiled to a WebAssembly (Wasm) module via [Emscripten](https://emscripten.org).
3
+ This package provides a simplified runtime API around a System Dynamics model as generated
4
+ by the [SDEverywhere](https://github.com/climateinteractive/SDEverywhere) transpiler.
5
+
6
+ Note that the SDEverywhere transpiler can be configured to generate model code in different formats:
7
+
8
+ - JavaScript code, which can be used directly without an extra compilation step, or
9
+ - C code, which can be compiled to a more optimized WebAssembly (Wasm) module (this requires an extra tool called [Emscripten](https://emscripten.org); see the `@sdeverywhere/plugin-wasm` package for more details)
10
+
11
+ The `@sdeverywhere/runtime` package presents a format-agnostic API that can be used to
12
+ run your model, regardless of whether you generate a pure JavaScript model or a WebAssembly
13
+ model.
4
14
 
5
15
  ## Quick Start
6
16
 
@@ -27,50 +37,58 @@ _NOTE:_ If you followed the "Quick Start" instructions and/or used the
27
37
  steps listed below are already implemented for you in the generated `core` package,
28
38
  and you can work directly with a `ModelRunner` and/or `ModelScheduler` instance.
29
39
 
30
- ### 1. Initialize the `WasmModel`
40
+ ### 1. Import your generated model
31
41
 
32
- In your application, load the wasm module using the wrapper produced by
33
- Emscripten, then pass it to `initWasmModelAndBuffers`.
34
- This will create the `WasmModel` and `WasmBuffer` instances that will be
35
- used in the next step to initalize the `ModelRunner`.
42
+ In your application, import the model file that was generated by the SDEverywhere
43
+ transpiler.
44
+ Depending on how you configured your project (for example, if you set a different
45
+ `prepDir`), the generated model file may be in a different location than what is
46
+ shown in this example, but the approach will generally be the same.
36
47
 
37
48
  ```ts
38
- import { initWasmModelAndBuffers, WasmModelInitResult } from '@sdeverywhere/runtime'
39
- import loadWasm from './generated/mymodel'
40
-
41
- // These are the same lists (and must be in the same order) as the spec file passed to `sde`.
42
- const inputVarNames = [] // from spec.json
43
- const outputVarNames = [] // from spec.json
44
-
45
- async function initWasmModel(): Promise<WasmModelInitResult> {
46
- // Load the wasm module asynchronously
47
- const wasmModule = await loadWasm()
48
-
49
- // Initialize the wasm model and its associated buffers
50
- return initWasmModelAndBuffers(wasmModule, inputVarNames.length, outputVarNames)
51
- }
49
+ import loadGeneratedModel from './sde-prep/generated-model.js'
52
50
  ```
53
51
 
54
- ### 2. Initialize the `ModelRunner`
52
+ ### 2. Initialize a `ModelRunner`
55
53
 
56
54
  The next step is to create a `ModelRunner` instance, which simplifies
57
55
  the process of running a `WasmModel` with a given set of inputs and
58
56
  parsing the outputs.
59
57
  The `ModelRunner` produces an `Outputs` instance that provides easy
60
58
  access to time series data for each output variable in the model.
61
- The `createWasmModelRunner` function is the simplest way to create
62
- a `ModelRunner` that works with your `WasmModel`:
59
+
60
+ Note that SDEverywhere offers two implementations of the `ModelRunner`
61
+ interface:
62
+
63
+ - The `createSynchronousModelRunner` function creates a `ModelRunner`
64
+ that runs your generated model on the main JavaScript thread. This
65
+ is the simplest option and is sufficient for small models, but for
66
+ larger models that take longer to run, it may block the JavaScript
67
+ thread and cause your application to appear unresponsive.
68
+ - The `spawnAsyncModelRunner` function (in the separate
69
+ `@sdeverywhere/runtime-async` package) provides an alternative
70
+ implementation of `ModelRunner` that runs your generated model in
71
+ a Web Worker or Node.js worker thread. This requires an extra
72
+ build-time step (see the `@sdeverywhere/plugin-worker` package),
73
+ but the benefit of an asynchronous runner is that the model can
74
+ run in a separate thread, which frees up the main thread for user
75
+ interface work and other computation.
76
+
77
+ The following example demonstrates the use of the
78
+ `createSynchronousModelRunner` function:
63
79
 
64
80
  ```ts
65
- import { createWasmModelRunner, createInputValue, Outputs } from '@sdeverywhere/runtime'
81
+ import { createSynchronousModelRunner } from '@sdeverywhere/runtime'
82
+ import loadGeneratedModel from './sde-prep/generated-model.js'
66
83
 
67
84
  async function main() {
68
- // Initialize the `WasmModel` and `ModelRunner`
69
- const wasmResult = await initWasmModel()
70
- const modelRunner = createWasmModelRunner(wasmResult)
85
+ // Initialize the `ModelRunner`
86
+ const generatedModel = await loadGeneratedModel()
87
+ const modelRunner = createSynchronousModelRunner(generatedModel)
71
88
 
72
- // Create a set of `InputValue` instances corresponding to the inputs in the spec.json file
73
- const inputs = [createInputValue('_input1', 2), createInputValue('_input2', 10)] // etc
89
+ // Create an array that holds the model input values; these must be in the same order
90
+ // as the inputs that are specified in your `sde.config.js` or `spec.json` file
91
+ const inputs = [2, 10] // etc
74
92
 
75
93
  // Create an `Outputs` instance to hold the model outputs
76
94
  let outputs = modelRunner.createOutputs()
@@ -84,21 +102,28 @@ async function main() {
84
102
  console.log(`Temperature change in 2100: ${tempChangeIn2100}`)
85
103
  ```
86
104
 
87
- See the `@sdeverywhere/runtime-async` package for an alternative
88
- implementation of `ModelRunner` that allows for running a model in a Web
89
- Worker or Node.js worker thread.
90
-
91
105
  ### 3. Initialize a `ModelScheduler` (optional)
92
106
 
93
- If you build a more complex application with a user interface around a
94
- model, the `ModelScheduler` class takes care of automatically scheduling
95
- and running the model whenever there are changes to input variables:
107
+ If you build a more complex application with a user interface around a model
108
+ (especially with a responsive web framework such as Svelte, Vue, React, etc),
109
+ the `ModelScheduler` class takes care of automatically scheduling and running
110
+ the model whenever there are changes to input variables:
96
111
 
97
112
  ```ts
98
- import { ModelScheduler } from '@sdeverywhere/runtime'
113
+ import { createInputValue, createSynchronousModelRunner, ModelScheduler } from '@sdeverywhere/runtime'
114
+ import loadGeneratedModel from './sde-prep/generated-model.js'
99
115
 
100
116
  async function initModel() {
101
- // Initialize the `WasmModel`, `ModelRunner`, inputs, and outputs as above
117
+ // Initialize the `ModelRunner`
118
+ const generatedModel = await loadGeneratedModel()
119
+ const modelRunner = createSynchronousModelRunner(generatedModel)
120
+
121
+ // Create an array of reactive `InputValue` instances; these must be in the same order
122
+ // as the inputs that are specified in your `sde.config.js` or `spec.json` file
123
+ const inputs = [createInputValue('_input1', 2), createInputValue('_input2', 0)] // etc
124
+
125
+ // Create a `ModelScheduler`
126
+ const outputs = modelRunner.createOutputs()
102
127
  const modelScheduler = new ModelScheduler(modelRunner, inputs, outputs)
103
128
 
104
129
  // Get notified when new output data is available
@@ -128,16 +153,18 @@ $ emcc \
128
153
  build/<mymodel>.c build/macros.c build/model.c build/vensim.c \
129
154
  -Ibuild -o ./output/<mymodel>.js -Wall -Os \
130
155
  -s STRICT=1 -s MALLOC=emmalloc -s FILESYSTEM=0 -s MODULARIZE=1 \
131
- -s EXPORTED_FUNCTIONS="['_malloc','_getInitialTime','_getFinalTime','_getSaveper','_runModelWithBuffers']" \
156
+ -s EXPORTED_FUNCTIONS="['_malloc','_free','_getInitialTime','_getFinalTime','_getSaveper','_setLookup','_runModelWithBuffers']" \
132
157
  -s EXPORTED_RUNTIME_METHODS="['cwrap']"
133
158
  ```
134
159
 
135
160
  Note that the generated module must export the following functions at minimum:
136
161
 
137
162
  - `_malloc`
163
+ - `_free`
138
164
  - `_getInitialTime`
139
165
  - `_getFinalTime`
140
166
  - `_getSaveper`
167
+ - `_setLookup`
141
168
  - `_runModelWithBuffers`
142
169
  - `cwrap`
143
170