@sdeverywhere/cli 0.7.0

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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2016-2022 Todd Fincannon and Climate Interactive / New Venture Fund
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,14 @@
1
+ # @sdeverywhere/cli
2
+
3
+ This package contains the `sde` command line interface for the SDEverywhere
4
+ suite of tools.
5
+
6
+ SDEverywhere can be used to translate System Dynamics models from Vensim to C and WebAssembly.
7
+
8
+ ## Documentation
9
+
10
+ TODO
11
+
12
+ ## License
13
+
14
+ SDEverywhere is distributed under the MIT license. See `LICENSE` for more details.
package/package.json ADDED
@@ -0,0 +1,41 @@
1
+ {
2
+ "name": "@sdeverywhere/cli",
3
+ "version": "0.7.0",
4
+ "description": "Contains the `sde` command line interface for the SDEverywhere tool suite.",
5
+ "type": "module",
6
+ "files": [
7
+ "src/**",
8
+ "!.DS_Store"
9
+ ],
10
+ "bin": {
11
+ "sde": "src/main.js"
12
+ },
13
+ "dependencies": {
14
+ "@sdeverywhere/build": "^0.1.0",
15
+ "@sdeverywhere/compile": "^0.7.0",
16
+ "bufx": "^1.0.5",
17
+ "byline": "^5.0.0",
18
+ "fs-extra": "^10.1.0",
19
+ "ramda": "^0.27.0",
20
+ "shelljs": "^0.8.3",
21
+ "yargs": "^17.5.1"
22
+ },
23
+ "author": "Climate Interactive",
24
+ "license": "MIT",
25
+ "homepage": "https://sdeverywhere.org",
26
+ "repository": {
27
+ "type": "git",
28
+ "url": "https://github.com/climateinteractive/SDEverywhere.git",
29
+ "directory": "packages/cli"
30
+ },
31
+ "bugs": {
32
+ "url": "https://github.com/climateinteractive/SDEverywhere/issues"
33
+ },
34
+ "scripts": {
35
+ "lint": "eslint . --max-warnings 0",
36
+ "prettier:check": "prettier --check .",
37
+ "prettier:fix": "prettier --write .",
38
+ "precommit": "../../scripts/precommit",
39
+ "ci:build": "run-s lint prettier:check"
40
+ }
41
+ }
package/src/c/macros.c ADDED
@@ -0,0 +1,4 @@
1
+ #include "sde.h"
2
+
3
+ extern double _time;
4
+ extern double _time_step;
package/src/c/macros.h ADDED
File without changes
package/src/c/main.c ADDED
@@ -0,0 +1,58 @@
1
+ #include "sde.h"
2
+
3
+ int main(int argc, char** argv) {
4
+ // TODO make the input buffer size dynamic
5
+ char inputs[1500];
6
+ // When true, output data without newlines or a header, suitable for embedding reference data.
7
+ bool raw_output = false;
8
+ // When true, suppress data output when using PR* macros.
9
+ bool suppress_data_output = false;
10
+ // Try to read input from a file named in the argument.
11
+ if (argc > 1) {
12
+ FILE* instream = fopen(argv[1], "r");
13
+ if (instream && fgets(inputs, sizeof inputs, instream) != NULL) {
14
+ fclose(instream);
15
+ size_t len = strlen(inputs);
16
+ if (inputs[len - 1] == '\n') {
17
+ inputs[len - 1] = '\0';
18
+ }
19
+ }
20
+ if (argc > 2) {
21
+ // Set option flags.
22
+ if (strcmp(argv[2], "--raw") == 0) {
23
+ raw_output = true;
24
+ }
25
+ }
26
+ } else {
27
+ *inputs = '\0';
28
+ }
29
+ // Run the model and get output for all time steps.
30
+ char* outputs = run_model(inputs);
31
+ if (!suppress_data_output) {
32
+ if (raw_output) {
33
+ // Write raw output data directly.
34
+ fputs(outputs, stdout);
35
+ } else {
36
+ // Write a header for output data.
37
+ printf("%s\n", getHeader());
38
+ // Write tab-delimited output data, one line per output time step.
39
+ if (outputs != NULL) {
40
+ char* p = outputs;
41
+ while (*p) {
42
+ char* line = p;
43
+ for (size_t i = 0; i < numOutputs; i++) {
44
+ if (i > 0) {
45
+ p++;
46
+ }
47
+ while (*p && *p != '\t') {
48
+ p++;
49
+ }
50
+ }
51
+ *p++ = '\0';
52
+ printf("%s\n", line);
53
+ }
54
+ }
55
+ }
56
+ }
57
+ finish();
58
+ }
package/src/c/makefile ADDED
@@ -0,0 +1,15 @@
1
+ # Do "export P={c-file-basename}" before running this makefile.
2
+ OBJECTS=main.o vensim.o model.o macros.o
3
+ CFLAGS=-Wall -O3
4
+ LDLIBS=
5
+
6
+ # In case of a Linux build we need to add
7
+ # an extra flag, to link with math library
8
+ UNAME_S := $(shell uname -s)
9
+ ifeq ($(UNAME_S),Linux)
10
+ LDLIBS+= -lm
11
+ endif
12
+
13
+ CC=clang
14
+
15
+ $(P): $(OBJECTS) $P.o
package/src/c/model.c ADDED
@@ -0,0 +1,134 @@
1
+ #include <time.h>
2
+
3
+ #include "sde.h"
4
+
5
+ // Define PERF_TEST to output run time information.
6
+ // #define PERF_TEST
7
+ #ifdef PERF_TEST
8
+ struct timespec startTime, finishTime;
9
+ #endif
10
+
11
+ // The special _time variable is not included in .mdl files.
12
+ double _time;
13
+
14
+ // Output data buffer used by `run_model`
15
+ char* outputData = NULL;
16
+ size_t outputIndex = 0;
17
+
18
+ // Output data buffer used by `runModelWithBuffers`
19
+ double* outputBuffer = NULL;
20
+ size_t outputVarIndex = 0;
21
+ size_t numSavePoints = 0;
22
+ size_t savePointIndex = 0;
23
+
24
+ int step = 0;
25
+
26
+ char* run_model(const char* inputs) {
27
+ // run_model does everything necessary to run the model with the given inputs.
28
+ // It may be called multiple times. Call finish() after all runs are complete.
29
+ // Initialize the state to default values in the model at the start of each run.
30
+ initConstants();
31
+ // Set inputs for this run that override default values.
32
+ // fprintf(stderr, "run_model inputs = %s\n", inputs);
33
+ setInputs(inputs);
34
+ initLevels();
35
+ run();
36
+ return outputData;
37
+ }
38
+
39
+ /**
40
+ * Run the model, reading inputs from the given `inputs` buffer, and writing outputs
41
+ * to the given `outputs` buffer.
42
+ *
43
+ * This function performs the same steps as the original `run_model` function,
44
+ * except that it uses the provided pre-allocated buffers.
45
+ *
46
+ * The `inputs` buffer is assumed to have one double value for each input variable;
47
+ * they must be in exactly the same order as the variables are listed in the spec file.
48
+ *
49
+ * After each step of the run, the `outputs` buffer will be updated with the output
50
+ * variables. The buffer needs to be at least as large as:
51
+ * `number of output variables` * `number of save points`
52
+ * where `number of save points` is typically one point for each year inclusive of
53
+ * the start and end times.
54
+ *
55
+ * The outputs will be stored in the same order as the outputs are defined in the
56
+ * spec file, with one "row" for each variable. For example, the first value in
57
+ * the buffer will be the output value at t0 for the first output variable, followed
58
+ * by the output value for that variable at t1, and so on. After the value for tN
59
+ * (where tN is the last time in the range), the second variable outputs will begin,
60
+ * and so on.
61
+ */
62
+ void runModelWithBuffers(double* inputs, double* outputs) {
63
+ outputBuffer = outputs;
64
+ initConstants();
65
+ setInputsFromBuffer(inputs);
66
+ initLevels();
67
+ run();
68
+ outputBuffer = NULL;
69
+ }
70
+
71
+ void run() {
72
+ #ifdef PERF_TEST
73
+ clock_gettime(CLOCK_MONOTONIC, &startTime);
74
+ #endif
75
+
76
+ // Restart fresh output for all steps in this run.
77
+ numSavePoints = (size_t)(round((_final_time - _initial_time) / _saveper)) + 1;
78
+ savePointIndex = 0;
79
+ outputIndex = 0;
80
+
81
+ // Initialize time with the required INITIAL TIME control variable.
82
+ _time = _initial_time;
83
+
84
+ // Set up a run loop using a fixed number of time steps.
85
+ int lastStep = (int)(round((_final_time - _initial_time) / _time_step));
86
+ step = 0;
87
+ while (step <= lastStep) {
88
+ evalAux();
89
+ if (fmod(_time, _saveper) < 1e-6) {
90
+ outputVarIndex = 0;
91
+ storeOutputData();
92
+ savePointIndex++;
93
+ }
94
+ if (step == lastStep) break;
95
+ // Propagate levels for the next time step.
96
+ evalLevels();
97
+ _time += _time_step;
98
+ step++;
99
+ }
100
+ }
101
+
102
+ void outputVar(double value) {
103
+ if (outputBuffer != NULL) {
104
+ // Write each value into the preallocated buffer; each variable has a "row" that
105
+ // contains `numSavePoints` values, one value for each save point
106
+ double* outputPtr = outputBuffer + (outputVarIndex * numSavePoints) + savePointIndex;
107
+ *outputPtr = value;
108
+ outputVarIndex++;
109
+ } else {
110
+ // Allocate an output buffer for all output steps as a single block.
111
+ // Add one character for a null terminator.
112
+ if (outputData == NULL) {
113
+ int numOutputSteps = (int)(round((_final_time - _initial_time) / _saveper)) + 1;
114
+ size_t size = numOutputSteps * (OUTPUT_STRING_LEN * numOutputs) + 1;
115
+ // fprintf(stderr, "output data size = %zu\n", size);
116
+ outputData = (char*)malloc(size);
117
+ }
118
+ // Format the value as a string in the output data buffer.
119
+ int numChars = snprintf(outputData + outputIndex, OUTPUT_STRING_LEN + 1, "%g\t", value);
120
+ outputIndex += numChars;
121
+ }
122
+ }
123
+
124
+ void finish() {
125
+ #ifdef PERF_TEST
126
+ clock_gettime(CLOCK_MONOTONIC, &finishTime);
127
+ double runtime =
128
+ 1000.0 * finishTime.tv_sec + 1e-6 * finishTime.tv_nsec - (1000.0 * startTime.tv_sec + 1e-6 * startTime.tv_nsec);
129
+ fprintf(stderr, "calculation runtime = %.0f ms\n", runtime);
130
+ #endif
131
+ if (outputData != NULL) {
132
+ free(outputData);
133
+ }
134
+ }
package/src/c/model.h ADDED
@@ -0,0 +1,12 @@
1
+ #pragma once
2
+
3
+ #ifdef __cplusplus
4
+ extern "C" {
5
+ #endif
6
+
7
+ double roundToSignificantFigures(double num, int n);
8
+ double print6(double num);
9
+
10
+ #ifdef __cplusplus
11
+ }
12
+ #endif
package/src/c/sde.h ADDED
@@ -0,0 +1,73 @@
1
+ #pragma once
2
+
3
+ #ifndef EXTERN
4
+ #ifdef __cplusplus
5
+ #define EXTERN extern "C"
6
+ #else
7
+ #define EXTERN extern
8
+ #endif
9
+ #endif
10
+
11
+ #ifdef __cplusplus
12
+ extern "C" {
13
+ #endif
14
+
15
+ #include <math.h>
16
+ #include <stdlib.h>
17
+ #include <stdarg.h>
18
+ #include <stdbool.h>
19
+ #include <float.h>
20
+ #include <string.h>
21
+ #include <ctype.h>
22
+ #include <stdio.h>
23
+
24
+ #include "model.h"
25
+ #include "vensim.h"
26
+ #include "macros.h"
27
+
28
+ EXTERN double _epsilon;
29
+
30
+ // Enable this to add print statements in initLevels and evalAux for debugging.
31
+ // #define PRDBG
32
+ #ifdef PRDBG
33
+ #define PRINIT(v) printf("initLevels: " #v " = %g\n", (v));
34
+ #define PRAUX(v, t) if (_time == t) { printf("evalAux: " #v " = %g\n", (v)); }
35
+ #else
36
+ #define PRINIT(v)
37
+ #define PRAUX(v, t)
38
+ #endif
39
+
40
+ // Each number in the output can take up to 13 characters plus a separator character.
41
+ #define OUTPUT_STRING_LEN 14
42
+
43
+ // Internal variables
44
+ EXTERN const int numOutputs;
45
+
46
+ // Standard simulation control parameters
47
+ EXTERN double _time;
48
+ EXTERN double _initial_time;
49
+ EXTERN double _final_time;
50
+ EXTERN double _time_step;
51
+ EXTERN double _saveper;
52
+
53
+ // API
54
+ char* run_model(const char* inputs);
55
+ void runModelWithBuffers(double* inputs, double* outputs);
56
+ void run(void);
57
+ void startOutput(void);
58
+ void outputVar(double value);
59
+ void finish(void);
60
+
61
+ // Functions implemented by the model
62
+ void initConstants(void);
63
+ void initLevels(void);
64
+ void setInputs(const char* inputData);
65
+ void setInputsFromBuffer(double *inputData);
66
+ void evalAux(void);
67
+ void evalLevels(void);
68
+ void storeOutputData(void);
69
+ const char* getHeader(void);
70
+
71
+ #ifdef __cplusplus
72
+ }
73
+ #endif