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, ¶mid); 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