xref: /petsc/src/snes/tutorials/network/water/waterreaddata.c (revision a69119a591a03a9d906b29c0a4e9802e4d7c9795)
1 #include "water.h"
2 #include <string.h>
3 #include <ctype.h>
4 
5 PetscErrorCode PumpHeadCurveResidual(SNES snes, Vec X, Vec F, void *ctx) {
6   const PetscScalar *x;
7   PetscScalar       *f;
8   Pump              *pump = (Pump *)ctx;
9   PetscScalar       *head = pump->headcurve.head, *flow = pump->headcurve.flow;
10   PetscInt           i;
11 
12   PetscFunctionBegin;
13   PetscCall(VecGetArrayRead(X, &x));
14   PetscCall(VecGetArray(F, &f));
15 
16   f[0] = f[1] = f[2] = 0;
17   for (i = 0; i < pump->headcurve.npt; i++) {
18     f[0] += x[0] - x[1] * PetscPowScalar(flow[i], x[2]) - head[i];                                                          /* Partial w.r.t x[0] */
19     f[1] += (x[0] - x[1] * PetscPowScalar(flow[i], x[2]) - head[i]) * -1 * PetscPowScalar(flow[i], x[2]);                   /*Partial w.r.t x[1] */
20     f[2] += (x[0] - x[1] * PetscPowScalar(flow[i], x[2]) - head[i]) * -1 * x[1] * x[2] * PetscPowScalar(flow[i], x[2] - 1); /*Partial w.r.t x[2] */
21   }
22 
23   PetscCall(VecRestoreArrayRead(X, &x));
24   PetscCall(VecRestoreArray(F, &f));
25 
26   PetscFunctionReturn(0);
27 }
28 
29 PetscErrorCode SetPumpHeadCurveParams(Pump *pump) {
30   SNES                snes;
31   Vec                 X, F;
32   PetscScalar        *head, *flow, *x;
33   SNESConvergedReason reason;
34 
35   PetscFunctionBegin;
36   head = pump->headcurve.head;
37   flow = pump->headcurve.flow;
38   if (pump->headcurve.npt == 1) {
39     /* Single point head curve, set the other two data points */
40     flow[1] = 0;
41     head[1] = 1.33 * head[0]; /* 133% of design head -- From EPANET manual */
42     flow[2] = 2 * flow[0];    /* 200% of design flow -- From EPANET manual */
43     head[2] = 0;
44     pump->headcurve.npt += 2;
45   }
46 
47   PetscCall(SNESCreate(PETSC_COMM_SELF, &snes));
48 
49   PetscCall(VecCreate(PETSC_COMM_SELF, &X));
50   PetscCall(VecSetSizes(X, 3, 3));
51   PetscCall(VecSetFromOptions(X));
52   PetscCall(VecDuplicate(X, &F));
53 
54   PetscCall(SNESSetFunction(snes, F, PumpHeadCurveResidual, (void *)pump));
55   PetscCall(SNESSetJacobian(snes, NULL, NULL, SNESComputeJacobianDefault, NULL));
56   PetscCall(SNESSetFromOptions(snes));
57 
58   PetscCall(VecGetArray(X, &x));
59   x[0] = head[1];
60   x[1] = 10;
61   x[2] = 3;
62   PetscCall(VecRestoreArray(X, &x));
63 
64   PetscCall(SNESSolve(snes, NULL, X));
65 
66   PetscCall(SNESGetConvergedReason(snes, &reason));
67   PetscCheck(reason >= 0, PETSC_COMM_SELF, PETSC_ERR_CONV_FAILED, "Pump head curve did not converge");
68 
69   PetscCall(VecGetArray(X, &x));
70   pump->h0 = x[0];
71   pump->r  = x[1];
72   pump->n  = x[2];
73   PetscCall(VecRestoreArray(X, &x));
74 
75   PetscCall(VecDestroy(&X));
76   PetscCall(VecDestroy(&F));
77   PetscCall(SNESDestroy(&snes));
78   PetscFunctionReturn(0);
79 }
80 
81 int LineStartsWith(const char *a, const char *b) {
82   if (strncmp(a, b, strlen(b)) == 0) return 1;
83   return 0;
84 }
85 
86 int CheckDataSegmentEnd(const char *line) {
87   if (LineStartsWith(line, "[JUNCTIONS]") || LineStartsWith(line, "[RESERVOIRS]") || LineStartsWith(line, "[TANKS]") || LineStartsWith(line, "[PIPES]") || LineStartsWith(line, "[PUMPS]") || LineStartsWith(line, "[CURVES]") || LineStartsWith(line, "[VALVES]") || LineStartsWith(line, "[PATTERNS]") || LineStartsWith(line, "[VALVES]") || LineStartsWith(line, "[QUALITY]") || LineStartsWith(line, "\n") || LineStartsWith(line, "\r\n")) {
88     return 1;
89   }
90   return 0;
91 }
92 
93 /* Gets the file pointer positiion for the start of the data segment and the
94    number of data segments (lines) read
95 */
96 PetscErrorCode GetDataSegment(FILE *fp, char *line, fpos_t *data_segment_start_pos, PetscInt *ndatalines) {
97   PetscInt data_segment_end;
98   PetscInt nlines = 0;
99 
100   PetscFunctionBegin;
101   data_segment_end = 0;
102   fgetpos(fp, data_segment_start_pos);
103   PetscCheck(fgets(line, MAXLINE, fp), PETSC_COMM_SELF, PETSC_ERR_FILE_READ, "Cannot read data segment from file");
104   while (LineStartsWith(line, ";")) {
105     fgetpos(fp, data_segment_start_pos);
106     PetscCheck(fgets(line, MAXLINE, fp), PETSC_COMM_SELF, PETSC_ERR_FILE_READ, "Cannot read data segment from file");
107   }
108   while (!data_segment_end) {
109     PetscCheck(fgets(line, MAXLINE, fp), PETSC_COMM_SELF, PETSC_ERR_FILE_READ, "Cannot read data segment from file");
110     nlines++;
111     data_segment_end = CheckDataSegmentEnd(line);
112   }
113   *ndatalines = nlines;
114   PetscFunctionReturn(0);
115 }
116 
117 PetscErrorCode WaterReadData(WATERDATA *water, char *filename) {
118   FILE        *fp = NULL;
119   VERTEX_Water vert;
120   EDGE_Water   edge;
121   fpos_t       junc_start_pos, res_start_pos, tank_start_pos, pipe_start_pos, pump_start_pos;
122   fpos_t       curve_start_pos, title_start_pos;
123   char         line[MAXLINE];
124   PetscInt     i, j, nv = 0, ne = 0, ncurve = 0, ntitle = 0, nlines, ndata, curve_id;
125   Junction    *junction  = NULL;
126   Reservoir   *reservoir = NULL;
127   Tank        *tank      = NULL;
128   Pipe        *pipe      = NULL;
129   Pump        *pump      = NULL;
130   PetscScalar  curve_x, curve_y;
131   double       v1, v2, v3, v4, v5, v6;
132 
133   PetscFunctionBegin;
134   water->nvertex = water->nedge = 0;
135   fp                            = fopen(filename, "rb");
136   /* Check for valid file */
137   PetscCheck(fp, PETSC_COMM_SELF, PETSC_ERR_FILE_OPEN, "Can't open EPANET data file %s", filename);
138 
139   /* Read file and get line numbers for different data segments */
140   while (fgets(line, MAXLINE, fp)) {
141     if (strstr(line, "[TITLE]")) GetDataSegment(fp, line, &title_start_pos, &ntitle);
142 
143     if (strstr(line, "[JUNCTIONS]")) {
144       GetDataSegment(fp, line, &junc_start_pos, &nlines);
145       water->nvertex += nlines;
146       water->njunction = nlines;
147     }
148 
149     if (strstr(line, "[RESERVOIRS]")) {
150       GetDataSegment(fp, line, &res_start_pos, &nlines);
151       water->nvertex += nlines;
152       water->nreservoir = nlines;
153     }
154 
155     if (strstr(line, "[TANKS]")) {
156       GetDataSegment(fp, line, &tank_start_pos, &nlines);
157       water->nvertex += nlines;
158       water->ntank = nlines;
159     }
160 
161     if (strstr(line, "[PIPES]")) {
162       GetDataSegment(fp, line, &pipe_start_pos, &nlines);
163       water->nedge += nlines;
164       water->npipe = nlines;
165     }
166 
167     if (strstr(line, "[PUMPS]")) {
168       GetDataSegment(fp, line, &pump_start_pos, &nlines);
169       water->nedge += nlines;
170       water->npump = nlines;
171     }
172 
173     if (strstr(line, "[CURVES]")) GetDataSegment(fp, line, &curve_start_pos, &ncurve);
174   }
175 
176   /* Allocate vertex and edge data structs */
177   PetscCall(PetscCalloc1(water->nvertex, &water->vertex));
178   PetscCall(PetscCalloc1(water->nedge, &water->edge));
179   vert = water->vertex;
180   edge = water->edge;
181 
182   /* Junctions */
183   fsetpos(fp, &junc_start_pos);
184   for (i = 0; i < water->njunction; i++) {
185     int id = 0, pattern = 0;
186     PetscCheck(fgets(line, MAXLINE, fp), PETSC_COMM_SELF, PETSC_ERR_FILE_READ, "Cannot read junction from file");
187     vert[nv].type = VERTEX_TYPE_JUNCTION;
188     junction      = &vert[nv].junc;
189     ndata         = sscanf(line, "%d %lf %lf %d", &id, &v1, &v2, &pattern);
190     PetscCheck(ndata >= 3, PETSC_COMM_SELF, PETSC_ERR_FILE_READ, "Unable to read junction data");
191     vert[nv].id          = id;
192     junction->dempattern = pattern;
193     junction->elev       = (PetscScalar)v1;
194     junction->demand     = (PetscScalar)v2;
195     junction->demand *= GPM_CFS;
196     junction->id = vert[nv].id;
197     nv++;
198   }
199 
200   /* Reservoirs */
201   fsetpos(fp, &res_start_pos);
202   for (i = 0; i < water->nreservoir; i++) {
203     int id = 0, pattern = 0;
204     PetscCheck(fgets(line, MAXLINE, fp), PETSC_COMM_SELF, PETSC_ERR_FILE_READ, "Cannot read reservoir from file");
205     vert[nv].type = VERTEX_TYPE_RESERVOIR;
206     reservoir     = &vert[nv].res;
207     ndata         = sscanf(line, "%d %lf %d", &id, &v1, &pattern);
208     PetscCheck(ndata >= 2, PETSC_COMM_SELF, PETSC_ERR_FILE_READ, "Unable to read reservoir data");
209     vert[nv].id            = id;
210     reservoir->headpattern = pattern;
211     reservoir->head        = (PetscScalar)v1;
212     reservoir->id          = vert[nv].id;
213     nv++;
214   }
215 
216   /* Tanks */
217   fsetpos(fp, &tank_start_pos);
218   for (i = 0; i < water->ntank; i++) {
219     int id = 0, curve = 0;
220     PetscCheck(fgets(line, MAXLINE, fp), PETSC_COMM_SELF, PETSC_ERR_FILE_READ, "Cannot read data tank from file");
221     vert[nv].type = VERTEX_TYPE_TANK;
222     tank          = &vert[nv].tank;
223     ndata         = sscanf(line, "%d %lf %lf %lf %lf %lf %lf %d", &id, &v1, &v2, &v3, &v4, &v5, &v6, &curve);
224     PetscCheck(ndata >= 7, PETSC_COMM_SELF, PETSC_ERR_FILE_READ, "Unable to read tank data");
225     vert[nv].id       = id;
226     tank->volumecurve = curve;
227     tank->elev        = (PetscScalar)v1;
228     tank->initlvl     = (PetscScalar)v2;
229     tank->minlvl      = (PetscScalar)v3;
230     tank->maxlvl      = (PetscScalar)v4;
231     tank->diam        = (PetscScalar)v5;
232     tank->minvolume   = (PetscScalar)v6;
233     tank->id          = vert[nv].id;
234     nv++;
235   }
236 
237   /* Pipes */
238   fsetpos(fp, &pipe_start_pos);
239   for (i = 0; i < water->npipe; i++) {
240     int id = 0, node1 = 0, node2 = 0;
241     PetscCheck(fgets(line, MAXLINE, fp), PETSC_COMM_SELF, PETSC_ERR_FILE_READ, "Cannot read data pipe from file");
242     edge[ne].type   = EDGE_TYPE_PIPE;
243     pipe            = &edge[ne].pipe;
244     ndata           = sscanf(line, "%d %d %d %lf %lf %lf %lf %s", &id, &node1, &node2, &v1, &v2, &v3, &v4, pipe->stat);
245     pipe->id        = id;
246     pipe->node1     = node1;
247     pipe->node2     = node2;
248     pipe->length    = (PetscScalar)v1;
249     pipe->diam      = (PetscScalar)v2;
250     pipe->roughness = (PetscScalar)v3;
251     pipe->minorloss = (PetscScalar)v4;
252     edge[ne].id     = pipe->id;
253     if (strcmp(pipe->stat, "OPEN") == 0) pipe->status = PIPE_STATUS_OPEN;
254     if (ndata < 8) {
255       strcpy(pipe->stat, "OPEN"); /* default OPEN */
256       pipe->status = PIPE_STATUS_OPEN;
257     }
258     if (ndata < 7) pipe->minorloss = 0.;
259     pipe->n = 1.85;
260     pipe->k = 4.72 * pipe->length / (PetscPowScalar(pipe->roughness, pipe->n) * PetscPowScalar(0.0833333 * pipe->diam, 4.87));
261     ne++;
262   }
263 
264   /* Pumps */
265   fsetpos(fp, &pump_start_pos);
266   for (i = 0; i < water->npump; i++) {
267     int id = 0, node1 = 0, node2 = 0, paramid = 0;
268     PetscCheck(fgets(line, MAXLINE, fp), PETSC_COMM_SELF, PETSC_ERR_FILE_READ, "Cannot read data pump from file");
269     edge[ne].type = EDGE_TYPE_PUMP;
270     pump          = &edge[ne].pump;
271     ndata         = sscanf(line, "%d %d %d %s %d", &id, &node1, &node2, pump->param, &paramid);
272     PetscCheck(ndata == 5, PETSC_COMM_SELF, PETSC_ERR_FILE_READ, "Unable to read pump data");
273     pump->id      = id;
274     pump->node1   = node1;
275     pump->node2   = node2;
276     pump->paramid = paramid;
277     edge[ne].id   = pump->id;
278     ne++;
279   }
280 
281   /* Curves */
282   fsetpos(fp, &curve_start_pos);
283   for (i = 0; i < ncurve; i++) {
284     int icurve_id = 0;
285     PetscCheck(fgets(line, MAXLINE, fp), PETSC_COMM_SELF, PETSC_ERR_FILE_READ, "Cannot read data curve from file");
286     ndata = sscanf(line, "%d %lf %lf", &icurve_id, &v1, &v2);
287     PetscCheck(ndata == 3, PETSC_COMM_SELF, PETSC_ERR_FILE_READ, "Unable to read curve data");
288     curve_id = icurve_id;
289     curve_x  = (PetscScalar)v1;
290     curve_y  = (PetscScalar)v2;
291     /* Check for pump with the curve_id */
292     for (j = water->npipe; j < water->npipe + water->npump; j++) {
293       if (water->edge[j].pump.paramid == curve_id) {
294         PetscCheck(pump->headcurve.npt != 3, PETSC_COMM_SELF, PETSC_ERR_SUP, "Pump %" PetscInt_FMT " [%" PetscInt_FMT " --> %" PetscInt_FMT "]: No support for more than 3-pt head-flow curve", pump->id, pump->node1, pump->node2);
295         pump                                      = &water->edge[j].pump;
296         pump->headcurve.flow[pump->headcurve.npt] = curve_x * GPM_CFS;
297         pump->headcurve.head[pump->headcurve.npt] = curve_y;
298         pump->headcurve.npt++;
299         break;
300       }
301     }
302   }
303 
304   fclose(fp);
305 
306   /* Get pump curve parameters */
307   for (j = water->npipe; j < water->npipe + water->npump; j++) {
308     pump = &water->edge[j].pump;
309     if (strcmp(pump->param, "HEAD") == 0) {
310       /* Head-flow curve */
311       PetscCall(SetPumpHeadCurveParams(pump));
312     }
313   }
314   PetscFunctionReturn(0);
315 }
316