epanet-plus 0.0.1__cp313-cp313-musllinux_1_2_x86_64.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.

Potentially problematic release.


This version of epanet-plus might be problematic. Click here for more details.

Files changed (105) hide show
  1. docs/conf.py +67 -0
  2. epanet-msx-src/dispersion.h +27 -0
  3. epanet-msx-src/hash.c +107 -0
  4. epanet-msx-src/hash.h +28 -0
  5. epanet-msx-src/include/epanetmsx.h +104 -0
  6. epanet-msx-src/include/epanetmsx_export.h +42 -0
  7. epanet-msx-src/mathexpr.c +937 -0
  8. epanet-msx-src/mathexpr.h +39 -0
  9. epanet-msx-src/mempool.c +204 -0
  10. epanet-msx-src/mempool.h +24 -0
  11. epanet-msx-src/msxchem.c +1285 -0
  12. epanet-msx-src/msxcompiler.c +368 -0
  13. epanet-msx-src/msxdict.h +42 -0
  14. epanet-msx-src/msxdispersion.c +586 -0
  15. epanet-msx-src/msxerr.c +116 -0
  16. epanet-msx-src/msxfile.c +260 -0
  17. epanet-msx-src/msxfuncs.c +175 -0
  18. epanet-msx-src/msxfuncs.h +35 -0
  19. epanet-msx-src/msxinp.c +1504 -0
  20. epanet-msx-src/msxout.c +398 -0
  21. epanet-msx-src/msxproj.c +791 -0
  22. epanet-msx-src/msxqual.c +2011 -0
  23. epanet-msx-src/msxrpt.c +400 -0
  24. epanet-msx-src/msxtank.c +422 -0
  25. epanet-msx-src/msxtoolkit.c +1164 -0
  26. epanet-msx-src/msxtypes.h +551 -0
  27. epanet-msx-src/msxutils.c +524 -0
  28. epanet-msx-src/msxutils.h +56 -0
  29. epanet-msx-src/newton.c +158 -0
  30. epanet-msx-src/newton.h +34 -0
  31. epanet-msx-src/rk5.c +287 -0
  32. epanet-msx-src/rk5.h +39 -0
  33. epanet-msx-src/ros2.c +293 -0
  34. epanet-msx-src/ros2.h +35 -0
  35. epanet-msx-src/smatrix.c +816 -0
  36. epanet-msx-src/smatrix.h +29 -0
  37. epanet-src/AUTHORS +60 -0
  38. epanet-src/LICENSE +21 -0
  39. epanet-src/enumstxt.h +151 -0
  40. epanet-src/epanet.c +5937 -0
  41. epanet-src/epanet2.c +961 -0
  42. epanet-src/epanet2.def +131 -0
  43. epanet-src/errors.dat +79 -0
  44. epanet-src/flowbalance.c +186 -0
  45. epanet-src/funcs.h +219 -0
  46. epanet-src/genmmd.c +1000 -0
  47. epanet-src/hash.c +177 -0
  48. epanet-src/hash.h +28 -0
  49. epanet-src/hydcoeffs.c +1303 -0
  50. epanet-src/hydraul.c +1164 -0
  51. epanet-src/hydsolver.c +781 -0
  52. epanet-src/hydstatus.c +442 -0
  53. epanet-src/include/epanet2.h +466 -0
  54. epanet-src/include/epanet2_2.h +1962 -0
  55. epanet-src/include/epanet2_enums.h +518 -0
  56. epanet-src/inpfile.c +884 -0
  57. epanet-src/input1.c +672 -0
  58. epanet-src/input2.c +970 -0
  59. epanet-src/input3.c +2265 -0
  60. epanet-src/leakage.c +527 -0
  61. epanet-src/mempool.c +146 -0
  62. epanet-src/mempool.h +24 -0
  63. epanet-src/output.c +853 -0
  64. epanet-src/project.c +1691 -0
  65. epanet-src/quality.c +695 -0
  66. epanet-src/qualreact.c +800 -0
  67. epanet-src/qualroute.c +696 -0
  68. epanet-src/report.c +1559 -0
  69. epanet-src/rules.c +1500 -0
  70. epanet-src/smatrix.c +871 -0
  71. epanet-src/text.h +508 -0
  72. epanet-src/types.h +928 -0
  73. epanet-src/util/cstr_helper.c +59 -0
  74. epanet-src/util/cstr_helper.h +38 -0
  75. epanet-src/util/errormanager.c +92 -0
  76. epanet-src/util/errormanager.h +39 -0
  77. epanet-src/util/filemanager.c +212 -0
  78. epanet-src/util/filemanager.h +81 -0
  79. epanet-src/validate.c +408 -0
  80. epanet.cpython-313-x86_64-linux-musl.so +0 -0
  81. epanet_plus/VERSION +1 -0
  82. epanet_plus/__init__.py +8 -0
  83. epanet_plus/epanet_plus.c +118 -0
  84. epanet_plus/epanet_toolkit.py +2730 -0
  85. epanet_plus/epanet_wrapper.py +2414 -0
  86. epanet_plus/include/epanet_plus.h +9 -0
  87. epanet_plus-0.0.1.dist-info/METADATA +152 -0
  88. epanet_plus-0.0.1.dist-info/RECORD +105 -0
  89. epanet_plus-0.0.1.dist-info/WHEEL +5 -0
  90. epanet_plus-0.0.1.dist-info/licenses/LICENSE +21 -0
  91. epanet_plus-0.0.1.dist-info/top_level.txt +11 -0
  92. examples/basic_usage.py +35 -0
  93. python-extension/ext.c +344 -0
  94. python-extension/pyepanet.c +2133 -0
  95. python-extension/pyepanet.h +143 -0
  96. python-extension/pyepanet2.c +1823 -0
  97. python-extension/pyepanet2.h +141 -0
  98. python-extension/pyepanet_plus.c +37 -0
  99. python-extension/pyepanet_plus.h +4 -0
  100. python-extension/pyepanetmsx.c +388 -0
  101. python-extension/pyepanetmsx.h +35 -0
  102. tests/test_epanet.py +16 -0
  103. tests/test_epanetmsx.py +36 -0
  104. tests/test_epyt.py +114 -0
  105. tests/test_load_inp_from_buffer.py +18 -0
epanet-src/smatrix.c ADDED
@@ -0,0 +1,871 @@
1
+ /*
2
+ ******************************************************************************
3
+ Project: OWA EPANET
4
+ Version: 2.3
5
+ Module: smatrix.c
6
+ Description: solves a sparse set of linear equations
7
+ Authors: see AUTHORS
8
+ Copyright: see AUTHORS
9
+ License: see LICENSE
10
+ Last Updated: 05/15/2019
11
+ ******************************************************************************
12
+ */
13
+ /*
14
+ This module contains the sparse matrix routines used to solve a network's
15
+ hydraulic equations. The functions exported by this module are:
16
+ createsparse() -- called from openhyd() in HYDRAUL.C
17
+ freesparse() -- called from closehyd() in HYDRAUL.C
18
+ linsolve() -- called from netsolve() in HYDRAUL.C
19
+ */
20
+
21
+ #include <stdlib.h>
22
+ #include <stdio.h>
23
+ #include <string.h>
24
+ #include <math.h>
25
+ #include <limits.h>
26
+
27
+ #include <time.h> //For optional timer macros
28
+
29
+ #include "text.h"
30
+ #include "types.h"
31
+ #include "funcs.h"
32
+
33
+ // The multiple minimum degree re-ordering routine (see genmmd.c)
34
+ extern int genmmd(int *neqns, int *xadj, int *adjncy, int *invp, int *perm,
35
+ int *delta, int *dhead, int *qsize, int *llist, int *marker,
36
+ int *maxint, int *nofsub);
37
+
38
+ // Exported functions
39
+ int createsparse(Project *);
40
+ void freesparse(Project *);
41
+ int linsolve(Smatrix *, int);
42
+
43
+ // Local functions
44
+ static int allocsmatrix(Smatrix *, int, int);
45
+ static int alloclinsolve(Smatrix *, int);
46
+ static int localadjlists(Network *, Smatrix *);
47
+ static int paralink(Network *, Smatrix *, int, int, int k);
48
+ static void xparalinks(Network *);
49
+ static int reordernodes(Project *);
50
+ static int factorize(Project *);
51
+ static int growlist(Project *, int, int *);
52
+ static int newlink(Project *, Padjlist, int *);
53
+ static int linked(Network *, int, int);
54
+ static int addlink(Network *, int, int, int);
55
+ static int storesparse(Project *, int);
56
+ static int sortsparse(Smatrix *, int);
57
+ static void transpose(int, int *, int *, int *, int *,
58
+ int *, int *, int *);
59
+
60
+
61
+ /*************************************************************************
62
+ * Timer macros
63
+ **************************************************************************/
64
+ //#define cleartimer(tmr) (tmr = 0.0)
65
+ //#define starttimer(tmr) (tmr -= ((double) clock()/CLOCKS_PER_SEC));
66
+ //#define stoptimer(tmr) (tmr += ((double) clock()/CLOCKS_PER_SEC));
67
+ //#define gettimer(tmr) (tmr)
68
+
69
+
70
+ /*************************************************************************
71
+ * The following data type implements a timer
72
+ **************************************************************************/
73
+ // typedef double timer;
74
+ // timer SmatrixTimer;
75
+
76
+
77
+ int createsparse(Project *pr)
78
+ /*
79
+ **--------------------------------------------------------------
80
+ ** Input: none
81
+ ** Output: returns error code
82
+ ** Purpose: creates sparse representation of coeff. matrix
83
+ **--------------------------------------------------------------
84
+ */
85
+ {
86
+ Network *net = &pr->network;
87
+ Smatrix *sm = &pr->hydraul.smatrix;
88
+
89
+ int errcode = 0;
90
+
91
+ // cleartimer(SmatrixTimer);
92
+ // starttimer(SmatrixTimer);
93
+
94
+ // Allocate sparse matrix data structures
95
+ errcode = allocsmatrix(sm, net->Nnodes, net->Nlinks);
96
+ if (errcode) return errcode;
97
+
98
+ // Build a local version of node-link adjacency lists
99
+ // with parallel links removed
100
+ errcode = localadjlists(net, sm);
101
+ if (errcode) return errcode;
102
+
103
+ // Re-order nodes to minimize number of non-zero coeffs.
104
+ // in factorized solution matrix
105
+ ERRCODE(reordernodes(pr));
106
+
107
+ // Factorize solution matrix by updating adjacency lists
108
+ // with non-zero connections due to fill-ins
109
+ sm->Ncoeffs = net->Nlinks;
110
+ ERRCODE(factorize(pr));
111
+
112
+ // Allocate memory for sparse storage of positions of non-zero
113
+ // coeffs. and store these positions in vector NZSUB
114
+ ERRCODE(storesparse(pr, net->Njuncs));
115
+
116
+ // Free memory used for local adjacency lists and sort
117
+ // row indexes in NZSUB to optimize linsolve()
118
+ freeadjlists(net);
119
+ ERRCODE(sortsparse(sm, net->Njuncs));
120
+
121
+ // Allocate memory used by linear eqn. solver
122
+ ERRCODE(alloclinsolve(sm, net->Nnodes));
123
+
124
+ // Re-build adjacency lists for future use
125
+ ERRCODE(buildadjlists(net));
126
+ return errcode;
127
+ }
128
+
129
+
130
+ int allocsmatrix(Smatrix *sm, int Nnodes, int Nlinks)
131
+ /*
132
+ **--------------------------------------------------------------
133
+ ** Input: none
134
+ ** Output: returns error code
135
+ ** Purpose: allocates memory for representing a sparse matrix
136
+ **--------------------------------------------------------------
137
+ */
138
+ {
139
+ int errcode = 0;
140
+
141
+ // Memory for linear eqn. solver allocated in alloclinsolve().
142
+ sm->Aij = NULL;
143
+ sm->Aii = NULL;
144
+ sm->F = NULL;
145
+ sm->temp = NULL;
146
+ sm->link = NULL;
147
+ sm->first = NULL;
148
+
149
+ // Memory for representing sparse matrix data structure
150
+ sm->Order = (int *) calloc(Nnodes+1, sizeof(int));
151
+ sm->Row = (int *) calloc(Nnodes+1, sizeof(int));
152
+ sm->Ndx = (int *) calloc(Nlinks+1, sizeof(int));
153
+ ERRCODE(MEMCHECK(sm->Order));
154
+ ERRCODE(MEMCHECK(sm->Row));
155
+ ERRCODE(MEMCHECK(sm->Ndx));
156
+ return errcode;
157
+ }
158
+
159
+
160
+ int alloclinsolve(Smatrix *sm, int n)
161
+ /*
162
+ **--------------------------------------------------------------
163
+ ** Input: none
164
+ ** Output: returns error code
165
+ ** Purpose: allocates memory used by linear eqn. solver.
166
+ **--------------------------------------------------------------
167
+ */
168
+ {
169
+ int errcode = 0;
170
+ n = n + 1; // All arrays are 1-based
171
+
172
+ sm->Aij = (double *)calloc(sm->Ncoeffs + 1, sizeof(double));
173
+ sm->Aii = (double *)calloc(n, sizeof(double));
174
+ sm->F = (double *)calloc(n, sizeof(double));
175
+ sm->temp = (double *)calloc(n, sizeof(double));
176
+ sm->link = (int *)calloc(n, sizeof(int));
177
+ sm->first = (int *)calloc(n, sizeof(int));
178
+ ERRCODE(MEMCHECK(sm->Aij));
179
+ ERRCODE(MEMCHECK(sm->Aii));
180
+ ERRCODE(MEMCHECK(sm->F));
181
+ ERRCODE(MEMCHECK(sm->temp));
182
+ ERRCODE(MEMCHECK(sm->link));
183
+ ERRCODE(MEMCHECK(sm->first));
184
+ return errcode;
185
+ }
186
+
187
+
188
+ void freesparse(Project *pr)
189
+ /*
190
+ **----------------------------------------------------------------
191
+ ** Input: None
192
+ ** Output: None
193
+ ** Purpose: Frees memory used for sparse matrix storage
194
+ **----------------------------------------------------------------
195
+ */
196
+ {
197
+ Smatrix *sm = &pr->hydraul.smatrix;
198
+
199
+ // stoptimer(SmatrixTimer);
200
+ // printf("\n");
201
+ // printf("\n Processing Time = %7.3f s", gettimer(SmatrixTimer));
202
+ // printf("\n");
203
+
204
+ FREE(sm->Order);
205
+ FREE(sm->Row);
206
+ FREE(sm->Ndx);
207
+ FREE(sm->XLNZ);
208
+ FREE(sm->NZSUB);
209
+ FREE(sm->LNZ);
210
+
211
+ FREE(sm->Aij);
212
+ FREE(sm->Aii);
213
+ FREE(sm->F);
214
+ FREE(sm->temp);
215
+ FREE(sm->link);
216
+ FREE(sm->first);
217
+ }
218
+
219
+
220
+ int localadjlists(Network *net, Smatrix *sm)
221
+ /*
222
+ **--------------------------------------------------------------
223
+ ** Input: none
224
+ ** Output: returns error code
225
+ ** Purpose: builds linked list of non-parallel links adjacent to each node
226
+ **--------------------------------------------------------------
227
+ */
228
+ {
229
+ int i, j, k;
230
+ int pmark = 0; // parallel link marker
231
+ int errcode = 0;
232
+ Padjlist alink;
233
+
234
+ // Create an array of adjacency lists
235
+ freeadjlists(net);
236
+ net->Adjlist = (Padjlist *)calloc(net->Nnodes + 1, sizeof(Padjlist));
237
+ if (net->Adjlist == NULL) return 101;
238
+
239
+ // For each link, update adjacency lists of its end nodes
240
+ for (k = 1; k <= net->Nlinks; k++)
241
+ {
242
+ i = net->Link[k].N1;
243
+ j = net->Link[k].N2;
244
+ pmark = paralink(net, sm, i, j, k); // Parallel link check
245
+
246
+ // Include link in start node i's list
247
+ alink = (struct Sadjlist *) malloc(sizeof(struct Sadjlist));
248
+ if (alink == NULL) return(101);
249
+ if (!pmark) alink->node = j;
250
+ else alink->node = 0; // Parallel link marker
251
+ alink->link = k;
252
+ alink->next = net->Adjlist[i];
253
+ net->Adjlist[i] = alink;
254
+
255
+ // Include link in end node j's list
256
+ alink = (struct Sadjlist *) malloc(sizeof(struct Sadjlist));
257
+ if (alink == NULL) return(101);
258
+ if (!pmark) alink->node = i;
259
+ else alink->node = 0; // Parallel link marker
260
+ alink->link = k;
261
+ alink->next = net->Adjlist[j];
262
+ net->Adjlist[j] = alink;
263
+ }
264
+
265
+ // Remove parallel links from adjacency lists
266
+ xparalinks(net);
267
+ return errcode;
268
+ }
269
+
270
+
271
+ int paralink(Network *net, Smatrix *sm, int i, int j, int k)
272
+ /*
273
+ **--------------------------------------------------------------
274
+ ** Input: i = index of start node of link
275
+ ** j = index of end node of link
276
+ ** k = link index
277
+ ** Output: returns 1 if link k parallels another link, else 0
278
+ ** Purpose: checks for parallel links between nodes i and j
279
+ **
280
+ **--------------------------------------------------------------
281
+ */
282
+ {
283
+ Padjlist alink;
284
+ for (alink = net->Adjlist[i]; alink != NULL; alink = alink->next)
285
+ {
286
+ // Link || to k (same end nodes)
287
+ if (alink->node == j)
288
+ {
289
+ // Assign Ndx entry to this link
290
+ sm->Ndx[k] = alink->link;
291
+ return(1);
292
+ }
293
+ }
294
+ // Ndx entry if link not parallel
295
+ sm->Ndx[k] = k;
296
+ return(0);
297
+ }
298
+
299
+
300
+ void xparalinks(Network *net)
301
+ /*
302
+ **--------------------------------------------------------------
303
+ ** Input: none
304
+ ** Output: none
305
+ ** Purpose: removes parallel links from nodal adjacency lists
306
+ **--------------------------------------------------------------
307
+ */
308
+ {
309
+ int i;
310
+ Padjlist alink, // Current item in adjacency list
311
+ blink; // Previous item in adjacency list
312
+
313
+ // Scan adjacency list of each node
314
+ for (i = 1; i <= net->Nnodes; i++)
315
+ {
316
+ alink = net->Adjlist[i]; // First item in list
317
+ blink = NULL;
318
+ while (alink != NULL)
319
+ {
320
+ if (alink->node == 0) // Parallel link marker found
321
+ {
322
+ if (blink == NULL) // This holds at start of list
323
+ {
324
+ net->Adjlist[i] = alink->next;
325
+ free(alink); // Remove item from list
326
+ alink = net->Adjlist[i];
327
+ }
328
+ else // This holds for interior of list
329
+ {
330
+ blink->next = alink->next;
331
+ free(alink); // Remove item from list
332
+ alink = blink->next;
333
+ }
334
+ }
335
+ else
336
+ {
337
+ blink = alink; // Move to next item in list
338
+ alink = alink->next;
339
+ }
340
+ }
341
+ }
342
+ }
343
+
344
+
345
+ int reordernodes(Project *pr)
346
+ /*
347
+ **--------------------------------------------------------------
348
+ ** Input: none
349
+ ** Output: returns 1 if successful, 0 if not
350
+ ** Purpose: re-orders nodes to minimize # of non-zeros that
351
+ ** will appear in factorized solution matrix
352
+ **--------------------------------------------------------------
353
+ */
354
+ {
355
+ Network *net = &pr->network;
356
+ Smatrix *sm = &pr->hydraul.smatrix;
357
+
358
+ int k, knode, m, njuncs, nlinks;
359
+ int delta = -1;
360
+ int nofsub = 0;
361
+ int maxint = INT_MAX; //defined in limits.h
362
+ int errcode;
363
+ Padjlist alink;
364
+
365
+ // Local versions of node adjacency lists
366
+ int *adjncy = NULL;
367
+ int *xadj = NULL;
368
+
369
+ // Work arrays
370
+ int *dhead = NULL;
371
+ int *qsize = NULL;
372
+ int *llist = NULL;
373
+ int *marker = NULL;
374
+
375
+ // Default ordering
376
+ for (k = 1; k <= net->Nnodes; k++)
377
+ {
378
+ sm->Row[k] = k;
379
+ sm->Order[k] = k;
380
+ }
381
+ njuncs = net->Njuncs;
382
+ nlinks = net->Nlinks;
383
+
384
+ // Allocate memory
385
+ adjncy = (int *) calloc(2*nlinks+1, sizeof(int));
386
+ xadj = (int *) calloc(njuncs+2, sizeof(int));
387
+ dhead = (int *) calloc(njuncs+1, sizeof(int));
388
+ qsize = (int *) calloc(njuncs + 1, sizeof(int));
389
+ llist = (int *) calloc(njuncs + 1, sizeof(int));
390
+ marker = (int *) calloc(njuncs + 1, sizeof(int));
391
+ if (adjncy && xadj && dhead && qsize && llist && marker)
392
+ {
393
+ // Create local versions of node adjacency lists
394
+ xadj[1] = 1;
395
+ m = 1;
396
+ for (k = 1; k <= njuncs; k++)
397
+ {
398
+ for (alink = net->Adjlist[k]; alink != NULL; alink = alink->next)
399
+ {
400
+ knode = alink->node;
401
+ if (knode > 0 && knode <= njuncs)
402
+ {
403
+ adjncy[m] = knode;
404
+ m++;
405
+ }
406
+ }
407
+ xadj[k+1] = m;
408
+ }
409
+
410
+ // Generate a multiple minimum degree node re-ordering
411
+ genmmd(&njuncs, xadj, adjncy, sm->Row, sm->Order, &delta,
412
+ dhead, qsize, llist, marker, &maxint, &nofsub);
413
+ errcode = 0;
414
+ }
415
+ else errcode = 101; //insufficient memory
416
+
417
+ // Free memory
418
+ FREE(adjncy);
419
+ FREE(xadj);
420
+ FREE(dhead);
421
+ FREE(qsize);
422
+ FREE(llist);
423
+ FREE(marker);
424
+ return errcode;
425
+ }
426
+
427
+
428
+ int factorize(Project *pr)
429
+ /*
430
+ **--------------------------------------------------------------
431
+ ** Input: none
432
+ ** Output: returns error code
433
+ ** Purpose: symbolically factorizes the solution matrix in
434
+ ** terms of its adjacency lists
435
+ **--------------------------------------------------------------
436
+ */
437
+ {
438
+ Network *net = &pr->network;
439
+ Smatrix *sm = &pr->hydraul.smatrix;
440
+
441
+ int k, knode;
442
+ int errcode = 0;
443
+ Padjlist alink;
444
+
445
+ // Find degree of each junction node
446
+ int *degree = (int *)calloc(net->Nnodes + 1, sizeof(int));
447
+ if (degree == NULL) return 101;
448
+
449
+ // NOTE: For purposes of node re-ordering, Tanks (nodes with
450
+ // indexes above Njuncs) have zero degree of adjacency.
451
+
452
+ for (k = 1; k <= net->Njuncs; k++)
453
+ {
454
+ for (alink = net->Adjlist[k]; alink != NULL; alink = alink->next)
455
+ {
456
+ if (alink->node > 0) degree[k]++;
457
+ }
458
+ }
459
+
460
+ // Augment each junction's adjacency list to account for
461
+ // new connections created when solution matrix is solved.
462
+ // NOTE: Only junctions (indexes <= Njuncs) appear in solution matrix.
463
+ for (k = 1; k <= net->Njuncs; k++) // Examine each junction
464
+ {
465
+ knode = sm->Order[k]; // Re-ordered index
466
+ if (!growlist(pr, knode, degree)) // Augment adjacency list
467
+ {
468
+ errcode = 101;
469
+ break;
470
+ }
471
+ degree[knode] = 0; // In-activate node
472
+ }
473
+ free(degree);
474
+ return errcode;
475
+ }
476
+
477
+
478
+ int growlist(Project *pr, int knode, int *degree)
479
+ /*
480
+ **--------------------------------------------------------------
481
+ ** Input: knode = node index
482
+ ** Output: returns 1 if successful, 0 if not
483
+ ** Purpose: creates new entries in knode's adjacency list for
484
+ ** all unlinked pairs of active nodes that are
485
+ ** adjacent to knode
486
+ **--------------------------------------------------------------
487
+ */
488
+ {
489
+ Network *net = &pr->network;
490
+ Smatrix *sm = &pr->hydraul.smatrix;
491
+
492
+ int node;
493
+ Padjlist alink;
494
+
495
+ // Iterate through all nodes connected to knode
496
+ for (alink = net->Adjlist[knode]; alink != NULL; alink = alink -> next)
497
+ {
498
+ node = alink->node; // End node of connecting link
499
+ if (node > 0 && degree[node] > 0) // End node is active
500
+ {
501
+ degree[node]--; // Reduce degree of adjacency
502
+ if (!newlink(pr, alink, degree)) // Add to adjacency list
503
+ {
504
+ return 0;
505
+ }
506
+ }
507
+ }
508
+ return 1;
509
+ }
510
+
511
+
512
+ int newlink(Project *pr, Padjlist alink, int *degree)
513
+ /*
514
+ **--------------------------------------------------------------
515
+ ** Input: alink = element of node's adjacency list
516
+ ** Output: returns 1 if successful, 0 if not
517
+ ** Purpose: links end of current adjacent link to end nodes of
518
+ ** all links that follow it on adjacency list
519
+ **--------------------------------------------------------------
520
+ */
521
+ {
522
+ Network *net = &pr->network;
523
+ Smatrix *sm = &pr->hydraul.smatrix;
524
+
525
+ int inode, jnode;
526
+ Padjlist blink;
527
+
528
+ // Scan all entries in adjacency list that follow anode.
529
+ inode = alink->node; // End node of connection to anode
530
+ for (blink = alink->next; blink != NULL; blink = blink->next)
531
+ {
532
+ jnode = blink->node; // End node of next connection
533
+
534
+ // If jnode still active, and inode not connected to jnode,
535
+ // then add a new connection between inode and jnode.
536
+ if (jnode > 0 && degree[jnode] > 0) // jnode still active
537
+ {
538
+ if (!linked(net, inode, jnode)) // inode not linked to jnode
539
+ {
540
+ // Since new connection represents a non-zero coeff.
541
+ // in the solution matrix, update the coeff. count.
542
+ sm->Ncoeffs++;
543
+
544
+ // Update adjacency lists for inode & jnode to
545
+ // reflect the new connection.
546
+ if (!addlink(net, inode, jnode, sm->Ncoeffs)) return 0;
547
+ if (!addlink(net, jnode, inode, sm->Ncoeffs)) return 0;
548
+ degree[inode]++;
549
+ degree[jnode]++;
550
+ }
551
+ }
552
+ }
553
+ return 1;
554
+ }
555
+
556
+
557
+ int linked(Network *net, int i, int j)
558
+ /*
559
+ **--------------------------------------------------------------
560
+ ** Input: i = node index
561
+ ** j = node index
562
+ ** Output: returns 1 if nodes i and j are linked, 0 if not
563
+ ** Purpose: checks if nodes i and j are already linked.
564
+ **--------------------------------------------------------------
565
+ */
566
+ {
567
+ Padjlist alink;
568
+ for (alink = net->Adjlist[i]; alink != NULL; alink = alink->next)
569
+ {
570
+ if (alink->node == j) return 1;
571
+ }
572
+ return 0;
573
+ }
574
+
575
+
576
+ int addlink(Network *net, int i, int j, int n)
577
+ /*
578
+ **--------------------------------------------------------------
579
+ ** Input: i = node index
580
+ ** j = node index
581
+ ** n = link index
582
+ ** Output: returns 1 if successful, 0 if not
583
+ ** Purpose: augments node i's adjacency list with node j
584
+ **--------------------------------------------------------------
585
+ */
586
+ {
587
+ Padjlist alink;
588
+ alink = (struct Sadjlist *) malloc(sizeof(struct Sadjlist));
589
+ if (alink == NULL) return 0;
590
+ alink->node = j;
591
+ alink->link = n;
592
+ alink->next = net->Adjlist[i];
593
+ net->Adjlist[i] = alink;
594
+ return 1;
595
+ }
596
+
597
+
598
+ int storesparse(Project *pr, int n)
599
+ /*
600
+ **--------------------------------------------------------------
601
+ ** Input: n = number of rows in solution matrix
602
+ ** Output: returns error code
603
+ ** Purpose: stores row indexes of non-zeros of each column of
604
+ ** lower triangular portion of factorized matrix
605
+ **--------------------------------------------------------------
606
+ */
607
+ {
608
+ Network *net = &pr->network;
609
+ Smatrix *sm = &pr->hydraul.smatrix;
610
+
611
+ int i, ii, j, k, l, m;
612
+ int errcode = 0;
613
+ Padjlist alink;
614
+
615
+ // Allocate sparse matrix storage
616
+ sm->XLNZ = (int *) calloc(n+2, sizeof(int));
617
+ sm->NZSUB = (int *) calloc(sm->Ncoeffs+2, sizeof(int));
618
+ sm->LNZ = (int *) calloc(sm->Ncoeffs+2, sizeof(int));
619
+ ERRCODE(MEMCHECK(sm->XLNZ));
620
+ ERRCODE(MEMCHECK(sm->NZSUB));
621
+ ERRCODE(MEMCHECK(sm->LNZ));
622
+ if (errcode) return errcode;
623
+
624
+ // Generate row index pointers for each column of matrix
625
+ k = 0;
626
+ sm->XLNZ[1] = 1;
627
+ for (i = 1; i <= n; i++) // column
628
+ {
629
+ m = 0;
630
+ ii = sm->Order[i];
631
+ for (alink = net->Adjlist[ii]; alink != NULL; alink = alink->next)
632
+ {
633
+ if (alink->node == 0) continue;
634
+ j = sm->Row[alink->node]; // row
635
+ l = alink->link;
636
+ if (j > i && j <= n)
637
+ {
638
+ m++;
639
+ k++;
640
+ sm->NZSUB[k] = j;
641
+ sm->LNZ[k] = l;
642
+ }
643
+ }
644
+ sm->XLNZ[i+1] = sm->XLNZ[i] + m;
645
+ }
646
+ return errcode;
647
+ }
648
+
649
+
650
+ int sortsparse(Smatrix *sm, int n)
651
+ /*
652
+ **--------------------------------------------------------------
653
+ ** Input: n = number of rows in solution matrix
654
+ ** Output: returns error code
655
+ ** Purpose: puts row indexes in ascending order in NZSUB
656
+ **--------------------------------------------------------------
657
+ */
658
+ {
659
+ int i, k;
660
+ int *xlnzt, *nzsubt, *lnzt, *nzt;
661
+ int errcode = 0;
662
+
663
+ int *LNZ = sm->LNZ;
664
+ int *XLNZ = sm->XLNZ;
665
+ int *NZSUB = sm->NZSUB;
666
+
667
+ xlnzt = (int *) calloc(n+2, sizeof(int));
668
+ nzsubt = (int *) calloc(sm->Ncoeffs+2, sizeof(int));
669
+ lnzt = (int *) calloc(sm->Ncoeffs+2, sizeof(int));
670
+ nzt = (int *) calloc(n+2, sizeof(int));
671
+ ERRCODE(MEMCHECK(xlnzt));
672
+ ERRCODE(MEMCHECK(nzsubt));
673
+ ERRCODE(MEMCHECK(lnzt));
674
+ ERRCODE(MEMCHECK(nzt));
675
+ if (!errcode)
676
+ {
677
+ // Count # non-zeros in each row
678
+ for (i = 1; i <= n; i++) nzt[i] = 0;
679
+ for (i = 1; i <= n; i++)
680
+ {
681
+ for (k = XLNZ[i]; k < XLNZ[i+1]; k++) nzt[NZSUB[k]]++;
682
+ }
683
+ xlnzt[1] = 1;
684
+ for (i = 1; i <= n; i++) xlnzt[i+1] = xlnzt[i] + nzt[i];
685
+
686
+ // Transpose matrix twice to order column indexes
687
+ transpose(n, XLNZ, NZSUB, LNZ, xlnzt, nzsubt, lnzt, nzt);
688
+ transpose(n, xlnzt, nzsubt, lnzt, XLNZ, NZSUB, LNZ, nzt);
689
+ }
690
+
691
+ // Reclaim memory
692
+ free(xlnzt);
693
+ free(nzsubt);
694
+ free(lnzt);
695
+ free(nzt);
696
+ return errcode;
697
+ }
698
+
699
+
700
+ void transpose(int n, int *il, int *jl, int *xl, int *ilt, int *jlt,
701
+ int *xlt, int *nzt)
702
+ /*
703
+ **---------------------------------------------------------------------
704
+ ** Input: n = matrix order
705
+ ** il,jl,xl = sparse storage scheme for original matrix
706
+ ** nzt = work array
707
+ ** Output: ilt,jlt,xlt = sparse storage scheme for transposed matrix
708
+ ** Purpose: Determines sparse storage scheme for transpose of a matrix
709
+ **---------------------------------------------------------------------
710
+ */
711
+ {
712
+ int i, j, k, kk;
713
+
714
+ for (i = 1; i <= n; i++) nzt[i] = 0;
715
+ for (i = 1; i <= n; i++)
716
+ {
717
+ for (k = il[i]; k < il[i+1]; k++)
718
+ {
719
+ j = jl[k];
720
+ kk = ilt[j] + nzt[j];
721
+ jlt[kk] = i;
722
+ xlt[kk] = xl[k];
723
+ nzt[j]++;
724
+ }
725
+ }
726
+ }
727
+
728
+
729
+ int linsolve(Smatrix *sm, int n)
730
+ /*
731
+ **--------------------------------------------------------------
732
+ ** Input: sm = sparse matrix struct
733
+ n = number of equations
734
+ ** Output: sm->F = solution values
735
+ ** returns 0 if solution found, or index of
736
+ ** equation causing system to be ill-conditioned
737
+ ** Purpose: solves sparse symmetric system of linear
738
+ ** equations using Cholesky factorization
739
+ **
740
+ ** NOTE: This procedure assumes that the solution matrix has
741
+ ** been symbolically factorized with the positions of
742
+ ** the lower triangular, off-diagonal, non-zero coeffs.
743
+ ** stored in the following integer arrays:
744
+ ** XLNZ (start position of each column in NZSUB)
745
+ ** NZSUB (row index of each non-zero in each column)
746
+ ** LNZ (position of each NZSUB entry in Aij array)
747
+ **
748
+ ** This procedure has been adapted from subroutines GSFCT and
749
+ ** GSSLV in the book "Computer Solution of Large Sparse
750
+ ** Positive Definite Systems" by A. George and J. W-H Liu
751
+ ** (Prentice-Hall, 1981).
752
+ **--------------------------------------------------------------
753
+ */
754
+ {
755
+ double *Aii = sm->Aii;
756
+ double *Aij = sm->Aij;
757
+ double *B = sm->F;
758
+ double *temp = sm->temp;
759
+ int *LNZ = sm->LNZ;
760
+ int *XLNZ = sm->XLNZ;
761
+ int *NZSUB = sm->NZSUB;
762
+ int *link = sm->link;
763
+ int *first = sm->first;
764
+
765
+ int i, istop, istrt, isub, j, k, kfirst, newk;
766
+ double bj, diagj, ljk;
767
+
768
+ memset(temp, 0, (n + 1) * sizeof(double));
769
+ memset(link, 0, (n + 1) * sizeof(int));
770
+ memset(first, 0, (n + 1) * sizeof(int));
771
+
772
+ // Begin numerical factorization of matrix A into L
773
+ // Compute column L(*,j) for j = 1,...n
774
+ for (j = 1; j <= n; j++)
775
+ {
776
+ // For each column L(*,k) that affects L(*,j):
777
+ diagj = 0.0;
778
+ newk = link[j];
779
+ k = newk;
780
+ while (k != 0)
781
+ {
782
+ // Outer product modification of L(*,j) by
783
+ // L(*,k) starting at first[k] of L(*,k)
784
+ newk = link[k];
785
+ kfirst = first[k];
786
+ ljk = Aij[LNZ[kfirst]];
787
+ diagj += ljk*ljk;
788
+ istrt = kfirst + 1;
789
+ istop = XLNZ[k+1] - 1;
790
+ if (istop >= istrt)
791
+ {
792
+
793
+ // Before modification, update vectors 'first'
794
+ // and 'link' for future modification steps
795
+ first[k] = istrt;
796
+ isub = NZSUB[istrt];
797
+ link[k] = link[isub];
798
+ link[isub] = k;
799
+
800
+ // The actual mod is saved in vector 'temp'
801
+ for (i = istrt; i <= istop; i++)
802
+ {
803
+ isub = NZSUB[i];
804
+ temp[isub] += Aij[LNZ[i]]*ljk;
805
+ }
806
+ }
807
+ k = newk;
808
+ }
809
+
810
+ // Apply the modifications accumulated
811
+ // in 'temp' to column L(*,j)
812
+ diagj = Aii[j] - diagj;
813
+ if (diagj <= 0.0) // Check for ill-conditioning
814
+ {
815
+ return j;
816
+ }
817
+ diagj = sqrt(diagj);
818
+ Aii[j] = diagj;
819
+ istrt = XLNZ[j];
820
+ istop = XLNZ[j+1] - 1;
821
+ if (istop >= istrt)
822
+ {
823
+ first[j] = istrt;
824
+ isub = NZSUB[istrt];
825
+ link[j] = link[isub];
826
+ link[isub] = j;
827
+ for (i = istrt; i <= istop; i++)
828
+ {
829
+ isub = NZSUB[i];
830
+ bj = (Aij[LNZ[i]] - temp[isub])/diagj;
831
+ Aij[LNZ[i]] = bj;
832
+ temp[isub] = 0.0;
833
+ }
834
+ }
835
+ } // next j
836
+
837
+ // Forward substitution
838
+ for (j = 1; j <= n; j++)
839
+ {
840
+ bj = B[j]/Aii[j];
841
+ B[j] = bj;
842
+ istrt = XLNZ[j];
843
+ istop = XLNZ[j+1] - 1;
844
+ if (istop >= istrt)
845
+ {
846
+ for (i = istrt; i <= istop; i++)
847
+ {
848
+ isub = NZSUB[i];
849
+ B[isub] -= Aij[LNZ[i]]*bj;
850
+ }
851
+ }
852
+ }
853
+
854
+ // Backward substitution
855
+ for (j = n; j >= 1; j--)
856
+ {
857
+ bj = B[j];
858
+ istrt = XLNZ[j];
859
+ istop = XLNZ[j+1] - 1;
860
+ if (istop >= istrt)
861
+ {
862
+ for (i = istrt; i <= istop; i++)
863
+ {
864
+ isub = NZSUB[i];
865
+ bj -= Aij[LNZ[i]]*B[isub];
866
+ }
867
+ }
868
+ B[j] = bj/Aii[j];
869
+ }
870
+ return 0;
871
+ }