epyt-flow 0.1.0__py3-none-any.whl

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.
Files changed (131) hide show
  1. epyt_flow/EPANET/EPANET/SRC_engines/AUTHORS +28 -0
  2. epyt_flow/EPANET/EPANET/SRC_engines/LICENSE +21 -0
  3. epyt_flow/EPANET/EPANET/SRC_engines/Readme_SRC_Engines.txt +18 -0
  4. epyt_flow/EPANET/EPANET/SRC_engines/enumstxt.h +134 -0
  5. epyt_flow/EPANET/EPANET/SRC_engines/epanet.c +5578 -0
  6. epyt_flow/EPANET/EPANET/SRC_engines/epanet2.c +865 -0
  7. epyt_flow/EPANET/EPANET/SRC_engines/epanet2.def +131 -0
  8. epyt_flow/EPANET/EPANET/SRC_engines/errors.dat +73 -0
  9. epyt_flow/EPANET/EPANET/SRC_engines/funcs.h +193 -0
  10. epyt_flow/EPANET/EPANET/SRC_engines/genmmd.c +1000 -0
  11. epyt_flow/EPANET/EPANET/SRC_engines/hash.c +177 -0
  12. epyt_flow/EPANET/EPANET/SRC_engines/hash.h +28 -0
  13. epyt_flow/EPANET/EPANET/SRC_engines/hydcoeffs.c +1151 -0
  14. epyt_flow/EPANET/EPANET/SRC_engines/hydraul.c +1117 -0
  15. epyt_flow/EPANET/EPANET/SRC_engines/hydsolver.c +720 -0
  16. epyt_flow/EPANET/EPANET/SRC_engines/hydstatus.c +476 -0
  17. epyt_flow/EPANET/EPANET/SRC_engines/include/epanet2.h +431 -0
  18. epyt_flow/EPANET/EPANET/SRC_engines/include/epanet2_2.h +1786 -0
  19. epyt_flow/EPANET/EPANET/SRC_engines/include/epanet2_enums.h +468 -0
  20. epyt_flow/EPANET/EPANET/SRC_engines/inpfile.c +810 -0
  21. epyt_flow/EPANET/EPANET/SRC_engines/input1.c +707 -0
  22. epyt_flow/EPANET/EPANET/SRC_engines/input2.c +864 -0
  23. epyt_flow/EPANET/EPANET/SRC_engines/input3.c +2170 -0
  24. epyt_flow/EPANET/EPANET/SRC_engines/main.c +93 -0
  25. epyt_flow/EPANET/EPANET/SRC_engines/mempool.c +142 -0
  26. epyt_flow/EPANET/EPANET/SRC_engines/mempool.h +24 -0
  27. epyt_flow/EPANET/EPANET/SRC_engines/output.c +852 -0
  28. epyt_flow/EPANET/EPANET/SRC_engines/project.c +1359 -0
  29. epyt_flow/EPANET/EPANET/SRC_engines/quality.c +685 -0
  30. epyt_flow/EPANET/EPANET/SRC_engines/qualreact.c +743 -0
  31. epyt_flow/EPANET/EPANET/SRC_engines/qualroute.c +694 -0
  32. epyt_flow/EPANET/EPANET/SRC_engines/report.c +1489 -0
  33. epyt_flow/EPANET/EPANET/SRC_engines/rules.c +1362 -0
  34. epyt_flow/EPANET/EPANET/SRC_engines/smatrix.c +871 -0
  35. epyt_flow/EPANET/EPANET/SRC_engines/text.h +497 -0
  36. epyt_flow/EPANET/EPANET/SRC_engines/types.h +874 -0
  37. epyt_flow/EPANET/EPANET-MSX/MSX_Updates.txt +53 -0
  38. epyt_flow/EPANET/EPANET-MSX/Src/dispersion.h +27 -0
  39. epyt_flow/EPANET/EPANET-MSX/Src/hash.c +107 -0
  40. epyt_flow/EPANET/EPANET-MSX/Src/hash.h +28 -0
  41. epyt_flow/EPANET/EPANET-MSX/Src/include/epanetmsx.h +102 -0
  42. epyt_flow/EPANET/EPANET-MSX/Src/include/epanetmsx_export.h +42 -0
  43. epyt_flow/EPANET/EPANET-MSX/Src/mathexpr.c +937 -0
  44. epyt_flow/EPANET/EPANET-MSX/Src/mathexpr.h +39 -0
  45. epyt_flow/EPANET/EPANET-MSX/Src/mempool.c +204 -0
  46. epyt_flow/EPANET/EPANET-MSX/Src/mempool.h +24 -0
  47. epyt_flow/EPANET/EPANET-MSX/Src/msxchem.c +1285 -0
  48. epyt_flow/EPANET/EPANET-MSX/Src/msxcompiler.c +368 -0
  49. epyt_flow/EPANET/EPANET-MSX/Src/msxdict.h +42 -0
  50. epyt_flow/EPANET/EPANET-MSX/Src/msxdispersion.c +586 -0
  51. epyt_flow/EPANET/EPANET-MSX/Src/msxerr.c +116 -0
  52. epyt_flow/EPANET/EPANET-MSX/Src/msxfile.c +260 -0
  53. epyt_flow/EPANET/EPANET-MSX/Src/msxfuncs.c +175 -0
  54. epyt_flow/EPANET/EPANET-MSX/Src/msxfuncs.h +35 -0
  55. epyt_flow/EPANET/EPANET-MSX/Src/msxinp.c +1504 -0
  56. epyt_flow/EPANET/EPANET-MSX/Src/msxout.c +401 -0
  57. epyt_flow/EPANET/EPANET-MSX/Src/msxproj.c +791 -0
  58. epyt_flow/EPANET/EPANET-MSX/Src/msxqual.c +2010 -0
  59. epyt_flow/EPANET/EPANET-MSX/Src/msxrpt.c +400 -0
  60. epyt_flow/EPANET/EPANET-MSX/Src/msxtank.c +422 -0
  61. epyt_flow/EPANET/EPANET-MSX/Src/msxtoolkit.c +1164 -0
  62. epyt_flow/EPANET/EPANET-MSX/Src/msxtypes.h +551 -0
  63. epyt_flow/EPANET/EPANET-MSX/Src/msxutils.c +524 -0
  64. epyt_flow/EPANET/EPANET-MSX/Src/msxutils.h +56 -0
  65. epyt_flow/EPANET/EPANET-MSX/Src/newton.c +158 -0
  66. epyt_flow/EPANET/EPANET-MSX/Src/newton.h +34 -0
  67. epyt_flow/EPANET/EPANET-MSX/Src/rk5.c +287 -0
  68. epyt_flow/EPANET/EPANET-MSX/Src/rk5.h +39 -0
  69. epyt_flow/EPANET/EPANET-MSX/Src/ros2.c +293 -0
  70. epyt_flow/EPANET/EPANET-MSX/Src/ros2.h +35 -0
  71. epyt_flow/EPANET/EPANET-MSX/Src/smatrix.c +816 -0
  72. epyt_flow/EPANET/EPANET-MSX/Src/smatrix.h +29 -0
  73. epyt_flow/EPANET/EPANET-MSX/readme.txt +14 -0
  74. epyt_flow/EPANET/compile.sh +4 -0
  75. epyt_flow/VERSION +1 -0
  76. epyt_flow/__init__.py +24 -0
  77. epyt_flow/data/__init__.py +0 -0
  78. epyt_flow/data/benchmarks/__init__.py +11 -0
  79. epyt_flow/data/benchmarks/batadal.py +257 -0
  80. epyt_flow/data/benchmarks/batadal_data.py +28 -0
  81. epyt_flow/data/benchmarks/battledim.py +473 -0
  82. epyt_flow/data/benchmarks/battledim_data.py +51 -0
  83. epyt_flow/data/benchmarks/gecco_water_quality.py +267 -0
  84. epyt_flow/data/benchmarks/leakdb.py +592 -0
  85. epyt_flow/data/benchmarks/leakdb_data.py +18923 -0
  86. epyt_flow/data/benchmarks/water_usage.py +123 -0
  87. epyt_flow/data/networks.py +650 -0
  88. epyt_flow/gym/__init__.py +4 -0
  89. epyt_flow/gym/control_gyms.py +47 -0
  90. epyt_flow/gym/scenario_control_env.py +101 -0
  91. epyt_flow/metrics.py +404 -0
  92. epyt_flow/models/__init__.py +2 -0
  93. epyt_flow/models/event_detector.py +31 -0
  94. epyt_flow/models/sensor_interpolation_detector.py +118 -0
  95. epyt_flow/rest_api/__init__.py +4 -0
  96. epyt_flow/rest_api/base_handler.py +70 -0
  97. epyt_flow/rest_api/res_manager.py +95 -0
  98. epyt_flow/rest_api/scada_data_handler.py +476 -0
  99. epyt_flow/rest_api/scenario_handler.py +352 -0
  100. epyt_flow/rest_api/server.py +106 -0
  101. epyt_flow/serialization.py +438 -0
  102. epyt_flow/simulation/__init__.py +5 -0
  103. epyt_flow/simulation/events/__init__.py +6 -0
  104. epyt_flow/simulation/events/actuator_events.py +259 -0
  105. epyt_flow/simulation/events/event.py +81 -0
  106. epyt_flow/simulation/events/leakages.py +404 -0
  107. epyt_flow/simulation/events/sensor_faults.py +267 -0
  108. epyt_flow/simulation/events/sensor_reading_attack.py +185 -0
  109. epyt_flow/simulation/events/sensor_reading_event.py +170 -0
  110. epyt_flow/simulation/events/system_event.py +88 -0
  111. epyt_flow/simulation/parallel_simulation.py +147 -0
  112. epyt_flow/simulation/scada/__init__.py +3 -0
  113. epyt_flow/simulation/scada/advanced_control.py +134 -0
  114. epyt_flow/simulation/scada/scada_data.py +1589 -0
  115. epyt_flow/simulation/scada/scada_data_export.py +255 -0
  116. epyt_flow/simulation/scenario_config.py +608 -0
  117. epyt_flow/simulation/scenario_simulator.py +1897 -0
  118. epyt_flow/simulation/scenario_visualizer.py +61 -0
  119. epyt_flow/simulation/sensor_config.py +1289 -0
  120. epyt_flow/topology.py +290 -0
  121. epyt_flow/uncertainty/__init__.py +3 -0
  122. epyt_flow/uncertainty/model_uncertainty.py +302 -0
  123. epyt_flow/uncertainty/sensor_noise.py +73 -0
  124. epyt_flow/uncertainty/uncertainties.py +555 -0
  125. epyt_flow/uncertainty/utils.py +206 -0
  126. epyt_flow/utils.py +306 -0
  127. epyt_flow-0.1.0.dist-info/LICENSE +21 -0
  128. epyt_flow-0.1.0.dist-info/METADATA +139 -0
  129. epyt_flow-0.1.0.dist-info/RECORD +131 -0
  130. epyt_flow-0.1.0.dist-info/WHEEL +5 -0
  131. epyt_flow-0.1.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,1504 @@
1
+ /*******************************************************************************
2
+ ** MODULE: MSXINP.C
3
+ ** PROJECT: EPANET-MSX
4
+ ** DESCRIPTION: Input data processor for the EPANET Multi-Species Extension
5
+ ** toolkit.
6
+ ** AUTHORS: see AUTHORS
7
+ ** Copyright: see AUTHORS
8
+ ** License: see LICENSE
9
+ ** VERSION: 2.0.00
10
+ ** LAST UPDATE: 08/20/2022
11
+ *******************************************************************************/
12
+
13
+ #include <stdio.h>
14
+ #include <stdlib.h>
15
+ #include <string.h>
16
+ #include <math.h>
17
+
18
+ #include "msxtypes.h"
19
+ #include "msxutils.h"
20
+ #include "msxdict.h"
21
+ #include "epanet2.h"
22
+
23
+ // Constants
24
+ //-----------
25
+ #define MAXERRS 100 // Max. input errors reported
26
+ #define MAXTOKS 40 // Max. items per line of input
27
+ #define SEPSTR " \t\n\r" // Token separator characters
28
+
29
+ // External variables
30
+ //--------------------
31
+ extern MSXproject MSX; // MSX project data
32
+
33
+ // Local variables
34
+ //-----------------
35
+ static char *Tok[MAXTOKS]; // String tokens from line of input
36
+ static int Ntokens; // Number of tokens in line of input
37
+ static double **TermArray; // Incidence array used to check Terms
38
+
39
+ enum InpErrorCodes { // Error codes (401 - 409)
40
+ INP_ERR_FIRST = 400,
41
+ ERR_LINE_LENGTH,
42
+ ERR_ITEMS,
43
+ ERR_KEYWORD,
44
+ ERR_NUMBER,
45
+ ERR_NAME,
46
+ ERR_RESERVED_NAME,
47
+ ERR_DUP_NAME,
48
+ ERR_DUP_EXPR,
49
+ ERR_MATH_EXPR,
50
+ ERR_UNSUPPORTED_OPTION,
51
+ INP_ERR_LAST};
52
+
53
+ static char *InpErrorTxt[INP_ERR_LAST-INP_ERR_FIRST] = {
54
+ "",
55
+ "Error 401 (too many characters)",
56
+ "Error 402 (too few input items)",
57
+ "Error 403 (invalid keyword)",
58
+ "Error 404 (invalid numeric value)",
59
+ "Error 405 (reference to undefined object)",
60
+ "Error 406 (illegal use of a reserved name)",
61
+ "Error 407 (name already used by another object)",
62
+ "Error 408 (species already assigned an expression)",
63
+ "Error 409 (illegal math expression)",
64
+ "Error 410 (option no longer supported)"};
65
+
66
+ // Imported functions
67
+ //--------------------
68
+ int MSXproj_addObject(int type, char *id, int n);
69
+ int MSXproj_findObject(int type, char *id);
70
+ char * MSXproj_findID(int type, char *id);
71
+
72
+ // Exported functions
73
+ //--------------------
74
+ int MSXinp_countMsxObjects(void);
75
+ int MSXinp_countNetObjects(void);
76
+ int MSXinp_readNetData(void);
77
+ int MSXinp_readMsxData(void);
78
+ void MSXinp_getSpeciesUnits(int m, char *units);
79
+
80
+ // Local functions
81
+ //-----------------
82
+ static int getLineLength(char *line);
83
+ static int getNewSection(char *tok, char *sectWords[], int *sect);
84
+ static int addSpecies(char *line);
85
+ static int addCoeff(char *line);
86
+ static int addTerm(char *id);
87
+ static int addPattern(char *id);
88
+ static int checkID(char *id);
89
+ static int parseLine(int sect, char *line);
90
+ static int parseOption(void);
91
+ static int parseSpecies(void);
92
+ static int parseCoeff(void);
93
+ static int parseTerm(void);
94
+ static int parseExpression(int classType);
95
+ static int parseTankData(void);
96
+ static int parseQuality(void);
97
+ static int parseParameter(void);
98
+ static int parseSource(void);
99
+ static int parsePattern(void);
100
+ static int parseReport(void);
101
+ static int parseDiffu(void);
102
+ static int getVariableCode(char *id);
103
+ static int getTokens(char *s);
104
+ static void writeInpErrMsg(int errcode, char *sect, char *line, int lineCount);
105
+
106
+ static int checkCyclicTerms(void);
107
+ static int traceTermPath(int i, int istar, int n);
108
+
109
+ //=============================================================================
110
+
111
+ int MSXinp_countMsxObjects()
112
+ /*
113
+ ** Purpose:
114
+ ** reads multi-species input file to determine number of system objects.
115
+ **
116
+ ** Input:
117
+ ** none
118
+ **
119
+ ** Returns:
120
+ ** an error code (0 if no error)
121
+ */
122
+ {
123
+ char line[MAXLINE+1]; // line from input data file
124
+ char wLine[MAXLINE+1]; // working copy of input line
125
+ char *tok; // first string token of line
126
+ int sect = -1; // input data sections
127
+ int errcode = 0; // error code
128
+ int errsum = 0; // number of errors found
129
+ long lineCount = 0;
130
+
131
+ // --- write name of input file to EPANET report file
132
+
133
+ strcpy(MSX.Msg, "Processing MSX input file ");
134
+ strcpy(line, MSX.MsxFile.name);
135
+ strcat(MSX.Msg, line);
136
+ ENwriteline(MSX.Msg);
137
+ ENwriteline("");
138
+
139
+ // --- make pass through EPANET-MSX data file counting number of each object
140
+
141
+ while ( fgets(line, MAXLINE, MSX.MsxFile.file) != NULL )
142
+ {
143
+
144
+ // --- skip blank lines & those beginning with a comment
145
+
146
+ errcode = 0;
147
+ lineCount++;
148
+ strcpy(wLine, line); // make working copy of line
149
+ tok = strtok(wLine, SEPSTR); // get first text token on line
150
+ if ( tok == NULL || *tok == ';' ) continue;
151
+ if ( getNewSection(tok, MsxSectWords, &sect) ) continue;
152
+
153
+ // --- read id names from SPECIES, COEFFS, TERMS, & PATTERNS sections
154
+
155
+ if ( sect == s_SPECIES ) errcode = addSpecies(line);
156
+ if ( sect == s_COEFF ) errcode = addCoeff(line);
157
+ if ( sect == s_TERM ) errcode = addTerm(tok);
158
+ if ( sect == s_PATTERN ) errcode = addPattern(tok);
159
+
160
+
161
+ // --- report any error found
162
+
163
+ if ( errcode )
164
+ {
165
+ writeInpErrMsg(errcode, MsxSectWords[sect], line, lineCount);
166
+ errsum++;
167
+ if (errsum >= MAXERRS ) break;
168
+ }
169
+ }
170
+
171
+ // --- return error code
172
+
173
+ if ( errsum > 0 ) return ERR_MSX_INPUT;
174
+ return errcode;
175
+ }
176
+
177
+ //=============================================================================
178
+
179
+ int MSXinp_countNetObjects()
180
+ /*
181
+ ** Purpose:
182
+ ** queries EPANET data base to determine number of network objects.
183
+ **
184
+ ** Input:
185
+ ** none
186
+ **
187
+ ** Returns:
188
+ ** an error code (0 if no error)
189
+ */
190
+ {
191
+ int errcode = 0;
192
+
193
+ // --- retrieve number of network elements
194
+
195
+ CALL(errcode, ENgetcount(EN_NODECOUNT, &MSX.Nobjects[NODE]));
196
+ CALL(errcode, ENgetcount(EN_TANKCOUNT, &MSX.Nobjects[TANK]));
197
+ CALL(errcode, ENgetcount(EN_LINKCOUNT, &MSX.Nobjects[LINK]));
198
+ return errcode;
199
+ }
200
+
201
+ //=============================================================================
202
+
203
+ int MSXinp_readNetData()
204
+ /*
205
+ ** Purpose:
206
+ ** retrieves required input data from the EPANET project data.
207
+ **
208
+ ** Input:
209
+ ** none
210
+ **
211
+ ** Returns:
212
+ ** an error code (0 if no error)
213
+ */
214
+ {
215
+ int errcode = 0;
216
+ int i, k, n, t = 0;
217
+ int n1 = 0, n2 = 0;
218
+ long qstep;
219
+ float diam = 0.0, len = 0.0, v0 = 0.0, xmix = 0.0, vmix = 0.0;
220
+
221
+ float roughness = 0.0;
222
+
223
+ // --- get flow units & time parameters
224
+
225
+ CALL(errcode, ENgetflowunits(&MSX.Flowflag));
226
+ if ( MSX.Flowflag >= EN_LPS ) MSX.Unitsflag = SI;
227
+ else MSX.Unitsflag = US;
228
+ CALL(errcode, ENgettimeparam(EN_QUALSTEP, &qstep));
229
+ MSX.Qstep = qstep * 1000;
230
+ CALL(errcode, ENgettimeparam(EN_REPORTSTEP, &MSX.Rstep));
231
+ CALL(errcode, ENgettimeparam(EN_REPORTSTART, &MSX.Rstart));
232
+ CALL(errcode, ENgettimeparam(EN_PATTERNSTEP, &MSX.Pstep));
233
+ CALL(errcode, ENgettimeparam(EN_PATTERNSTART, &MSX.Pstart));
234
+ CALL(errcode, ENgettimeparam(EN_STATISTIC, &MSX.Statflag));
235
+
236
+ // --- read tank/reservoir data
237
+
238
+ n = MSX.Nobjects[NODE] - MSX.Nobjects[TANK];
239
+ for (i=1; i<=MSX.Nobjects[NODE]; i++)
240
+ {
241
+ k = i - n;
242
+ if ( k > 0 )
243
+ {
244
+ CALL(errcode, ENgetnodetype(i, &t));
245
+ CALL(errcode, ENgetnodevalue(i, EN_INITVOLUME, &v0));
246
+ CALL(errcode, ENgetnodevalue(i, EN_MIXMODEL, &xmix));
247
+ CALL(errcode, ENgetnodevalue(i, EN_MIXZONEVOL, &vmix));
248
+ if ( !errcode )
249
+ {
250
+ MSX.Node[i].tank = k;
251
+ MSX.Tank[k].node = i;
252
+ if ( t == EN_RESERVOIR ) MSX.Tank[k].a = 0.0;
253
+ else MSX.Tank[k].a = 1.0;
254
+ MSX.Tank[k].v0 = v0;
255
+ MSX.Tank[k].mixModel = (int)xmix;
256
+ MSX.Tank[k].vMix = vmix;
257
+ }
258
+ }
259
+ }
260
+
261
+ // --- read link data
262
+
263
+ for (i=1; i<=MSX.Nobjects[LINK]; i++)
264
+ {
265
+ CALL(errcode, ENgetlinknodes(i, &n1, &n2));
266
+ CALL(errcode, ENgetlinkvalue(i, EN_DIAMETER, &diam));
267
+ CALL(errcode, ENgetlinkvalue(i, EN_LENGTH, &len));
268
+ CALL(errcode, ENgetlinkvalue(i, EN_ROUGHNESS, &roughness));
269
+ if ( !errcode )
270
+ {
271
+ MSX.Link[i].n1 = n1;
272
+ MSX.Link[i].n2 = n2;
273
+ MSX.Link[i].diam = diam;
274
+ MSX.Link[i].len = len;
275
+ MSX.Link[i].roughness = roughness;
276
+ }
277
+ }
278
+ return errcode;
279
+ }
280
+
281
+ //=============================================================================
282
+
283
+ int MSXinp_readMsxData()
284
+ /*
285
+ ** Purpose:
286
+ ** reads multi-species data from the EPANET-MSX input file.
287
+ **
288
+ ** Input:
289
+ ** none
290
+ **
291
+ ** Returns:
292
+ ** an error code (0 if no error)
293
+ */
294
+ {
295
+ char line[MAXLINE+1]; // line from input data file
296
+ char wLine[MAXLINE+1]; // working copy of input line
297
+ int sect = -1; // input data sections
298
+ int errsum = 0; // number of errors found
299
+ int inperr = 0; // input error code
300
+ long lineCount = 0; // line count
301
+
302
+ // --- create the TermArray for checking circular references in Terms
303
+
304
+ TermArray = createMatrix(MSX.Nobjects[TERM]+1, MSX.Nobjects[TERM]+1);
305
+ if ( TermArray == NULL ) return ERR_MEMORY;
306
+
307
+ // --- read each line from MSX input file
308
+
309
+ rewind(MSX.MsxFile.file);
310
+ while ( fgets(line, MAXLINE, MSX.MsxFile.file) != NULL )
311
+ {
312
+ // --- make copy of line and scan for tokens
313
+
314
+ lineCount++;
315
+ strcpy(wLine, line);
316
+ Ntokens = getTokens(wLine);
317
+
318
+ // --- skip blank lines and comments
319
+
320
+ if ( Ntokens == 0 || *Tok[0] == ';' ) continue;
321
+
322
+ // --- check if max. line length exceeded
323
+
324
+ if ( getLineLength(line) >= MAXLINE )
325
+ {
326
+ inperr = ERR_LINE_LENGTH;
327
+ writeInpErrMsg(inperr, MsxSectWords[sect], line, lineCount);
328
+ errsum++;
329
+ }
330
+
331
+ // --- check if at start of a new input section
332
+
333
+ if ( getNewSection(Tok[0], MsxSectWords, &sect) ) continue;
334
+
335
+ // --- parse tokens from input line
336
+
337
+ inperr = parseLine(sect, line);
338
+ if ( inperr > 0 )
339
+ {
340
+ errsum++;
341
+ writeInpErrMsg(inperr, MsxSectWords[sect], line, lineCount);
342
+ }
343
+
344
+ // --- stop if reach end of file or max. error count
345
+
346
+ if (errsum >= MAXERRS) break;
347
+ } // End of while
348
+
349
+ // --- check for errors
350
+
351
+ if ( checkCyclicTerms() ) errsum++;
352
+ freeMatrix(TermArray);
353
+ if (errsum > 0) return ERR_MSX_INPUT;
354
+ return 0;
355
+ }
356
+
357
+ //=============================================================================
358
+
359
+ void MSXinp_getSpeciesUnits(int m, char *units)
360
+ /*
361
+ ** Purpose:
362
+ ** constructs the character string for a species concentration units.
363
+ **
364
+ ** Input:
365
+ ** m = species index
366
+ **
367
+ ** Output:
368
+ ** units = character string with species concentration units
369
+ */
370
+ {
371
+ strcpy(units, MSX.Species[m].units);
372
+ strcat(units, "/");
373
+ if ( MSX.Species[m].type == BULK ) strcat(units, "L");
374
+ else strcat(units, AreaUnitsWords[MSX.AreaUnits]);
375
+ }
376
+
377
+ //=============================================================================
378
+
379
+ int getLineLength(char *line)
380
+ /*
381
+ ** Purpose:
382
+ ** determines number of characters of data in a line of input.
383
+ **
384
+ ** Input:
385
+ ** line = line of text from an input file
386
+ **
387
+ ** Returns:
388
+ ** number of characters in the line (with comment ignored).
389
+ */
390
+ {
391
+ char *comment;
392
+ int lineLength = (int)strlen(line);
393
+ if ( lineLength >= MAXLINE )
394
+ {
395
+ // --- don't count comment if present
396
+ comment = strchr(line, ';');
397
+ if ( comment ) lineLength = (int)(comment - line); // Pointer math here
398
+ }
399
+ return lineLength;
400
+ }
401
+
402
+ //=============================================================================
403
+
404
+ int getNewSection(char *tok, char *sectWords[], int *sect)
405
+ /*
406
+ ** Purpose:
407
+ ** checks if a line begins a new section in the input file.
408
+ **
409
+ ** Input:
410
+ ** tok = a string token
411
+ ** sectWords = array of input file section keywords
412
+ **
413
+ ** Output:
414
+ ** sect = index code of section matching tok (or -1 if no match)
415
+ **
416
+ ** Returns:
417
+ ** 1 if a section is found, 0 if not
418
+ */
419
+ {
420
+ int newsect;
421
+
422
+ // --- check if line begins with a new section heading
423
+
424
+ if ( *tok == '[' )
425
+ {
426
+ // --- look for section heading in list of section keywords
427
+
428
+ newsect = MSXutils_findmatch(tok, sectWords);
429
+ if ( newsect >= 0 ) *sect = newsect;
430
+ else *sect = -1;
431
+ return 1;
432
+ }
433
+ return 0;
434
+ }
435
+
436
+ //=============================================================================
437
+
438
+ int addSpecies(char *line)
439
+ /*
440
+ ** Purpose:
441
+ ** adds a species ID name to the project.
442
+ **
443
+ ** Input:
444
+ ** line = line of input data
445
+ **
446
+ ** Returns:
447
+ ** an error code (0 if no error)
448
+ */
449
+ {
450
+ int errcode = 0;
451
+ Ntokens = getTokens(line);
452
+ if ( Ntokens < 2 ) return ERR_ITEMS;
453
+ errcode = checkID(Tok[1]);
454
+ if ( errcode ) return errcode;
455
+ if ( MSXproj_addObject(SPECIES, Tok[1], MSX.Nobjects[SPECIES]+1) < 0 )
456
+ errcode = 101;
457
+ else MSX.Nobjects[SPECIES]++;
458
+ return errcode;
459
+ }
460
+
461
+ //=============================================================================
462
+
463
+ int addCoeff(char *line)
464
+ /*
465
+ ** Purpose:
466
+ ** adds a coefficient ID name to the project.
467
+ **
468
+ ** Input:
469
+ ** line = line of input data
470
+ **
471
+ ** Returns:
472
+ ** an error code (0 if no error)
473
+ */
474
+ {
475
+ int k;
476
+ int errcode = 0;
477
+
478
+ // --- determine the type of coeff.
479
+
480
+ Ntokens = getTokens(line);
481
+ if ( Ntokens < 2 ) return ERR_ITEMS;
482
+ if (MSXutils_match(Tok[0], "PARAM")) k = PARAMETER;
483
+ else if (MSXutils_match(Tok[0], "CONST")) k = CONSTANT;
484
+ else return ERR_KEYWORD;
485
+
486
+ // --- check for valid id name
487
+
488
+ errcode = checkID(Tok[1]);
489
+ if ( errcode ) return errcode;
490
+ if ( MSXproj_addObject(k, Tok[1], MSX.Nobjects[k]+1) < 0 )
491
+ errcode = 101;
492
+ else MSX.Nobjects[k]++;
493
+ return errcode;
494
+ }
495
+
496
+ //=============================================================================
497
+
498
+ int addTerm(char *id)
499
+ /*
500
+ ** Purpose:
501
+ ** adds an intermediate expression term ID name to the project.
502
+ **
503
+ ** Input:
504
+ ** id = name of an intermediate expression term
505
+ **
506
+ ** Returns:
507
+ ** an error code (0 if no error)
508
+ */
509
+ {
510
+ int errcode = checkID(id);
511
+ if ( !errcode )
512
+ {
513
+ if ( MSXproj_addObject(TERM, id, MSX.Nobjects[TERM]+1) < 0 )
514
+ errcode = 101;
515
+ else MSX.Nobjects[TERM]++;
516
+ }
517
+ return errcode;
518
+ }
519
+
520
+ //=============================================================================
521
+
522
+ int addPattern(char *id)
523
+ /*
524
+ ** Purpose:
525
+ ** adds a time pattern ID name to the project.
526
+ **
527
+ ** Input:
528
+ ** id = name of a time pattern
529
+ **
530
+ ** Returns:
531
+ ** an error code (0 if no error)
532
+ */
533
+ {
534
+ int errcode = 0;
535
+
536
+ // --- a time pattern can span several lines
537
+
538
+ if ( MSXproj_findObject(PATTERN, id) <= 0 )
539
+ {
540
+ if ( MSXproj_addObject(PATTERN, id, MSX.Nobjects[PATTERN]+1) < 0 )
541
+ errcode = 101;
542
+ else MSX.Nobjects[PATTERN]++;
543
+ }
544
+ return errcode;
545
+ }
546
+
547
+ //=============================================================================
548
+
549
+ int checkID(char *id)
550
+ /*
551
+ ** Purpose:
552
+ ** checks that an object's name is unique
553
+ **
554
+ ** Input:
555
+ ** id = name of an object
556
+ **
557
+ ** Returns:
558
+ ** an error code (0 if successful)
559
+ */
560
+ {
561
+ // --- check that id name is not a reserved word
562
+ int i = 1;
563
+ while (HydVarWords[i] != NULL)
564
+ {
565
+ if (MSXutils_strcomp(id, HydVarWords[i])) return ERR_RESERVED_NAME;
566
+ i++;
567
+ }
568
+
569
+ // --- check that id name not used before
570
+
571
+ if ( MSXproj_findObject(SPECIES, id) > 0 ||
572
+ MSXproj_findObject(TERM, id) > 0 ||
573
+ MSXproj_findObject(PARAMETER, id) > 0 ||
574
+ MSXproj_findObject(CONSTANT, id) > 0
575
+ ) return ERR_DUP_NAME;
576
+ return 0;
577
+ }
578
+
579
+ //=============================================================================
580
+
581
+ int parseLine(int sect, char *line)
582
+ /*
583
+ ** Purpose:
584
+ ** parses the contents of a line of input data.
585
+ **
586
+ ** Input:
587
+ ** sect = index of current input data section
588
+ ** line = contents of current line of input data
589
+ **
590
+ ** Returns:
591
+ ** an error code (0 if no error)
592
+ */
593
+ {
594
+ switch(sect)
595
+ {
596
+ case s_TITLE:
597
+ strcpy(MSX.Title, line);
598
+ break;
599
+
600
+ case s_OPTION:
601
+ return parseOption();
602
+
603
+ case s_SPECIES:
604
+ return parseSpecies();
605
+
606
+ case s_COEFF:
607
+ return parseCoeff();
608
+
609
+ case s_TERM:
610
+ return parseTerm();
611
+
612
+ case s_PIPE:
613
+ return parseExpression(LINK);
614
+
615
+ case s_TANK:
616
+ return parseExpression(TANK);
617
+
618
+ case s_SOURCE:
619
+ return parseSource();
620
+
621
+ case s_QUALITY:
622
+ return parseQuality();
623
+
624
+ case s_PARAMETER:
625
+ return parseParameter();
626
+
627
+ case s_PATTERN:
628
+ return parsePattern();
629
+
630
+ case s_REPORT:
631
+ return parseReport();
632
+
633
+ case s_Diffu:
634
+ return parseDiffu();
635
+ }
636
+ return 0;
637
+ }
638
+
639
+ //=============================================================================
640
+
641
+ int parseOption()
642
+ /*
643
+ ** Purpose:
644
+ ** parses an input line containing a project option.
645
+ **
646
+ ** Input:
647
+ ** none
648
+ **
649
+ ** Returns:
650
+ ** an error code (0 if no error)
651
+ */
652
+ {
653
+ int k;
654
+ double v;
655
+
656
+ // --- determine which option is being read
657
+
658
+ if ( Ntokens < 2 ) return 0;
659
+ k = MSXutils_findmatch(Tok[0], OptionTypeWords);
660
+ if ( k < 0 ) return ERR_KEYWORD;
661
+
662
+ // --- parse the value for the given option
663
+
664
+ switch ( k )
665
+ {
666
+ case AREA_UNITS_OPTION:
667
+ k = MSXutils_findmatch(Tok[1], AreaUnitsWords);
668
+ if ( k < 0 ) return ERR_KEYWORD;
669
+ MSX.AreaUnits = k;
670
+ break;
671
+
672
+ case RATE_UNITS_OPTION:
673
+ k = MSXutils_findmatch(Tok[1], TimeUnitsWords);
674
+ if ( k < 0 ) return ERR_KEYWORD;
675
+ MSX.RateUnits = k;
676
+ break;
677
+
678
+ case SOLVER_OPTION:
679
+ k = MSXutils_findmatch(Tok[1], SolverTypeWords);
680
+ if ( k < 0 ) return ERR_KEYWORD;
681
+ MSX.Solver = k;
682
+ break;
683
+
684
+ case COUPLING_OPTION:
685
+ k = MSXutils_findmatch(Tok[1], CouplingWords);
686
+ if ( k < 0 ) return ERR_KEYWORD;
687
+ MSX.Coupling = k;
688
+ break;
689
+
690
+ case TIMESTEP_OPTION:
691
+
692
+ // Read time step as a floating point value in seconds
693
+ if ( !MSXutils_getDouble(Tok[1], &v) ) return ERR_NUMBER;
694
+ if ( v < 0.001 ) return ERR_NUMBER;
695
+
696
+ // Convert time step to integer milliseconds
697
+ v = round(v * 1000.);
698
+ MSX.Qstep = (int64_t)v;
699
+ break;
700
+
701
+ case RTOL_OPTION:
702
+ if ( !MSXutils_getDouble(Tok[1], &MSX.DefRtol) ) return ERR_NUMBER;
703
+ break;
704
+
705
+ case ATOL_OPTION:
706
+ if ( !MSXutils_getDouble(Tok[1], &MSX.DefAtol) ) return ERR_NUMBER;
707
+ break;
708
+
709
+ case COMPILER_OPTION:
710
+ k = MSXutils_findmatch(Tok[1], CompilerWords);
711
+ if ( k < 0 ) return ERR_KEYWORD;
712
+ MSX.Compiler = k;
713
+ break;
714
+
715
+ case PECLETNUMER_OPTION:
716
+ v = atof(Tok[1]);
717
+ if (v <= 0.0)return ERR_NUMBER;
718
+ MSX.Dispersion.PecletLimit = MAX(v, 1.0);
719
+ break;
720
+
721
+ case MAXSEGMENT_OPTION:
722
+ k = atoi(Tok[1]);
723
+ if (k <= 0) return ERR_NUMBER;
724
+ MSX.MaxSegments = MAX(k, 50); //at least 50 segments
725
+
726
+ }
727
+ return 0;
728
+ }
729
+
730
+ //=============================================================================
731
+
732
+ int parseSpecies()
733
+ /*
734
+ ** Purpose:
735
+ ** parses an input line containing a species variable.
736
+ **
737
+ ** Input:
738
+ ** none
739
+ **
740
+ ** Returns:
741
+ ** an error code (0 if no error)
742
+ */
743
+ {
744
+ int i;
745
+
746
+ // --- get Species index
747
+
748
+ if ( Ntokens < 3 ) return ERR_ITEMS;
749
+ i = MSXproj_findObject(SPECIES, Tok[1]);
750
+ if ( i <= 0 ) return ERR_NAME;
751
+
752
+ // --- get pointer to Species name
753
+
754
+ MSX.Species[i].id = MSXproj_findID(SPECIES, Tok[1]);
755
+
756
+ // --- get Species type
757
+
758
+ if ( MSXutils_match(Tok[0], "BULK") ) MSX.Species[i].type = BULK;
759
+ else if ( MSXutils_match(Tok[0], "WALL") ) MSX.Species[i].type = WALL;
760
+ else return ERR_KEYWORD;
761
+
762
+ // --- get Species units
763
+
764
+ strncpy(MSX.Species[i].units, Tok[2], MAXUNITS);
765
+
766
+ // --- get Species error tolerance
767
+
768
+ MSX.Species[i].aTol = 0.0;
769
+ MSX.Species[i].rTol = 0.0;
770
+ if ( Ntokens >= 4)
771
+ {
772
+ if ( !MSXutils_getDouble(Tok[3], &MSX.Species[i].aTol) )
773
+ return ERR_NUMBER;
774
+ }
775
+ if ( Ntokens >= 5)
776
+ {
777
+ if ( !MSXutils_getDouble(Tok[4], &MSX.Species[i].rTol) )
778
+ return ERR_NUMBER;
779
+ }
780
+ return 0;
781
+ }
782
+
783
+ //=============================================================================
784
+
785
+ int parseCoeff()
786
+ /*
787
+ ** Purpose:
788
+ ** parses an input line containing a coefficient definition.
789
+ **
790
+ ** Input:
791
+ ** none
792
+ **
793
+ ** Returns:
794
+ ** an error code (0 if no error)
795
+ */
796
+ {
797
+ int i, j;
798
+ double x;
799
+
800
+ // --- check if variable is a Parameter
801
+
802
+ if ( Ntokens < 2 ) return 0;
803
+ if ( MSXutils_match(Tok[0], "PARAM") )
804
+ {
805
+ // --- get Parameter's index
806
+
807
+ i = MSXproj_findObject(PARAMETER, Tok[1]);
808
+ if ( i <= 0 ) return ERR_NAME;
809
+
810
+ // --- get Parameter's value
811
+
812
+ MSX.Param[i].id = MSXproj_findID(PARAMETER, Tok[1]);
813
+ if ( Ntokens >= 3 )
814
+ {
815
+ if ( !MSXutils_getDouble(Tok[2], &x) ) return ERR_NUMBER;
816
+ MSX.Param[i].value = x;
817
+ for (j=1; j<=MSX.Nobjects[LINK]; j++) MSX.Link[j].param[i] = x;
818
+ for (j=1; j<=MSX.Nobjects[TANK]; j++) MSX.Tank[j].param[i] = x;
819
+ }
820
+ return 0;
821
+ }
822
+
823
+ // --- check if variable is a Constant
824
+
825
+ else if ( MSXutils_match(Tok[0], "CONST") )
826
+ {
827
+ // --- get Constant's index
828
+
829
+ i = MSXproj_findObject(CONSTANT, Tok[1]);
830
+ if ( i <= 0 ) return ERR_NAME;
831
+
832
+ // --- get Constant's value
833
+
834
+ MSX.Const[i].id = MSXproj_findID(CONSTANT, Tok[1]);
835
+ MSX.Const[i].value = 0.0;
836
+ if ( Ntokens >= 3 )
837
+ {
838
+ if ( !MSXutils_getDouble(Tok[2], &MSX.Const[i].value) )
839
+ return ERR_NUMBER;
840
+ }
841
+ return 0;
842
+ }
843
+ else return ERR_KEYWORD;
844
+ }
845
+
846
+ //=============================================================================
847
+
848
+ int parseTerm()
849
+ /*
850
+ ** Purpose:
851
+ ** parses an input line containing an intermediate expression term .
852
+ **
853
+ ** Input:
854
+ ** none
855
+ **
856
+ ** Returns:
857
+ ** an error code (0 if no error)
858
+ */
859
+ {
860
+ int i, j;
861
+ int k;
862
+ char s[MAXLINE+1] = "";
863
+ MathExpr *expr;
864
+
865
+ // --- get term's name
866
+
867
+ if ( Ntokens < 2 ) return 0;
868
+ i = MSXproj_findObject(TERM, Tok[0]);
869
+ MSX.Term[i].id = MSXproj_findID(TERM, Tok[0]);
870
+
871
+ // --- reconstruct the expression string from its tokens
872
+
873
+ for (j=1; j<Ntokens; j++)
874
+ {
875
+ strcat(s, Tok[j]);
876
+ k = MSXproj_findObject(TERM, Tok[j]);
877
+ if ( k > 0 ) TermArray[i][k] = 1.0;
878
+ }
879
+
880
+ // --- convert expression into a postfix stack of op codes
881
+
882
+ expr = mathexpr_create(s, getVariableCode);
883
+ if ( expr == NULL ) return ERR_MATH_EXPR;
884
+
885
+ // --- assign the expression to a Term object
886
+
887
+ MSX.Term[i].expr = expr;
888
+ return 0;
889
+ }
890
+
891
+ //=============================================================================
892
+
893
+ int parseExpression(int classType)
894
+ /*
895
+ ** Purpose:
896
+ ** parses an input line containing a math expression.
897
+ **
898
+ ** Input:
899
+ ** classType = either LINK or TANK
900
+ **
901
+ ** Returns:
902
+ ** an error code (0 if no error)
903
+ */
904
+ {
905
+ int i, j, k;
906
+ char s[MAXLINE+1] = "";
907
+ MathExpr *expr;
908
+
909
+ // --- determine expression type
910
+
911
+ if ( Ntokens < 3 ) return ERR_ITEMS;
912
+ k = MSXutils_findmatch(Tok[0], ExprTypeWords);
913
+ if ( k < 0 ) return ERR_KEYWORD;
914
+
915
+ // --- determine species associated with expression
916
+
917
+ i = MSXproj_findObject(SPECIES, Tok[1]);
918
+ if ( i < 1 ) return ERR_NAME;
919
+
920
+ // --- check that species does not already have an expression
921
+
922
+ if ( classType == LINK )
923
+ {
924
+ if ( MSX.Species[i].pipeExprType != NO_EXPR ) return ERR_DUP_EXPR;
925
+ }
926
+ if ( classType == TANK )
927
+ {
928
+ if ( MSX.Species[i].tankExprType != NO_EXPR ) return ERR_DUP_EXPR;
929
+ }
930
+
931
+ // --- reconstruct the expression string from its tokens
932
+
933
+ for (j=2; j<Ntokens; j++) strcat(s, Tok[j]);
934
+
935
+ // --- convert expression into a postfix stack of op codes
936
+
937
+ expr = mathexpr_create(s, getVariableCode);
938
+ if ( expr == NULL ) return ERR_MATH_EXPR;
939
+
940
+ // --- assign the expression to the species
941
+
942
+ switch (classType)
943
+ {
944
+ case LINK:
945
+ MSX.Species[i].pipeExpr = expr;
946
+ MSX.Species[i].pipeExprType = k;
947
+ break;
948
+ case TANK:
949
+ MSX.Species[i].tankExpr = expr;
950
+ MSX.Species[i].tankExprType = k;
951
+ break;
952
+ }
953
+ return 0;
954
+ }
955
+
956
+ //=============================================================================
957
+
958
+ int parseQuality()
959
+ /*
960
+ ** Purpose:
961
+ ** parses an input line containing initial species concentrations.
962
+ **
963
+ ** Input:
964
+ ** none
965
+ **
966
+ ** Returns:
967
+ ** an error code (0 if no error)
968
+ */
969
+ {
970
+ int err, i, j, k, m;
971
+ double x;
972
+
973
+ // --- determine if quality value is global or object-specific
974
+
975
+ if ( Ntokens < 3 ) return ERR_ITEMS;
976
+ if ( MSXutils_match(Tok[0], "GLOBAL") ) i = 1;
977
+ else if ( MSXutils_match(Tok[0], "NODE") ) i = 2;
978
+ else if ( MSXutils_match(Tok[0], "LINK") ) i = 3;
979
+ else return ERR_KEYWORD;
980
+
981
+ // --- find species index
982
+
983
+ k = 1;
984
+ if ( i >= 2 ) k = 2;
985
+ m = MSXproj_findObject(SPECIES, Tok[k]);
986
+ if ( m <= 0 ) return ERR_NAME;
987
+
988
+ // --- get quality value
989
+
990
+ if ( i >= 2 && Ntokens < 4 ) return ERR_ITEMS;
991
+ k = 2;
992
+ if ( i >= 2 ) k = 3;
993
+ if ( !MSXutils_getDouble(Tok[k], &x) ) return ERR_NUMBER;
994
+
995
+ // --- for global specification, set initial quality either for
996
+ // all nodes or links depending on type of species
997
+
998
+ if ( i == 1)
999
+ {
1000
+ MSX.C0[m] = x;
1001
+ if ( MSX.Species[m].type == BULK )
1002
+ {
1003
+ for (j=1; j<=MSX.Nobjects[NODE]; j++) MSX.Node[j].c0[m] = x;
1004
+ }
1005
+ for (j=1; j<=MSX.Nobjects[LINK]; j++) MSX.Link[j].c0[m] = x;
1006
+ }
1007
+
1008
+ // --- for a specific node, get its index & set its initial quality
1009
+
1010
+ else if ( i == 2 )
1011
+ {
1012
+ err = ENgetnodeindex(Tok[1], &j);
1013
+ if ( err ) return ERR_NAME;
1014
+ if ( MSX.Species[m].type == BULK ) MSX.Node[j].c0[m] = x;
1015
+ }
1016
+
1017
+ // --- for a specific link, get its index & set its initial quality
1018
+
1019
+ else if ( i == 3 )
1020
+ {
1021
+ err = ENgetlinkindex(Tok[1], &j);
1022
+ if ( err ) return ERR_NAME;
1023
+ MSX.Link[j].c0[m] = x;
1024
+ }
1025
+ return 0;
1026
+ }
1027
+
1028
+ //=============================================================================
1029
+
1030
+ int parseParameter()
1031
+ /*
1032
+ ** Purpose:
1033
+ ** parses an input line containing a parameter data.
1034
+ **
1035
+ ** Input:
1036
+ ** none
1037
+ **
1038
+ ** Returns:
1039
+ ** an error code (0 if no error)
1040
+ */
1041
+ {
1042
+ int err, i, j;
1043
+ double x;
1044
+
1045
+ // --- get parameter name
1046
+
1047
+ if ( Ntokens < 4 ) return 0;
1048
+ i = MSXproj_findObject(PARAMETER, Tok[2]);
1049
+
1050
+ // --- get parameter value
1051
+
1052
+ if ( !MSXutils_getDouble(Tok[3], &x) ) return ERR_NUMBER;
1053
+
1054
+ // --- for pipe parameter, get pipe index and update parameter's value
1055
+
1056
+ if ( MSXutils_match(Tok[0], "PIPE") )
1057
+ {
1058
+ err = ENgetlinkindex(Tok[1], &j);
1059
+ if ( err ) return ERR_NAME;
1060
+ MSX.Link[j].param[i] = x;
1061
+ }
1062
+
1063
+ // --- for tank parameter, get tank index and update parameter's value
1064
+
1065
+ else if ( MSXutils_match(Tok[0], "TANK") )
1066
+ {
1067
+ err = ENgetnodeindex(Tok[1], &j);
1068
+ if ( err ) return ERR_NAME;
1069
+ j = MSX.Node[j].tank;
1070
+ if ( j > 0 ) MSX.Tank[j].param[i] = x;
1071
+ }
1072
+ else return ERR_KEYWORD;
1073
+ return 0;
1074
+ }
1075
+
1076
+ //=============================================================================
1077
+
1078
+ int parseSource()
1079
+ /*
1080
+ ** Purpose:
1081
+ ** parses an input line containing a source input data.
1082
+ **
1083
+ ** Input:
1084
+ ** none
1085
+ **
1086
+ ** Returns:
1087
+ ** an error code (0 if no error)
1088
+ */
1089
+ {
1090
+ int err, i, j, k, m;
1091
+ double x;
1092
+ Psource source;
1093
+
1094
+ // --- get source type
1095
+
1096
+ if ( Ntokens < 4 ) return ERR_ITEMS;
1097
+ k = MSXutils_findmatch(Tok[0], SourceTypeWords);
1098
+ if ( k < 0 ) return ERR_KEYWORD;
1099
+
1100
+ // --- get node index
1101
+
1102
+ err = ENgetnodeindex(Tok[1], &j);
1103
+ if ( err ) return ERR_NAME;
1104
+
1105
+ // --- get species index
1106
+
1107
+ m = MSXproj_findObject(SPECIES, Tok[2]);
1108
+ if ( m <= 0 ) return ERR_NAME;
1109
+
1110
+ // --- check that species is a BULK species
1111
+
1112
+ if ( MSX.Species[m].type != BULK ) return 0;
1113
+
1114
+ // --- get base strength
1115
+
1116
+ if ( !MSXutils_getDouble(Tok[3], &x) ) return ERR_NUMBER;
1117
+
1118
+ // --- get time pattern if present
1119
+
1120
+ i = 0;
1121
+ if ( Ntokens >= 5 )
1122
+ {
1123
+ i = MSXproj_findObject(PATTERN, Tok[4]);
1124
+ if ( i <= 0 ) return ERR_NAME;
1125
+ }
1126
+
1127
+ // --- check if a source for this species already exists
1128
+
1129
+ source = MSX.Node[j].sources;
1130
+ while ( source )
1131
+ {
1132
+ if ( source->species == m ) break;
1133
+ source = source->next;
1134
+ }
1135
+
1136
+ // --- otherwise create a new source object
1137
+
1138
+ if ( source == NULL )
1139
+ {
1140
+ source = (struct Ssource *) malloc(sizeof(struct Ssource));
1141
+ if ( source == NULL ) return 101;
1142
+ source->next = MSX.Node[j].sources;
1143
+ MSX.Node[j].sources = source;
1144
+ }
1145
+
1146
+ // --- save source's properties
1147
+
1148
+ source->type = (char)k;
1149
+ source->species = m;
1150
+ source->c0 = x;
1151
+ source->pat = i;
1152
+ return 0;
1153
+ }
1154
+
1155
+ //=============================================================================
1156
+
1157
+ int parsePattern()
1158
+ /*
1159
+ ** Purpose:
1160
+ ** parses an input line containing a time pattern data.
1161
+ **
1162
+ ** Input:
1163
+ ** none
1164
+ **
1165
+ ** Returns:
1166
+ ** an error code (0 if no error)
1167
+ */
1168
+ {
1169
+ int i, k;
1170
+ double x;
1171
+ SnumList *listItem;
1172
+
1173
+ // --- get time pattern index
1174
+
1175
+ if ( Ntokens < 2 ) return ERR_ITEMS;
1176
+ i = MSXproj_findObject(PATTERN, Tok[0]);
1177
+ if ( i <= 0 ) return ERR_NAME;
1178
+ MSX.Pattern[i].id = MSXproj_findID(PATTERN, Tok[0]);
1179
+
1180
+ // --- begin reading pattern multipliers
1181
+
1182
+ k = 1;
1183
+ while ( k < Ntokens )
1184
+ {
1185
+ if ( !MSXutils_getDouble(Tok[k], &x) ) return ERR_NUMBER;
1186
+ listItem = (SnumList *) malloc(sizeof(SnumList));
1187
+ if ( listItem == NULL ) return 101;
1188
+ listItem->value = x;
1189
+ listItem->next = NULL;
1190
+ if ( MSX.Pattern[i].first == NULL )
1191
+ {
1192
+ MSX.Pattern[i].current = listItem;
1193
+ MSX.Pattern[i].first = listItem;
1194
+ }
1195
+ else
1196
+ {
1197
+ MSX.Pattern[i].current->next = listItem;
1198
+ MSX.Pattern[i].current = listItem;
1199
+ }
1200
+ MSX.Pattern[i].length++;
1201
+ k++;
1202
+ }
1203
+ return 0;
1204
+ }
1205
+
1206
+ //=============================================================================
1207
+
1208
+ int parseReport()
1209
+ {
1210
+ int i, j, k, err;
1211
+
1212
+ // --- get keyword
1213
+
1214
+ if ( Ntokens < 2 ) return 0;
1215
+ k = MSXutils_findmatch(Tok[0], ReportWords);
1216
+ if ( k < 0 ) return ERR_KEYWORD;
1217
+ switch(k)
1218
+ {
1219
+
1220
+ // --- keyword is NODE; parse ID names of reported nodes
1221
+
1222
+ case 0:
1223
+ if ( MSXutils_strcomp(Tok[1], ALL) )
1224
+ {
1225
+ for (j=1; j<=MSX.Nobjects[NODE]; j++) MSX.Node[j].rpt = 1;
1226
+ }
1227
+ else if ( MSXutils_strcomp(Tok[1], NONE) )
1228
+ {
1229
+ for (j=1; j<=MSX.Nobjects[NODE]; j++) MSX.Node[j].rpt = 0;
1230
+ }
1231
+ else for (i=1; i<Ntokens; i++)
1232
+ {
1233
+ err = ENgetnodeindex(Tok[i], &j);
1234
+ if ( err ) return ERR_NAME;
1235
+ MSX.Node[j].rpt = 1;
1236
+ }
1237
+ break;
1238
+
1239
+ // --- keyword is LINK: parse ID names of reported links
1240
+ case 1:
1241
+ if ( MSXutils_strcomp(Tok[1], ALL) )
1242
+ {
1243
+ for (j=1; j<=MSX.Nobjects[LINK]; j++) MSX.Link[j].rpt = 1;
1244
+ }
1245
+ else if ( MSXutils_strcomp(Tok[1], NONE) )
1246
+ {
1247
+ for (j=1; j<=MSX.Nobjects[LINK]; j++) MSX.Link[j].rpt = 0;
1248
+ }
1249
+ else for (i=1; i<Ntokens; i++)
1250
+ {
1251
+ err = ENgetlinkindex(Tok[i], &j);
1252
+ if ( err ) return ERR_NAME;
1253
+ MSX.Link[j].rpt = 1;
1254
+ }
1255
+ break;
1256
+
1257
+ // --- keyword is SPECIES; get YES/NO & precision
1258
+
1259
+ case 2:
1260
+ j = MSXproj_findObject(SPECIES, Tok[1]);
1261
+ if ( j <= 0 ) return ERR_NAME;
1262
+ if ( Ntokens >= 3 )
1263
+ {
1264
+ if ( MSXutils_strcomp(Tok[2], YES) ) MSX.Species[j].rpt = 1;
1265
+ else if ( MSXutils_strcomp(Tok[2], NO) ) MSX.Species[j].rpt = 0;
1266
+ else return ERR_KEYWORD;
1267
+ }
1268
+ if ( Ntokens >= 4 )
1269
+ {
1270
+ if ( !MSXutils_getInt(Tok[3], &MSX.Species[j].precision) )
1271
+ return ERR_NUMBER;
1272
+ }
1273
+ break;
1274
+
1275
+ // --- keyword is FILE: get name of report file
1276
+
1277
+ case 3:
1278
+ strcpy(MSX.RptFile.name, Tok[1]);
1279
+ break;
1280
+
1281
+ // --- keyword is PAGESIZE;
1282
+
1283
+ case 4:
1284
+ if ( !MSXutils_getInt(Tok[1], &MSX.PageSize) ) return ERR_NUMBER;
1285
+ break;
1286
+ }
1287
+ return 0;
1288
+ }
1289
+
1290
+ int parseDiffu()
1291
+ /*
1292
+ ** Purpose:
1293
+ ** parses an input line containing molecular diffusivity data.
1294
+ **
1295
+ ** Input:
1296
+ ** none
1297
+ **
1298
+ ** Returns:
1299
+ ** an error code (0 if no error)
1300
+ */
1301
+ {
1302
+ int m;
1303
+ double x;
1304
+
1305
+ // --- get source type
1306
+ if (Ntokens < 2) return ERR_ITEMS;
1307
+
1308
+ // --- get species index
1309
+ m = MSXproj_findObject(SPECIES, Tok[0]);
1310
+ if (m <= 0) return ERR_NAME;
1311
+
1312
+ // --- check that species is a BULK species
1313
+ if (MSX.Species[m].type != BULK) return 0;
1314
+
1315
+ // --- get base strength
1316
+ if (!MSXutils_getDouble(Tok[1], &x)) return ERR_NUMBER;
1317
+ if (x < 0) return ERR_NUMBER;
1318
+
1319
+ if (Ntokens > 2 && MSXutils_match(Tok[2], "FIXED"))
1320
+ {
1321
+ x = x * MSX.Dispersion.DIFFUS;
1322
+ MSX.Dispersion.ld[m] = x;
1323
+ }
1324
+ else
1325
+ {
1326
+ x = x * MSX.Dispersion.DIFFUS;
1327
+ MSX.Dispersion.md[m] = x;
1328
+ }
1329
+ MSX.DispersionFlag = 1;
1330
+ return 0;
1331
+ }
1332
+
1333
+ //=============================================================================
1334
+
1335
+ int getVariableCode(char *id)
1336
+ /*
1337
+ ** Purpose:
1338
+ ** finds the index assigned to a species, intermediate term,
1339
+ ** parameter, or constant that appears in a math expression.
1340
+ **
1341
+ ** Input:
1342
+ ** id = ID name being sought
1343
+ **
1344
+ ** Returns:
1345
+ ** index of the symbolic variable or term named id.
1346
+ **
1347
+ ** Note:
1348
+ ** Variables are assigned consecutive code numbers starting from 1
1349
+ ** and proceeding through each Species, Term, Parameter and Constant.
1350
+ */
1351
+ {
1352
+ int j = MSXproj_findObject(SPECIES, id);
1353
+ if ( j >= 1 ) return j;
1354
+ j = MSXproj_findObject(TERM, id);
1355
+ if ( j >= 1 ) return MSX.Nobjects[SPECIES] + j;
1356
+ j = MSXproj_findObject(PARAMETER, id);
1357
+ if ( j >= 1 ) return MSX.Nobjects[SPECIES] + MSX.Nobjects[TERM] + j;
1358
+ j = MSXproj_findObject(CONSTANT, id);
1359
+ if ( j >= 1 ) return MSX.Nobjects[SPECIES] + MSX.Nobjects[TERM] +
1360
+ MSX.Nobjects[PARAMETER] + j;
1361
+ j = MSXutils_findmatch(id, HydVarWords);
1362
+ if ( j >= 1 ) return MSX.Nobjects[SPECIES] + MSX.Nobjects[TERM] +
1363
+ MSX.Nobjects[PARAMETER] + MSX.Nobjects[CONSTANT] + j;
1364
+ return -1;
1365
+ }
1366
+
1367
+ //=============================================================================
1368
+
1369
+ int getTokens(char *s)
1370
+ /*
1371
+ ** Purpose:
1372
+ ** scans a string for tokens, saving pointers to them
1373
+ ** in shared variable Tok[].
1374
+ **
1375
+ ** Input:
1376
+ ** s = a character string
1377
+ **
1378
+ ** Returns:
1379
+ ** number of tokens found in s
1380
+ **
1381
+ ** Notes:
1382
+ ** Tokens can be separated by the characters listed in SEPSTR
1383
+ ** (spaces, tabs, newline, carriage return) which is defined in
1384
+ ** MSXGLOBALS.H. Text between quotes is treated as a single token.
1385
+ */
1386
+ {
1387
+ int len, m, n;
1388
+ char *c;
1389
+
1390
+ // --- begin with no tokens
1391
+
1392
+ for (n = 0; n < MAXTOKS; n++) Tok[n] = NULL;
1393
+ n = 0;
1394
+
1395
+ // --- truncate s at start of comment
1396
+
1397
+ c = strchr(s,';');
1398
+ if (c) *c = '\0';
1399
+ len = (int)strlen(s);
1400
+
1401
+ // --- scan s for tokens until nothing left
1402
+
1403
+ while (len > 0 && n < MAXTOKS)
1404
+ {
1405
+ m = (int)strcspn(s,SEPSTR); // find token length
1406
+ if (m == 0) s++; // no token found
1407
+ else
1408
+ {
1409
+ if (*s == '"') // token begins with quote
1410
+ {
1411
+ s++; // start token after quote
1412
+ len--; // reduce length of s
1413
+ m = (int)strcspn(s,"\"\n"); // find end quote or new line
1414
+ }
1415
+ s[m] = '\0'; // null-terminate the token
1416
+ Tok[n] = s; // save pointer to token
1417
+ n++; // update token count
1418
+ s += m+1; // begin next token
1419
+ }
1420
+ len -= m+1; // update length of s
1421
+ }
1422
+ return(n);
1423
+ }
1424
+
1425
+ //=============================================================================
1426
+
1427
+ void writeInpErrMsg(int errcode, char *sect, char *line, int lineCount)
1428
+ {
1429
+ char msg[MAXMSG+1];
1430
+ if ( errcode >= INP_ERR_LAST || errcode <= INP_ERR_FIRST )
1431
+ {
1432
+ sprintf(msg, "Error Code = %d", errcode);
1433
+ }
1434
+ else
1435
+ {
1436
+ sprintf(msg, "%s at line %d of %s] section:",
1437
+ InpErrorTxt[errcode-INP_ERR_FIRST], lineCount, sect);
1438
+ }
1439
+ ENwriteline("");
1440
+ ENwriteline(msg);
1441
+ ENwriteline(line);
1442
+ }
1443
+
1444
+ //=============================================================================
1445
+
1446
+ int checkCyclicTerms()
1447
+ /*
1448
+ ** Purpose:
1449
+ ** checks for cyclic references in Term expressions (e.g., T1 = T2 + T3
1450
+ ** and T3 = T2/T1)
1451
+ **
1452
+ ** Input:
1453
+ ** none
1454
+ **
1455
+ ** Returns:
1456
+ ** 1 if cyclic reference found or 0 if none found.
1457
+ */
1458
+ {
1459
+ int i, j, n;
1460
+ char msg[MAXMSG+1];
1461
+
1462
+ n = MSX.Nobjects[TERM];
1463
+ for (i=1; i<n; i++)
1464
+ {
1465
+ for (j=1; j<=n; j++) TermArray[0][j] = 0.0;
1466
+ if ( traceTermPath(i, i, n) )
1467
+ {
1468
+ sprintf(msg, "Error 410 - term %s contains a cyclic reference.",
1469
+ MSX.Term[i].id);
1470
+ ENwriteline(msg);
1471
+ return 1;
1472
+ }
1473
+ }
1474
+ return 0;
1475
+ }
1476
+
1477
+ //=============================================================================
1478
+
1479
+ int traceTermPath(int i, int istar, int n)
1480
+ /*
1481
+ ** Purpose:
1482
+ ** checks if Term[istar] is in the path of terms that appear when evaluating
1483
+ ** Term[i]
1484
+ **
1485
+ ** Input:
1486
+ ** i = index of term whose expressions are to be traced
1487
+ ** istar = index of term being searched for
1488
+ ** n = total number of terms
1489
+ **
1490
+ ** Returns:
1491
+ ** 1 if term istar found; 0 if not found.
1492
+ */
1493
+ {
1494
+ int j;
1495
+ if ( TermArray[0][i] == 1.0 ) return 0;
1496
+ TermArray[0][i] = 1.0;
1497
+ for (j=1; j<=n; j++)
1498
+ {
1499
+ if ( TermArray[i][j] == 0.0 ) continue;
1500
+ if ( j == istar ) return 1;
1501
+ else if ( traceTermPath(j, istar, n) ) return 1;
1502
+ }
1503
+ return 0;
1504
+ }