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