@sdeverywhere/cli 0.7.37 → 0.7.39

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sdeverywhere/cli",
3
- "version": "0.7.37",
3
+ "version": "0.7.39",
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.8",
15
- "@sdeverywhere/compile": "^0.7.26",
14
+ "@sdeverywhere/build": "^0.3.10",
15
+ "@sdeverywhere/compile": "^0.7.27",
16
16
  "byline": "^5.0.0",
17
17
  "ramda": "^0.27.0",
18
18
  "shelljs": "^0.10.0",
package/src/c/main.c CHANGED
@@ -1,8 +1,70 @@
1
1
  #include "sde.h"
2
2
 
3
+ /**
4
+ * Count the number of input pairs in the input string in the format
5
+ * "varIndex:value varIndex:value ..." (for example, "0:3.14 6:42").
6
+ */
7
+ static size_t countInputs(const char* inputData) {
8
+ if (inputData == NULL || *inputData == '\0') {
9
+ return 0;
10
+ }
11
+
12
+ // Make a copy since strtok modifies the string
13
+ char* inputsCopy = (char*)malloc(strlen(inputData) + 1);
14
+ strcpy(inputsCopy, inputData);
15
+
16
+ size_t count = 0;
17
+ char* token = strtok(inputsCopy, " ");
18
+ while (token) {
19
+ if (strchr(token, ':') != NULL) {
20
+ count++;
21
+ }
22
+ token = strtok(NULL, " ");
23
+ }
24
+ free(inputsCopy);
25
+
26
+ return count;
27
+ }
28
+
29
+ /**
30
+ * Parse an input data string in the format "varIndex:value varIndex:value ..."
31
+ * (for example, "0:3.14 6:42") and populate the inputValues and inputIndices
32
+ * arrays for sparse input setting.
33
+ *
34
+ * @param inputData The input string to parse.
35
+ * @param inputValues The array to populate with input values.
36
+ * @param inputIndices The array to populate with input indices (first element is count).
37
+ */
38
+ static void parseInputs(const char* inputData, double* inputValues, int32_t* inputIndices) {
39
+ if (inputData == NULL || *inputData == '\0') {
40
+ return;
41
+ }
42
+
43
+ // Make a copy since strtok modifies the string
44
+ char* inputsCopy = (char*)malloc(strlen(inputData) + 1);
45
+ strcpy(inputsCopy, inputData);
46
+
47
+ size_t i = 0;
48
+ char* token = strtok(inputsCopy, " ");
49
+ while (token) {
50
+ char* p = strchr(token, ':');
51
+ if (p) {
52
+ *p = '\0';
53
+ int modelVarIndex = atoi(token);
54
+ double value = atof(p + 1);
55
+ inputIndices[i + 1] = modelVarIndex;
56
+ inputValues[i] = value;
57
+ i++;
58
+ }
59
+ token = strtok(NULL, " ");
60
+ }
61
+ inputIndices[0] = (int32_t)i;
62
+ free(inputsCopy);
63
+ }
64
+
3
65
  int main(int argc, char** argv) {
4
66
  // TODO make the input buffer size dynamic
5
- char inputs[500000];
67
+ char inputString[500000];
6
68
  // When true, output data without newlines or a header, suitable for embedding reference data.
7
69
  bool raw_output = false;
8
70
  // When true, suppress data output when using PR* macros.
@@ -10,11 +72,11 @@ int main(int argc, char** argv) {
10
72
  // Try to read input from a file named in the argument.
11
73
  if (argc > 1) {
12
74
  FILE* instream = fopen(argv[1], "r");
13
- if (instream && fgets(inputs, sizeof inputs, instream) != NULL) {
75
+ if (instream && fgets(inputString, sizeof inputString, instream) != NULL) {
14
76
  fclose(instream);
15
- size_t len = strlen(inputs);
16
- if (inputs[len - 1] == '\n') {
17
- inputs[len - 1] = '\0';
77
+ size_t len = strlen(inputString);
78
+ if (inputString[len - 1] == '\n') {
79
+ inputString[len - 1] = '\0';
18
80
  }
19
81
  }
20
82
  if (argc > 2) {
@@ -24,35 +86,68 @@ int main(int argc, char** argv) {
24
86
  }
25
87
  }
26
88
  } else {
27
- *inputs = '\0';
89
+ *inputString = '\0';
90
+ }
91
+
92
+ // Parse input string and create sparse input arrays. Only allocate buffers if there
93
+ // are inputs to parse; otherwise pass NULL to runModelWithBuffers so that the model
94
+ // uses its default values from initConstants.
95
+ double* inputValues = NULL;
96
+ int32_t* inputIndices = NULL;
97
+ size_t inputCount = countInputs(inputString);
98
+ if (inputCount > 0) {
99
+ inputValues = (double*)malloc(inputCount * sizeof(double));
100
+ inputIndices = (int32_t*)malloc((inputCount + 1) * sizeof(int32_t));
101
+ parseInputs(inputString, inputValues, inputIndices);
28
102
  }
29
- // Run the model and get output for all time steps.
30
- char* outputs = run_model(inputs);
103
+
104
+ // Calculate the number of save points for the output buffer
105
+ double initialTime = getInitialTime();
106
+ double finalTime = getFinalTime();
107
+ double saveper = getSaveper();
108
+ size_t numSavePoints = (size_t)(round((finalTime - initialTime) / saveper)) + 1;
109
+
110
+ // Allocate output buffer
111
+ double* outputBuffer = (double*)malloc(numOutputs * numSavePoints * sizeof(double));
112
+
113
+ // Run the model with the sparse input arrays and output buffer
114
+ runModelWithBuffers(inputValues, inputIndices, outputBuffer, NULL, NULL, NULL);
115
+
31
116
  if (!suppress_data_output) {
32
117
  if (raw_output) {
33
- // Write raw output data directly.
34
- fputs(outputs, stdout);
118
+ // Write raw output data directly (tab-separated, no newlines)
119
+ for (size_t t = 0; t < numSavePoints; t++) {
120
+ for (size_t v = 0; v < numOutputs; v++) {
121
+ // Output buffer is organized by variable (each variable has numSavePoints values)
122
+ double value = outputBuffer[v * numSavePoints + t];
123
+ printf("%g\t", value);
124
+ }
125
+ }
35
126
  } else {
36
127
  // Write a header for output data.
37
128
  printf("%s\n", getHeader());
38
129
  // 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
- }
130
+ for (size_t t = 0; t < numSavePoints; t++) {
131
+ for (size_t v = 0; v < numOutputs; v++) {
132
+ // Output buffer is organized by variable (each variable has numSavePoints values)
133
+ double value = outputBuffer[v * numSavePoints + t];
134
+ if (v > 0) {
135
+ printf("\t");
50
136
  }
51
- *p++ = '\0';
52
- printf("%s\n", line);
137
+ printf("%g", value);
53
138
  }
139
+ printf("\n");
54
140
  }
55
141
  }
56
142
  }
143
+
144
+ // Clean up
145
+ if (inputValues != NULL) {
146
+ free(inputValues);
147
+ }
148
+ if (inputIndices != NULL) {
149
+ free(inputIndices);
150
+ }
151
+ free(outputBuffer);
57
152
  finish();
58
153
  }
package/src/c/model.c CHANGED
@@ -11,11 +11,7 @@ struct timespec startTime, finishTime;
11
11
  // The special _time variable is not included in .mdl files.
12
12
  double _time;
13
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`
14
+ // Output data buffer and parameters used by `runModelWithBuffers`
19
15
  double* outputBuffer = NULL;
20
16
  int32_t* outputIndexBuffer = NULL;
21
17
  size_t outputVarIndex = 0;
@@ -67,47 +63,157 @@ double getSaveper() {
67
63
  return _saveper;
68
64
  }
69
65
 
70
- char* run_model(const char* inputs) {
71
- // run_model does everything necessary to run the model with the given inputs.
72
- // It may be called multiple times. Call finish() after all runs are complete.
73
- // Initialize the state to default values in the model at the start of each run.
74
- initConstants();
75
- // Set inputs for this run that override default values.
76
- // fprintf(stderr, "run_model inputs = %s\n", inputs);
77
- setInputs(inputs);
78
- initLevels();
79
- run();
80
- return outputData;
66
+ /**
67
+ * Set constant overrides from the given buffers.
68
+ *
69
+ * The `constantIndices` buffer contains the variable indices and subscript indices
70
+ * for each constant to override. The format is:
71
+ * [count, varIndex1, subCount1, subIndex1_1, ..., varIndex2, subCount2, ...]
72
+ *
73
+ * The `constantValues` buffer contains the corresponding values for each constant.
74
+ */
75
+ void setConstantOverridesFromBuffers(double* constantValues, int32_t* constantIndices) {
76
+ if (constantValues == NULL || constantIndices == NULL) {
77
+ return;
78
+ }
79
+
80
+ size_t indexBufferOffset = 0;
81
+ size_t valueBufferOffset = 0;
82
+ size_t constantCount = (size_t)constantIndices[indexBufferOffset++];
83
+
84
+ for (size_t i = 0; i < constantCount; i++) {
85
+ size_t varIndex = (size_t)constantIndices[indexBufferOffset++];
86
+ size_t subCount = (size_t)constantIndices[indexBufferOffset++];
87
+ size_t* subIndices;
88
+ if (subCount > 0) {
89
+ subIndices = (size_t*)(constantIndices + indexBufferOffset);
90
+ indexBufferOffset += subCount;
91
+ } else {
92
+ subIndices = NULL;
93
+ }
94
+ double value = constantValues[valueBufferOffset++];
95
+ setConstant(varIndex, subIndices, value);
96
+ }
81
97
  }
82
98
 
83
99
  /**
84
100
  * Run the model, reading inputs from the given `inputs` buffer, and writing outputs
85
101
  * to the given `outputs` buffer.
86
102
  *
87
- * This function performs the same steps as the original `run_model` function,
88
- * except that it uses the provided pre-allocated buffers.
89
- *
90
- * The `inputs` buffer is assumed to have one double value for each input variable;
91
- * they must be in exactly the same order as the variables are listed in the spec file.
103
+ * This is a simplified version of `runModelWithBuffers` that passes NULL for
104
+ * all parameters other than `inputs` and `outputs`.
92
105
  *
93
106
  * After each step of the run, the `outputs` buffer will be updated with the output
94
- * variables. The buffer needs to be at least as large as:
107
+ * variables. The `outputs` buffer needs to be at least as large as:
95
108
  * `number of output variables` * `number of save points`
96
- * where `number of save points` is typically one point for each year inclusive of
97
- * the start and end times.
98
109
  *
99
110
  * The outputs will be stored in the same order as the outputs are defined in the
100
111
  * spec file, with one "row" for each variable. For example, the first value in
101
- * the buffer will be the output value at t0 for the first output variable, followed
102
- * by the output value for that variable at t1, and so on. After the value for tN
103
- * (where tN is the last time in the range), the second variable outputs will begin,
104
- * and so on.
112
+ * the buffer will be the output value at t0 for the first output variable,
113
+ * followed by the output value for that variable at t1, and so on. After the
114
+ * value for tN (where tN is the last time in the range), the second variable
115
+ * outputs will begin, and so on.
116
+ *
117
+ * @param inputs The buffer that contains the model input values. If NULL,
118
+ * no inputs will be set and the model will use the default values for all
119
+ * constants as defined in the generated model. If non-NULL, the buffer is
120
+ * assumed to have one double value for each input variable in exactly the
121
+ * same order that the variables are listed in the spec file.
122
+ * @param outputs The required buffer that will receive the model output
123
+ * values. See above for details on the expected format.
124
+ */
125
+ void runModel(double* inputs, double* outputs) {
126
+ runModelWithBuffers(inputs, NULL, outputs, NULL, NULL, NULL);
127
+ }
128
+
129
+ /**
130
+ * Run the model, reading inputs from the given `inputs` buffer, and writing outputs
131
+ * to the given `outputs` buffer.
132
+ *
133
+ * INPUTS
134
+ * ------
135
+ *
136
+ * If `inputIndices` is NULL, the `inputs` buffer is assumed to have one double value
137
+ * for each input variable, in exactly the same order as the variables are listed in
138
+ * the spec file.
139
+ *
140
+ * If `inputIndices` is non-NULL, it specifies which inputs are being set:
141
+ * - inputIndices[0] is the count (C) of inputs being specified
142
+ * - inputIndices[1...C] are the indices of the inputs to set (where each index
143
+ * corresponds to the index of the input variable in the spec.json file)
144
+ * - inputs[0...C-1] are the corresponding values
145
+ *
146
+ * OUTPUTS
147
+ * -------
148
+ *
149
+ * After each step of the run, the `outputs` buffer will be updated with the output
150
+ * variables. The `outputs` buffer needs to be at least as large as:
151
+ * `number of output variables` * `number of save points`
152
+ *
153
+ * If `outputIndices` is NULL, outputs will be stored in the same order as the outputs
154
+ * are defined in the spec file, with one "row" for each variable. For example, the
155
+ * first value in the buffer will be the output value at t0 for the first output
156
+ * variable, followed by the output value for that variable at t1, and so on. After
157
+ * the value for tN (where tN is the last time in the range), the second variable
158
+ * outputs will begin, and so on.
159
+ *
160
+ * If `outputIndices` is non-NULL, it specifies which outputs are being stored:
161
+ * - outputIndices[0] is the count (C) of output variables being stored
162
+ * - outputIndices[1...] are the indices of the output variables to store, in
163
+ * the following format:
164
+ * [count, varIndex1, subCount1, subIndex1_1, ..., varIndex2, subCount2, ...]
165
+ * where `count` is the number of variables to store, `varIndexN` is the index
166
+ * of the variable to store (from the {model}.json listing file), `subCountN` is
167
+ * the number of subscripts for that variable, and `subIndexN_M` is the index of
168
+ * the subscript at the Mth position for that variable
169
+ * - outputs[0...C-1] are the corresponding values
170
+ *
171
+ * CONSTANT OVERRIDES
172
+ * ------------------
173
+ *
174
+ * If `constants` and `constantIndices` are non-NULL, the provided constant values will
175
+ * override the default values for those constants as defined in the generated model.
176
+ *
177
+ * The `constantIndices` buffer specifies which constants are being overridden. The
178
+ * format is the same as described above for `outputIndices`:
179
+ * - constantIndices[0] is the count (C) of constants being overridden
180
+ * - constantIndices[1...] are the indices of the constants to override, in the
181
+ * following format:
182
+ * [count, varIndex1, subCount1, subIndex1_1, ..., varIndex2, subCount2, ...]
183
+ * where `count` is the number of constants to override, `varIndexN` is the index
184
+ * of the variable to store (from the {model}.json listing file), `subCountN` is
185
+ * the number of subscripts for that variable, and `subIndexN_M` is the index of
186
+ * the subscript at the Mth position for that variable
187
+ * - constants[0...C-1] are the corresponding values
188
+ *
189
+ * @param inputs The buffer that contains the model input values. If NULL, no inputs
190
+ * will be set and the model will use the default values for all constants as defined
191
+ * in the generated model. If non-NULL, the buffer is assumed to have one double value
192
+ * for each input variable. The number of values provided depends on `inputIndices`;
193
+ * see above for details on the expected format of these two parameters.
194
+ * @param inputIndices The optional buffer that specifies which input values from the
195
+ * `inputs` buffer are being set. See above for details on the expected format.
196
+ * @param outputs The required buffer that will receive the model output values. See
197
+ * above for details on the expected format.
198
+ * @param outputIndices The optional buffer that specifies which output values will be
199
+ * stored in the `outputs` buffer. See above for details on the expected format.
200
+ * @param constants An optional buffer that contains the values of the constants to
201
+ * override. Pass NULL if not overriding any constants. Each value in the buffer
202
+ * corresponds to the value of the constant at the corresponding index.
203
+ * @param constantIndices An optional buffer that contains the indices of the constants
204
+ * to override. Pass NULL if not overriding any constants. See above for details on
205
+ * the expected format.
105
206
  */
106
- void runModelWithBuffers(double* inputs, double* outputs, int32_t* outputIndices) {
207
+ void runModelWithBuffers(double* inputs, int32_t* inputIndices, double* outputs, int32_t* outputIndices, double* constants, int32_t* constantIndices) {
107
208
  outputBuffer = outputs;
108
209
  outputIndexBuffer = outputIndices;
109
210
  initConstants();
110
- setInputsFromBuffer(inputs);
211
+ if (constants != NULL && constantIndices != NULL) {
212
+ setConstantOverridesFromBuffers(constants, constantIndices);
213
+ }
214
+ if (inputs != NULL) {
215
+ setInputs(inputs, inputIndices);
216
+ }
111
217
  initLevels();
112
218
  run();
113
219
  outputBuffer = NULL;
@@ -121,7 +227,6 @@ void run() {
121
227
 
122
228
  // Restart fresh output for all steps in this run.
123
229
  savePointIndex = 0;
124
- outputIndex = 0;
125
230
 
126
231
  // Initialize time with the required INITIAL TIME control variable.
127
232
  _time = _initial_time;
@@ -171,25 +276,11 @@ void run() {
171
276
  }
172
277
 
173
278
  void outputVar(double value) {
174
- if (outputBuffer != NULL) {
175
- // Write each value into the preallocated buffer; each variable has a "row" that
176
- // contains `numSavePoints` values, one value for each save point
177
- double* outputPtr = outputBuffer + (outputVarIndex * numSavePoints) + savePointIndex;
178
- *outputPtr = value;
179
- outputVarIndex++;
180
- } else {
181
- // Allocate an output buffer for all output steps as a single block.
182
- // Add one character for a null terminator.
183
- if (outputData == NULL) {
184
- int numOutputSteps = (int)(round((_final_time - _initial_time) / _saveper)) + 1;
185
- size_t size = numOutputSteps * (OUTPUT_STRING_LEN * numOutputs) + 1;
186
- // fprintf(stderr, "output data size = %zu\n", size);
187
- outputData = (char*)malloc(size);
188
- }
189
- // Format the value as a string in the output data buffer.
190
- int numChars = snprintf(outputData + outputIndex, OUTPUT_STRING_LEN + 1, "%g\t", value);
191
- outputIndex += numChars;
192
- }
279
+ // Write each value into the preallocated buffer; each variable has a "row" that
280
+ // contains `numSavePoints` values, one value for each save point
281
+ double* outputPtr = outputBuffer + (outputVarIndex * numSavePoints) + savePointIndex;
282
+ *outputPtr = value;
283
+ outputVarIndex++;
193
284
  }
194
285
 
195
286
  void finish() {
@@ -199,7 +290,4 @@ void finish() {
199
290
  1000.0 * finishTime.tv_sec + 1e-6 * finishTime.tv_nsec - (1000.0 * startTime.tv_sec + 1e-6 * startTime.tv_nsec);
200
291
  fprintf(stderr, "calculation runtime = %.0f ms\n", runtime);
201
292
  #endif
202
- if (outputData != NULL) {
203
- free(outputData);
204
- }
205
293
  }
package/src/c/sde.h CHANGED
@@ -41,6 +41,7 @@ EXTERN double _epsilon;
41
41
  #define OUTPUT_STRING_LEN 14
42
42
 
43
43
  // Internal variables
44
+ EXTERN const int numInputs;
44
45
  EXTERN const int numOutputs;
45
46
 
46
47
  // Standard simulation control parameters
@@ -54,8 +55,8 @@ EXTERN double _saveper;
54
55
  double getInitialTime(void);
55
56
  double getFinalTime(void);
56
57
  double getSaveper(void);
57
- char* run_model(const char* inputs);
58
- void runModelWithBuffers(double* inputs, double* outputs, int32_t* outputIndices);
58
+ void runModel(double* inputs, double* outputs);
59
+ void runModelWithBuffers(double* inputs, int32_t* inputIndices, double* outputs, int32_t* outputIndices, double* constants, int32_t* constantIndices);
59
60
  void run(void);
60
61
  void startOutput(void);
61
62
  void outputVar(double value);
@@ -64,8 +65,8 @@ void finish(void);
64
65
  // Functions implemented by the generated model
65
66
  void initConstants(void);
66
67
  void initLevels(void);
67
- void setInputs(const char* inputData);
68
- void setInputsFromBuffer(double *inputData);
68
+ void setInputs(double* inputValues, int32_t* inputIndices);
69
+ void setConstant(size_t varIndex, size_t* subIndices, double value);
69
70
  void setLookup(size_t varIndex, size_t* subIndices, double* points, size_t numPoints);
70
71
  void evalAux(void);
71
72
  void evalLevels(void);