@sdeverywhere/cli 0.7.45 → 0.7.47

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
@@ -2,7 +2,7 @@
2
2
 
3
3
  This package contains the `sde` command line interface for the [SDEverywhere](https://github.com/climateinteractive/SDEverywhere) suite of tools.
4
4
 
5
- SDEverywhere can be used to translate System Dynamics models from Vensim to C and WebAssembly.
5
+ SDEverywhere can be used to translate System Dynamics models from Vensim or Stella format to JavaScript, C, and WebAssembly.
6
6
 
7
7
  For more details on the full suite of tools and libraries provided in SDEverywhere, refer to the top-level [README](https://github.com/climateinteractive/SDEverywhere) in the SDEverywhere repository.
8
8
 
@@ -46,10 +46,10 @@ Use `sde -h` to see a list of all commands.
46
46
 
47
47
  Use `sde {command}` to see options for a command.
48
48
 
49
- It is usually easiest to run these commands from the directory where the `.mdl` file is located.
49
+ It is usually easiest to run these commands from the directory where the model file (for example, a Vensim `.mdl` file or a Stella `.stmx` file) is located.
50
50
  The `{model}` placeholder can be the model filename, for instance `arrays.mdl`, or simply the model name `arrays`.
51
51
 
52
- If you are not running from the model directory, you can give a full pathname to locate the `.mdl` file anywhere on the system.
52
+ If you are not running from the model directory, you can give a full pathname to locate the model file anywhere on the system.
53
53
 
54
54
  By default, SDEverywhere will create a `build` directory in your model directory to hold the generated code and the compiled model.
55
55
  If you run the model, it will also create an `output` directory by default.
@@ -70,9 +70,9 @@ sde generate --genc --spec {model}_spec.json {model}
70
70
  #### Start a local development builder/server
71
71
 
72
72
  The `sde dev` command is great for local development of a web application.
73
- It will start a builder process that rebuilds your app and runs QA checks against your model any time you save changes to your `mdl` file.
74
- You can leave the builder running while developing your model in Vensim.
75
- The app and model-check tabs in your browser will refresh automatically whenever you save changes in Vensim or make edits to your application code.
73
+ It will start a builder process that rebuilds your app and runs QA checks against your model any time you save changes to your model file.
74
+ You can leave the builder running while developing your model in Vensim or Stella.
75
+ The app and model-check tabs in your browser will refresh automatically whenever you save changes in your modeling tool or make edits to your application code.
76
76
 
77
77
  See [`examples/hello-world`](https://github.com/climateinteractive/SDEverywhere/tree/main/examples/hello-world) for a simple example of an `sde.config.js` file.
78
78
  You can also follow the [Quick Start](https://github.com/climateinteractive/SDEverywhere/tree/main/examples/sir#quick-start) instructions for `examples/sir`, which will generate a more complete example of an `sde.config.js` file.
@@ -176,8 +176,9 @@ sde which
176
176
 
177
177
  _NOTE:_ The following sections refer to "model specification files" (or "spec files" as a shorthand).
178
178
  These JSON spec files are generally used by the lower-level `sde` commands, such as `sde generate`.
179
- We are gradually adding support for a more flexible configuration file format (`sde.config.js`) that works with newer commands such as `sde dev` and `sde bundle`.
180
- We hope to unify these configuration file formats soon to eliminate any confusion about which file format can be used with which command (see related issue [#327](https://github.com/climateinteractive/SDEverywhere/issues/327)).
179
+ The higher-level `sde dev` and `sde bundle` commands use a more flexible configuration file format (`sde.config.js`) that also allows for defining plugins.
180
+ The two formats share the same property definitions where they overlap, so most of the properties documented below are also available in the `modelSpec` section of an `sde.config.js` file.
181
+ The exception is the input and output variables: a spec file takes plain variable names in `inputVarNames` and `outputVarNames`, whereas an `sde.config.js` file uses higher-level `inputs` and `outputs` properties that also accept objects carrying additional information about each variable.
181
182
 
182
183
  #### Specify input and output variables
183
184
 
@@ -198,6 +199,14 @@ Be sure to include `Time` first among the output variables.
198
199
 
199
200
  #### Specify external data sources
200
201
 
202
+ Add a `datFiles` section to the spec file to have SDEverywhere read data for exogenous data variables from one or more Vensim `dat` files.
203
+ Each entry can either be a plain file name, or an object that maps a variable name prefix to a file name.
204
+ There are examples in the `extdata` and `getdata` sample models.
205
+
206
+ ```json
207
+ "datFiles": ["data.dat", { "prefix ": "other.dat" }]
208
+ ```
209
+
201
210
  Add a `directData` section to the spec file to have SDEverywhere read data from an Excel file into lookups with a variable name prefix.
202
211
  There is an example in the `directdata` sample model.
203
212
 
@@ -207,6 +216,25 @@ There is an example in the `directdata` sample model.
207
216
  }
208
217
  ```
209
218
 
219
+ #### Supported spec file properties
220
+
221
+ The full set of supported properties is defined by the [`ModelSpec`](https://github.com/climateinteractive/SDEverywhere/blob/main/packages/compile/src/_shared/model-spec.js) type in the `@sdeverywhere/compile` package, which is the authoritative reference.
222
+ The table below is a summary.
223
+
224
+ | Property | Type | Description |
225
+ | ------------------------- | ------------------------ | ------------------------------------------------------------------------------------------------------------------------------------- |
226
+ | `inputVarNames` | `string[]` | The input variables, using the names as they appear in the modeling tool. |
227
+ | `outputVarNames` | `string[]` | The output variables, using the names as they appear in the modeling tool. It is customary to list `Time` first. |
228
+ | `datFiles` | `(string \| object)[]` | The `dat` files that provide data for exogenous data variables, resolved relative to the model directory. |
229
+ | `directData` | `object` | Maps the data tag used in a `GET DIRECT {DATA,CONSTANTS,LOOKUPS}` call (for example, `?data`) to an `xlsx` file name. |
230
+ | `dimensionFamilies` | `object` | Maps a dimension name to its family name, for cases where the family cannot be inferred from the model alone. |
231
+ | `specialSeparationDims` | `object` | Maps a variable identifier to the dimension(s) on which that variable should be separated, which can be used to break a cycle. |
232
+ | `separateAllVarsWithDims` | `(string \| string[])[]` | The dimensions for which all variables should be separated, as an alternative to listing each variable in `specialSeparationDims`. |
233
+ | `bundleListing` | `boolean` | Whether to bundle a model listing with the generated model so that the `runtime` package can resolve variables by name or identifier. |
234
+ | `customConstants` | `boolean \| string[]` | Whether (or which) constants can be overridden at runtime using `setConstant`. |
235
+ | `customLookups` | `boolean \| string[]` | Whether (or which) lookups can be overridden at runtime using `setLookup`. |
236
+ | `customOutputs` | `boolean \| string[]` | Whether (or which) variables can be captured at runtime using `storeOutput`. |
237
+
210
238
  #### Generating, compiling, running, and testing the C code
211
239
 
212
240
  To generate C code using the `--spec` argument, enter the following command:
@@ -238,7 +266,7 @@ There is a `setInputs` implementation in the generated code that gets called at
238
266
  It takes a string with serialized input values and sets variable values from it.
239
267
  The serialization format depends on the needs of your application.
240
268
  You can replace `setInputs` if you want to use a different serialization form.
241
- The input variables are listed in the `inputVars` section of the spec file.
269
+ The input variables are listed in the `inputVarNames` section of the spec file.
242
270
  Look at the `arrays` model for an example.
243
271
 
244
272
  The generated format minimizes the amount of data on the wire for web applications.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sdeverywhere/cli",
3
- "version": "0.7.45",
3
+ "version": "0.7.47",
4
4
  "description": "Contains the `sde` command line interface for the SDEverywhere tool suite.",
5
5
  "type": "module",
6
6
  "files": [
@@ -11,8 +11,8 @@
11
11
  "sde": "src/main.js"
12
12
  },
13
13
  "dependencies": {
14
- "@sdeverywhere/build": "^0.3.14",
15
- "@sdeverywhere/compile": "^0.7.32",
14
+ "@sdeverywhere/build": "^0.3.16",
15
+ "@sdeverywhere/compile": "^0.7.34",
16
16
  "byline": "^5.0.0",
17
17
  "ramda": "^0.27.0",
18
18
  "shelljs": "^0.10.0",
@@ -24,7 +24,7 @@
24
24
  // #define PRINT_ALLOCATIONS_DEBUG_INFO
25
25
 
26
26
  // Return true if the value is near zero up to the epsilon tolerance.
27
- static inline bool __isZero(double value) { return fabs(value) < _epsilon; }
27
+ static inline bool __isZero(double value) { return fabs(value) < SDE_EPSILON; }
28
28
  // Compute the absolute difference when x or y is near zero, otherwise compute
29
29
  // the relative difference, with y considered as the baseline.
30
30
  static inline double __difference(double x, double y) {
@@ -37,7 +37,7 @@ static inline double __difference(double x, double y) {
37
37
  return diff;
38
38
  }
39
39
  // Return true if the values are equal up to the tolerance.
40
- static inline bool __isEqual(double x, double y) { return __difference(x, y) < _epsilon; }
40
+ static inline bool __isEqual(double x, double y) { return __difference(x, y) < SDE_EPSILON; }
41
41
  // Clamp x to the interval [0,1].
42
42
  static inline double __clamp01(double x) {
43
43
  if (x < 0.0) return 0.0;
@@ -440,7 +440,7 @@ double* _ALLOCATE_BY_PRIORITY(
440
440
 
441
441
  // Validate request values (must be non-negative)
442
442
  for (size_t i = 0; i < num_requesters; i++) {
443
- if (request_quantities[i] < -_epsilon) {
443
+ if (request_quantities[i] < -SDE_EPSILON) {
444
444
  fprintf(stderr,
445
445
  "_ALLOCATE_BY_PRIORITY encountered negative request value at index %zu: %f\n",
446
446
  i, request_quantities[i]);
@@ -449,7 +449,7 @@ double* _ALLOCATE_BY_PRIORITY(
449
449
  }
450
450
 
451
451
  // Validate width (must not be negative)
452
- if (width < -_epsilon) {
452
+ if (width < -SDE_EPSILON) {
453
453
  fprintf(stderr,
454
454
  "_ALLOCATE_BY_PRIORITY encountered invalid width value: %f\n"
455
455
  "Width must not be negative.\n",
@@ -458,7 +458,7 @@ double* _ALLOCATE_BY_PRIORITY(
458
458
  }
459
459
 
460
460
  // Validate supply (must not be negative)
461
- if (supply < -_epsilon) {
461
+ if (supply < -SDE_EPSILON) {
462
462
  fprintf(stderr,
463
463
  "_ALLOCATE_BY_PRIORITY encountered invalid supply value: %f\n"
464
464
  "Supply must not be negative.\n",
@@ -472,7 +472,7 @@ double* _ALLOCATE_BY_PRIORITY(
472
472
  }
473
473
 
474
474
  // If supply = 0, all targets get allocated 0
475
- if(fabs(supply) < _epsilon) {
475
+ if(fabs(supply) < SDE_EPSILON) {
476
476
  return allocations;
477
477
  }
478
478
 
@@ -539,7 +539,7 @@ double* _ALLOCATE_BY_PRIORITY(
539
539
  size_t c_i = 0;
540
540
 
541
541
  // Continue allocating until supply is exhausted
542
- while (supply > _epsilon) {
542
+ while (supply > SDE_EPSILON) {
543
543
  // Check if there are any active targets left
544
544
  bool any_active = false;
545
545
  for (size_t i = 0; i < m; i++) {
package/src/c/sde.h CHANGED
@@ -25,8 +25,6 @@ extern "C" {
25
25
  #include "vensim.h"
26
26
  #include "macros.h"
27
27
 
28
- EXTERN double _epsilon;
29
-
30
28
  // Enable this to add print statements in initLevels and evalAux for debugging.
31
29
  // #define PRDBG
32
30
  #ifdef PRDBG
package/src/c/vensim.c CHANGED
@@ -1,19 +1,16 @@
1
1
  #include "sde.h"
2
2
 
3
- double _epsilon = 1e-6;
3
+ //
4
+ // Note: This file only contains implementations of complex functions. Simple
5
+ // (inline-friendly) functions are defined as `static inline` functions in `vensim.h`
6
+ // so that they can be inlined at each call site (for improved performance).
7
+ //
4
8
 
5
9
  //
6
10
  // Vensim functions
7
11
  // See the Vensim Reference Manual for descriptions of the functions.
8
12
  // http://www.vensim.com/documentation/index.html?22300.htm
9
13
  //
10
- double _PULSE(double start, double width) {
11
- double time_plus = _time + _time_step / 2.0;
12
- if (width == 0.0) {
13
- width = _time_step;
14
- }
15
- return (time_plus > start && time_plus < start + width) ? 1.0 : 0.0;
16
- }
17
14
  double _PULSE_TRAIN(double start, double width, double interval, double end) {
18
15
  double n = floor((end - start) / interval);
19
16
  for (double k = 0; k <= n; k++) {
@@ -23,29 +20,6 @@ double _PULSE_TRAIN(double start, double width, double interval, double end) {
23
20
  }
24
21
  return 0.0;
25
22
  }
26
- double _RAMP(double slope, double start_time, double end_time) {
27
- // Return 0 until the start time is exceeded.
28
- // Interpolate from start time to end time.
29
- // Hold at the end time value.
30
- // Allow start time > end time.
31
- if (_time > start_time) {
32
- if (_time < end_time || start_time > end_time) {
33
- return slope * (_time - start_time);
34
- } else {
35
- return slope * (end_time - start_time);
36
- }
37
- } else {
38
- return 0.0;
39
- }
40
- }
41
- double _XIDZ(double a, double b, double x) { return fabs(b) < _epsilon ? x : a / b; }
42
- double _ZIDZ(double a, double b) {
43
- if (fabs(b) < _epsilon) {
44
- return 0.0;
45
- } else {
46
- return a / b;
47
- }
48
- }
49
23
 
50
24
  //
51
25
  // Lookups
package/src/c/vensim.h CHANGED
@@ -10,6 +10,14 @@ extern "C" {
10
10
  #define _NA_ (-DBL_MAX)
11
11
  #define bool_cond(cond) ((double)(cond) != 0.0)
12
12
 
13
+ // The tolerance used by the functions that guard against division by zero.
14
+ #define SDE_EPSILON 1e-6
15
+
16
+ // Note: The `_time` and `_time_step` variables are declared in `sde.h`, but `sde.h` includes
17
+ // this header, so we declare them here for use by the inline functions below.
18
+ extern double _time;
19
+ extern double _time_step;
20
+
13
21
  //
14
22
  // Vensim functions
15
23
  // See the Vensim Reference Manual for descriptions of the functions.
@@ -44,13 +52,49 @@ double* _DEMAND_AT_PRICE(double* demand_quantities, double* demand_profiles, dou
44
52
  double _FIND_MARKET_PRICE(double* demand_quantities, double* demand_profiles, double* supply_quantities,
45
53
  double* supply_profiles, size_t num_demanders, size_t num_suppliers);
46
54
  double* _INVERT_MATRIX(double* matrix, size_t n);
47
- double _PULSE(double start, double width);
48
55
  double _PULSE_TRAIN(double start, double width, double interval, double end);
49
- double _RAMP(double slope, double start_time, double end_time);
50
56
  double* _SUPPLY_AT_PRICE(double* supply_quantities, double* supply_profiles, double price, size_t num_suppliers);
51
57
  double* _VECTOR_SORT_ORDER(double* vector, size_t size, double direction);
52
- double _XIDZ(double a, double b, double x);
53
- double _ZIDZ(double a, double b);
58
+
59
+ //
60
+ // Note: The following functions are defined here as `static inline` (rather than as
61
+ // out-of-line functions in `vensim.c`) because they are called very frequently (millions of
62
+ // times per run for a large model). Defining them in the header allows the compiler to
63
+ // inline them at each call site, which avoids the call overhead and allows repeated
64
+ // identical calls to be replaced with a single one.
65
+ //
66
+
67
+ static inline double _PULSE(double start, double width) {
68
+ double time_plus = _time + _time_step / 2.0;
69
+ if (width == 0.0) {
70
+ width = _time_step;
71
+ }
72
+ return (time_plus > start && time_plus < start + width) ? 1.0 : 0.0;
73
+ }
74
+
75
+ static inline double _RAMP(double slope, double start_time, double end_time) {
76
+ // Return 0 until the start time is exceeded.
77
+ // Interpolate from start time to end time.
78
+ // Hold at the end time value.
79
+ // Allow start time > end time.
80
+ if (_time > start_time) {
81
+ if (_time < end_time || start_time > end_time) {
82
+ return slope * (_time - start_time);
83
+ } else {
84
+ return slope * (end_time - start_time);
85
+ }
86
+ } else {
87
+ return 0.0;
88
+ }
89
+ }
90
+
91
+ static inline double _XIDZ(double a, double b, double x) {
92
+ return fabs(b) < SDE_EPSILON ? x : a / b;
93
+ }
94
+
95
+ static inline double _ZIDZ(double a, double b) {
96
+ return fabs(b) < SDE_EPSILON ? 0.0 : a / b;
97
+ }
54
98
 
55
99
  //
56
100
  // Lookups