EPANET中读取INPUT文件的函数文件——INPUT3.C
1 /* 2 ********************************************************************** 3 4 INPUT3.C -- Input data parser for EPANET; 5 6 VERSION: 2.00 7 DATE: 5/30/00 8 9/7/00 9 10/25/00 10 3/1/01 11 6/24/02 12 8/15/07 (2.00.11) 13 2/14/08 (2.00.12) 14 AUTHOR: L. Rossman 15 US EPA - NRMRL 16 17 This module parses data from each line of input from file InFile. 18 All functions in this module are called from newline() in INPUT2.C. 19 该模块逐行解析INPUT文件。 20 该模块中的所有函数都在INPUT2.C的newline(int sect, char *line)中被调用。 21 22 同时该模块也调用了INPUT2.C中的部分工具函数:addlinkID(int n, char *id)、addnodeID(int n, char *id)、addpattern(char *id)、 23 addcurve(char *id)、*findID(char *id, STmplist *list)、match(char *str, char *substr)、hour(char *time, char *units)等 24 25 ********************************************************************** 26 */ 27 28 #include <stdlib.h> 29 #include <stdio.h> 30 #include <string.h> 31 #include <malloc.h> 32 #include <math.h> 33 #include "hash.h" 34 #include "text.h" 35 #include "types.h" 36 #include "funcs.h" 37 #define EXTERN extern 38 #include "vars.h" 39 40 /* Defined in enumstxt.h in EPANET.C */ 41 extern char *MixTxt[]; 42 extern char *Fldname[]; //字段名称字符串数组 43 44 /* Defined in INPUT2.C */ 45 extern char *Tok[MAXTOKS]; /* Array of token strings ;每一输入行的项数组成的字符串数组*/ 46 extern STmplist *PrevPat; /* Pointer to pattern list element ;指向模式的最近一个指针*/ 47 extern STmplist *PrevCurve; /* Pointer to curve list element ;指向曲线的最近一个指针*/ 48 extern int Ntokens; /* Number of tokens in input line ;每一输入行的项数(项与项之间的分隔形式是:)*/ 49 50 51 int juncdata() 52 /* 53 **-------------------------------------------------------------- 54 ** Input: none ;输入:无 55 ** Output: returns error code ;输出:错误编码 56 ** Purpose: processes junction data ;作用:处理节点数据 57 ** Format: ;格式: 58 ** [JUNCTIONS] 59 ** id elev. (demand) (demand pattern) 数据项的列表 60 **-------------------------------------------------------------- 61 */ 62 { 63 int n, p = 0;//n代表待解析行中的数据项的个数;p代表该节点的用水模式链表中的index值; 64 double el,y = 0.0;//el表示该节点的高程值;y表示该节点的用水量值 65 Pdemand demand;//当该节点存在多个用水量及对应的用水模式曲线时,使用该变量保存 66 STmplist *pat; 67 68 /* Add new junction to data base ;增加一个新的节点数据*/ 69 n = Ntokens; 70 if (Nnodes == MaxNodes) return(200); 71 Njuncs++; 72 Nnodes++; 73 if (!addnodeID(Njuncs,Tok[0])) return(215); 74 75 /* Check for valid data ;检查该数据的正确性*/ 76 if (n < 2) return(201);//不存在高程信息 77 if (!getfloat(Tok[1],&el)) return(202);//高程信息数据类型非法 78 if (n >= 3 && !getfloat(Tok[2],&y)) return(202);//若需水量存在且需水量数据类型非法 79 if (n >= 4) 80 { 81 pat = findID(Tok[3],Patlist); 82 if (pat == NULL) return(205); 83 p = pat->i; 84 } 85 86 /* Save junction data ;将节点的高程等信息保存下来*/ 87 Node[Njuncs].El = el; 88 Node[Njuncs].C0 = 0.0; 89 Node[Njuncs].S = NULL; 90 Node[Njuncs].Ke = 0.0; 91 Node[Njuncs].Rpt = 0; 92 93 /* Create a new demand record ;一个节点有一个指向该节点所挂的需水量指针D,将读入的水量及模式挂接到D的链首*/ 94 /*** Updated 6/24/02 ***/ 95 if (n >= 3) 96 { 97 demand = (struct Sdemand *) malloc(sizeof(struct Sdemand)); 98 if (demand == NULL) return(101); 99 demand->Base = y; 100 demand->Pat = p; 101 demand->next = Node[Njuncs].D; 102 Node[Njuncs].D = demand; 103 D[Njuncs] = y;//全局变量中的节点实际需水量 104 } 105 else D[Njuncs] = MISSING; //标记该节点暂无以Junction节点中分配水量,如果Demand中有对该节点分水量,那么也标记为MISSING 106 /*** end of update ***/ 107 return(0); 108 } /* end of juncdata */ 109 110 111 int tankdata() 112 /* 113 **-------------------------------------------------------------- 114 ** Input: none ;输入:无 115 ** Output: returns error code ;输出:错误代码 116 ** Purpose: processes tank & reservoir data ;作用:处理水塔和水池(水库)数据 117 ** Format: ;格式: 118 ** [RESERVOIRS] 119 ** id elev (pattern) 120 ** [TANKS] 121 ** id elev (pattern) 122 ** id elev initlevel minlevel maxlevel diam (minvol vcurve) 123 **-------------------------------------------------------------- 124 */ 125 { 126 int i, /* Node index Node中的索引值*/ 127 n, /* # data items 该数据行的数据项数 */ 128 p = 0, /* Fixed grade time pattern index 时间模式中的索引值 */ 129 vcurve = 0; /* Volume curve index 容积曲线的索引值*/ 130 double el = 0.0, /* Elevation 高程值*/ 131 initlevel = 0.0, /* Initial level 初始水面高程*/ 132 minlevel = 0.0, /* Minimum level 最小水池高程*/ 133 maxlevel = 0.0, /* Maximum level 最大水池高程*/ 134 minvol = 0.0, /* Minimum volume 最小容积*/ 135 diam = 0.0, /* Diameter 口径*/ 136 area; /* X-sect. area 横截面面积*/ 137 STmplist *t; 138 139 /* Add new tank to data base 添加新的水塔数据*/ 140 n = Ntokens; 141 if (Ntanks == MaxTanks 142 || Nnodes == MaxNodes) return(200); 143 Ntanks++; 144 Nnodes++; 145 i = MaxJuncs + Ntanks; /* i = node index. i是水塔在Node中的索引 */ 146 if (!addnodeID(i,Tok[0])) return(215); /* Add ID to database. 将"ID标识"及其Node的索引值添加到节点哈希表中*/ 147 148 /* Check for valid data 检查数据的合法性*/ 149 if (n < 2) return(201); /* Too few fields. 字段个数过少,至少包含id与高程值*/ 150 if (!getfloat(Tok[1],&el)) return(202); /* Read elevation 高程值无法转成数值型*/ 151 if (n <= 3) /* Tank is reservoir.水池是特殊的水塔,如果数据项小于3项,则将水塔认为为水库*/ 152 { 153 if (n == 3) /* Pattern supplied 找到指定时间模式值的索引值*/ 154 { 155 t = findID(Tok[2],Patlist); 156 if (t == NULL) return(205); 157 p = t->i; 158 } 159 } 160 else if (n < 6) return(201); /* Too few fields for tank.水塔需要的字段数不能少于6个*/ 161 else 162 { 163 /* Check for valid input data 检查水塔数据的正确性,进行类型检查*/ 164 if (!getfloat(Tok[2],&initlevel)) return(202); 165 if (!getfloat(Tok[3],&minlevel)) return(202); 166 if (!getfloat(Tok[4],&maxlevel)) return(202); 167 if (!getfloat(Tok[5],&diam)) return(202); 168 if (diam < 0.0) return(202);//口径非负检查 169 if (n >= 7 170 && !getfloat(Tok[6],&minvol)) return(202); 171 172 /* If volume curve supplied check it exists 如果容积曲线存在,则对容积曲线进行存在性检查,若存在返回索引值*/ 173 if (n == 8) 174 { 175 t = findID(Tok[7],Curvelist); 176 if (t == NULL) return(202); 177 vcurve = t->i; 178 } 179 } 180 181 Node[i].Rpt = 0; 182 Node[i].El = el; /* Elevation. */ 183 Node[i].C0 = 0.0; /* Init. quality. */ 184 Node[i].S = NULL; /* WQ source data */ 185 Node[i].Ke = 0.0; /* Emitter coeff. */ 186 Tank[Ntanks].Node = i; /* Node index. */ 187 Tank[Ntanks].H0 = initlevel; /* Init. level. */ 188 Tank[Ntanks].Hmin = minlevel; /* Min. level. */ 189 Tank[Ntanks].Hmax = maxlevel; /* Max level. */ 190 Tank[Ntanks].A = diam; /* Diameter. */ 191 Tank[Ntanks].Pat = p; /* Fixed grade pattern. */ 192 Tank[Ntanks].Kb = MISSING; /* Reaction coeff. */ 193 /* 194 ******************************************************************* 195 NOTE: The min, max, & initial volumes set here are based on a 196 nominal tank diameter. They will be modified in INPUT1.C if 197 a volume curve is supplied for this tank. 198 这里的最小、最大和初始容积的计算是基于水塔的口径来的。 199 它们会在INPUT1.C文件中被修改,如果这个水塔存在一个容积曲线。 200 ******************************************************************* 201 */ 202 area = PI*SQR(diam)/4.0;//横截面积 203 Tank[Ntanks].Vmin = area*minlevel;//最小容积 204 if (minvol > 0.0) Tank[Ntanks].Vmin = minvol; 205 Tank[Ntanks].V0 = Tank[Ntanks].Vmin + area*(initlevel - minlevel); 206 Tank[Ntanks].Vmax = Tank[Ntanks].Vmin + area*(maxlevel - minlevel);//最大容积 207 208 Tank[Ntanks].Vcurve = vcurve; /* Volume curve 容积曲线索引值*/ 209 Tank[Ntanks].MixModel = MIX1; /* Completely mixed 水池出水模式*/ 210 Tank[Ntanks].V1max = 1.0; /* Compart. size ratio */ 211 return(0); 212 } /* end of tankdata */ 213 214 215 int pipedata() 216 /* 217 **-------------------------------------------------------------- 218 ** Input: none ;输入:无 219 ** Output: returns error code ;输出:错误代码 220 ** Purpose: processes pipe data :作用:处理管段数据 221 ** Format: ;格式:状态可以不需要,默认都是OPEN 222 ** [PIPE] 223 ** id node1 node2 length diam rcoeff (lcoeff) (status) 224 **-------------------------------------------------------------- 225 */ 226 { 227 int j1, /* Start-node index 起始junction在Node链表中的索引值*/ 228 j2, /* End-node index 终点junction在Node链表中的索引值*/ 229 n; /* # data items 当前行数据项个数*/ 230 char type = PIPE, /* Link type 管段类型,默认为Pipe*/ 231 status = OPEN; /* Link status 管段状态(OPEN, CLOSED或CV),默认是开启状态*/ 232 double length, /* Link length 管长*/ 233 diam, /* Link diameter 管径*/ 234 rcoeff, /* Roughness coeff. 粗糙系数*/ 235 lcoeff = 0.0; /* Minor loss coeff. 局部损失系数*/ 236 237 /* Add new pipe to data base 将新管段添加进来*/ 238 n = Ntokens; 239 if (Nlinks == MaxLinks) return(200); 240 Npipes++; 241 Nlinks++; 242 if (!addlinkID(Nlinks,Tok[0])) return(215); 243 244 /* Check for valid data 检查管段数据的正确性*/ 245 if (n < 6) return(201); //数据项个数至少有6个 246 if ((j1 = findnode(Tok[1])) == 0 || //获取始末点的索引值 247 (j2 = findnode(Tok[2])) == 0 248 ) return(203); 249 250 /*** Updated 10/25/00 ***/ 251 if (j1 == j2) return(222); //如果起点与终点相同则返回出错信息 252 253 if (!getfloat(Tok[3],&length) || //进行长度、口径、粗糙系数的数据类型检查 254 !getfloat(Tok[4],&diam) || 255 !getfloat(Tok[5],&rcoeff) 256 ) return(202); 257 258 if (length <= 0.0 || //进行长度、口径、粗糙系数的数据的非负检查 259 diam <= 0.0 || 260 rcoeff <= 0.0 261 ) return(202); 262 263 /* Case where either loss coeff. or status supplied 获取局部损失系数或者状态*/ 264 if (n == 7) 265 { 266 if (match(Tok[6],w_CV)) type = CV; 267 else if (match(Tok[6],w_CLOSED)) status = CLOSED; 268 else if (match(Tok[6],w_OPEN)) status = OPEN; 269 else if (!getfloat(Tok[6],&lcoeff)) return(202); 270 } 271 272 /* Case where both loss coeff. and status supplied 获取局部损失系数和状态*/ 273 if (n == 8) 274 { 275 if (!getfloat(Tok[6],&lcoeff)) return(202); 276 if (match(Tok[7],w_CV)) type = CV; 277 else if (match(Tok[7],w_CLOSED)) status = CLOSED; 278 else if (match(Tok[7],w_OPEN)) status = OPEN; 279 else return(202); 280 } 281 if (lcoeff < 0.0) return(202); 282 283 /* Save pipe data 保存该管段的数据*/ 284 Link[Nlinks].N1 = j1; /* Start-node index */ 285 Link[Nlinks].N2 = j2; /* End-node index */ 286 Link[Nlinks].Len = length; /* Length */ 287 Link[Nlinks].Diam = diam; /* Diameter */ 288 Link[Nlinks].Kc = rcoeff; /* Rough. coeff */ 289 Link[Nlinks].Km = lcoeff; /* Loss coeff */ 290 Link[Nlinks].Kb = MISSING; /* Bulk coeff */ 291 Link[Nlinks].Kw = MISSING; /* Wall coeff */ 292 Link[Nlinks].Type = type; /* Link type */ 293 Link[Nlinks].Stat = status; /* Link status */ 294 Link[Nlinks].Rpt = 0; /* Report flag */ 295 return(0); 296 } /* end of pipedata */ 297 298 299 int pumpdata() 300 /* 301 **--------------------------------------------------------------;备注:水泵被认为是特殊的管段数据 302 ** Input: none ;输入:无 303 ** Output: returns error code ;输出:错误代码 304 ** Purpose: processes pump data ;目的:处理水泵数据 305 ** Formats: ;格式: 306 ** [PUMP] 307 ** (Version 1.x Format): 308 ** id node1 node2 power 309 ** id node1 node2 h1 q1 310 ** id node1 node2 h0 h1 q1 h2 q2 311 ** (Version 2 Format): 312 ** id node1 node2 KEYWORD value {KEYWORD value ...} 313 ** where KEYWORD = [POWER,HEAD,PATTERN,SPEED] 314 ;ID Node1 Node2 Parameters 315 9 9 10 HEAD 1 ; 316 **-------------------------------------------------------------- 317 */ 318 { 319 int j, 320 j1, /* Start-node index 起始junction在Node链表中的索引值*/ 321 j2, /* End-node index 终点junction在Node链表中的索引值*/ 322 m, n; /* # data items 当前行数据项个数*/ 323 double y; 324 STmplist *t; /* Pattern record */ 325 326 /* Add new pump to data base 将新水泵数据添加进来*/ 327 n = Ntokens; 328 if (Nlinks == MaxLinks || 329 Npumps == MaxPumps 330 ) return(200); 331 Nlinks++; 332 Npumps++; 333 if (!addlinkID(Nlinks,Tok[0])) return(215); 334 335 /* Check for valid data 检查数据合法性*/ 336 if (n < 4) return(201); //字段个数至少4个 337 if ((j1 = findnode(Tok[1])) == 0 || //获取始末junction在Node链表中的索引值 338 (j2 = findnode(Tok[2])) == 0 339 ) return(203); 340 341 /*** Updated 10/25/00 ***/ 342 if (j1 == j2) return(222); //如果起点与终点相同则返回出错信息 343 344 /* Save pump data 保存水泵数据*/ 345 Link[Nlinks].N1 = j1; /* Start-node index. */ 346 Link[Nlinks].N2 = j2; /* End-node index. */ 347 Link[Nlinks].Diam = Npumps; /* Pump index. */ 348 Link[Nlinks].Len = 0.0; /* Link length. */ 349 Link[Nlinks].Kc = 1.0; /* Speed factor. */ 350 Link[Nlinks].Km = 0.0; /* Horsepower. */ 351 Link[Nlinks].Kb = 0.0; 352 Link[Nlinks].Kw = 0.0; 353 Link[Nlinks].Type = PUMP; /* Link type. */ 354 Link[Nlinks].Stat = OPEN; /* Link status. */ 355 Link[Nlinks].Rpt = 0; /* Report flag. */ 356 Pump[Npumps].Link = Nlinks; /* Link index. */ 357 Pump[Npumps].Ptype = NOCURVE; /* Type of pump curve 默认无水泵曲线*/ 358 Pump[Npumps].Hcurve = 0; /* Pump curve index */ 359 Pump[Npumps].Ecurve = 0; /* Effic. curve index */ 360 Pump[Npumps].Upat = 0; /* Utilization pattern*/ 361 Pump[Npumps].Ecost = 0.0; /* Unit energy cost */ 362 Pump[Npumps].Epat = 0; /* Energy cost pattern*/ 363 364 /* If 4-th token is a number then input follows Version 1.x format 若第4个数据项是一个数字则按版本1.x来解读水泵曲线*/ 365 /* so retrieve pump curve parameters 获取水泵参数曲线;一般现在都是按2.x版本,所以可以跳过不做细究*/ 366 if (getfloat(Tok[3],&X[0])) 367 { 368 m = 1; 369 for (j=4; j<n; j++) 370 { 371 if (!getfloat(Tok[j],&X[m])) return(202); 372 m++; 373 } 374 return(getpumpcurve(m)); /* Get pump curve params */ 375 } 376 377 /* Otherwise input follows Version 2 format */ 378 /* so retrieve keyword/value pairs. 版本2是采用键值对的方式来定义水泵曲线*/ 379 /* 380 关键词和数值(可以重复) 381 a. 关键词包括: 382 * POWER——定速能量水泵的功率数值,hp (kW) 383 * HEAD——描述了水泵扬程与流量关系的曲线ID 384 * SPEED——相对速度设置(额定速度为1.0 ,0意味着水泵关闭) 385 * PATTERN——时间模式的ID,描述了速度设置怎样随时间变化 386 b. 对于每一台水泵,必须提供POWER 或者HEAD。其它关键词是可选的。 387 */ 388 m = 4; 389 while (m < n) 390 { 391 if (match(Tok[m-1],w_POWER)) /* Const. HP curve 定速能量水泵的功率数值,hp (kW)*/ 392 { 393 y = atof(Tok[m]); 394 if (y <= 0.0) return(202); 395 Pump[Npumps].Ptype = CONST_HP; //水泵曲线类型 396 Link[Nlinks].Km = y; //Minor loss coeff. 局部损失系数 397 } 398 else if (match(Tok[m-1],w_HEAD)) /* Custom pump curve 描述了水泵扬程与流量关系的曲线ID*/ 399 { 400 t = findID(Tok[m],Curvelist); 401 if (t == NULL) return(206); 402 Pump[Npumps].Hcurve = t->i; 403 } 404 else if (match(Tok[m-1],w_PATTERN)) /* Speed/status pattern 时间模式的ID,描述了速度设置怎样随时间变化 */ 405 { 406 t = findID(Tok[m],Patlist); 407 if (t == NULL) return(205); 408 Pump[Npumps].Upat = t->i; 409 } 410 else if (match(Tok[m-1],w_SPEED)) /* Speed setting 相对速度设置(额定速度为1.0 ,0意味着水泵关闭)*/ 411 { 412 if (!getfloat(Tok[m],&y)) return(202); 413 if (y < 0.0) return(202); 414 Link[Nlinks].Kc = y; 415 } 416 else return(201); 417 m = m + 2; /* Skip to next keyword token 键值对都是2个一组的,可以重复*/ 418 } 419 return(0); 420 } /* end of pumpdata */ 421 422 423 int valvedata() 424 /* 425 **-------------------------------------------------------------- 426 ** Input: none ;输入:无 427 ** Output: returns error code ;输出:错误代码 428 ** Purpose: processes valve data ;作用:处理阀门数据 429 ** Format: ;格式: 430 ** [VALVE] 431 ** id node1 node2 diam type setting (lcoeff) 432 **-------------------------------------------------------------- 433 */ 434 { 435 int j1, /* Start-node index 起始junction在Node链表中的索引值*/ 436 j2, /* End-node index 终点junction在Node链表中的索引值*/ 437 n; /* # data items 当前行数据项个数*/ 438 char status = ACTIVE, /* Valve status 阀门状态*/ 439 type; /* Valve type 阀门类型*/ 440 double diam = 0.0, /* Valve diameter 阀门口径*/ 441 setting, /* Valve setting 阀门设置*/ 442 lcoeff = 0.0; /* Minor loss coeff. 局部损失系数*/ 443 STmplist *t; /* Curve record 阀门曲线*/ 444 445 /* Add new valve to data base 添加阀门数据*/ 446 n = Ntokens; 447 if (Nlinks == MaxLinks || 448 Nvalves == MaxValves 449 ) return(200); 450 Nvalves++; 451 Nlinks++; 452 if (!addlinkID(Nlinks,Tok[0])) return(215); 453 454 /* Check for valid data 检查阀门数据的合法性*/ 455 if (n < 6) return(201); //至少需要6个字段,第7个字段"局部损失系数"可选 456 if ((j1 = findnode(Tok[1])) == 0 || 457 (j2 = findnode(Tok[2])) == 0 458 ) return(203); 459 460 /*** Updated 10/25/00 ***/ 461 if (j1 == j2) return(222); //起点与终点相同,返回错误代码 462 //获取阀门类型 阀门 设置 463 if (match(Tok[4],w_PRV)) type = PRV;//PRV (减压阀) 压力,m(psi) 464 else if (match(Tok[4],w_PSV)) type = PSV;//PSV (稳压阀) 压力,m(psi) 465 else if (match(Tok[4],w_PBV)) type = PBV;//PBV (压力制动阀) 压力,m(psi) 466 else if (match(Tok[4],w_FCV)) type = FCV;//FCV (流量控制阀) 流量(流量单位) 467 else if (match(Tok[4],w_TCV)) type = TCV;//TCV (节流控制阀) 损失系数 468 else if (match(Tok[4],w_GPV)) type = GPV;//GPV (常规阀门) 水头损失曲线的ID 469 else return(201); /* Illegal valve type.*/ 470 if (!getfloat(Tok[3],&diam)) return(202); 471 if (diam <= 0.0) return(202); /* Illegal diameter.*/ 472 if (type == GPV) /* Headloss curve for GPV 获取水头损失曲线ID在曲线表中的索引值*/ 473 { 474 t = findID(Tok[5],Curvelist); 475 if (t == NULL) return(206); 476 setting = t->i; 477 478 /*** Updated 9/7/00 ***/ 479 status = OPEN; //阀门状态设置为开启 480 481 } 482 else if (!getfloat(Tok[5],&setting)) return(202); 483 if (n >= 7 && //获取 484 !getfloat(Tok[6],&lcoeff) 485 ) return(202); 486 487 /* Check that PRV, PSV, or FCV not connected to a tank & */ 488 /* check for illegal connections between pairs of valves.*/ 489 if ((j1 > Njuncs || j2 > Njuncs) && 490 (type == PRV || type == PSV || type == FCV) 491 ) return(219); 492 if (!valvecheck(type,j1,j2)) return(220); 493 494 /* Save valve data 设置阀门数据,注意阀门是特殊的管段数据*/ 495 Link[Nlinks].N1 = j1; /* Start-node index. 设置起始节点索引值*/ 496 Link[Nlinks].N2 = j2; /* End-node index. 设置终点节点索引值*/ 497 Link[Nlinks].Diam = diam; /* Valve diameter. 阀门口径*/ 498 Link[Nlinks].Len = 0.0; /* Link length. */ 499 Link[Nlinks].Kc = setting; /* Valve setting. */ 500 Link[Nlinks].Km = lcoeff; /* Loss coeff */ 501 Link[Nlinks].Kb = 0.0; 502 Link[Nlinks].Kw = 0.0; 503 Link[Nlinks].Type = type; /* Valve type. */ 504 Link[Nlinks].Stat = status; /* Valve status. */ 505 Link[Nlinks].Rpt = 0; /* Report flag. */ 506 Valve[Nvalves].Link = Nlinks; /* Link index. */ 507 return(0); 508 } /* end of valvedata */ 509 510 511 int patterndata() 512 /* 513 **-------------------------------------------------------------- 514 ** Input: none ;输入:无 515 ** Output: returns error code ;输出:错误代码 516 ** Purpose: processes time pattern data ;作用:处理时间模式数据 517 ** Format: ;格式: 518 ** [PATTERNS] 这让我回想起当时Hammer中导出的INP文件解算时只有前面几个时段的数据,很可能的一个原因就是EPANET对INP文件的规范是每行最多40项,而Hammer的Pattern格式长度过长导致的。 519 ** id mult1 mult2 ..... 520 **-------------------------------------------------------------- 521 */ 522 { 523 int i,n; /*n表示 当前行数据项个数*/ 524 double x; 525 SFloatlist *f; //f:一个包含浮点数的单向链表 526 STmplist *p; //p:当前的用水模式 527 n = Ntokens - 1; 528 if (n < 1) return(201); /* Too few values 当前行数据项个数过少*/ 529 if ( /* Check for new pattern 查看当前行的ID是否存在于PrevPat链表中*/ 530 PrevPat != NULL && /*这里的if语句的作用在于一个时间模式可能是连接多行,那么这样就能提高效率*/ 531 strcmp(Tok[0],PrevPat->ID) == 0 532 ) p = PrevPat; 533 else p = findID(Tok[0],Patlist); 534 if (p == NULL) return(205); 535 for (i=1; i<=n; i++) /* Add multipliers to list 添加用水模式系数,添加至链首*/ 536 { 537 if (!getfloat(Tok[i],&x)) return(202); 538 f = (SFloatlist *) malloc(sizeof(SFloatlist)); 539 if (f == NULL) return(101); 540 f->value = x; 541 f->next = p->x; //将当前系数挂在该模式p的系数链首 542 p->x = f; 543 } 544 Pattern[p->i].Length += n; /* Save # multipliers for pattern 维护当前模式的长度*/ 545 PrevPat = p; /* Set previous pattern pointer 临时的模式对象,因模式可能是连着多行而提高效率*/ 546 return(0); 547 } /* end of patterndata */ 548 549 550 int curvedata() 551 /* 552 **------------------------------------------------------ 553 ** Input: none ;输入:无 554 ** Output: returns error code ;输出:错误代码 555 ** Purpose: processes curve data ;作用:处理曲线数据 556 ** Format: ;格式: 557 ** [CURVES] 558 ** CurveID x-value y-value 559 **------------------------------------------------------ 560 */ 561 { 562 double x,y; 563 SFloatlist *fx, *fy; 564 STmplist *c; 565 566 /* Check for valid curve ID */ 567 if (Ntokens < 3) return(201); 568 if ( 569 PrevCurve != NULL && 570 strcmp(Tok[0],PrevCurve->ID) == 0 571 ) c = PrevCurve; 572 else c = findID(Tok[0],Curvelist); 573 if (c == NULL) return(205); 574 575 /* Check for valid data */ 576 if (!getfloat(Tok[1],&x)) return(202); 577 if (!getfloat(Tok[2],&y)) return(202); 578 579 /* Add new data point to curve's linked list */ 580 fx = (SFloatlist *) malloc(sizeof(SFloatlist)); 581 fy = (SFloatlist *) malloc(sizeof(SFloatlist)); 582 if (fx == NULL || fy == NULL) return(101); 583 fx->value = x; 584 fx->next = c->x; 585 c->x = fx; 586 fy->value = y; 587 fy->next = c->y; 588 c->y = fy; 589 Curve[c->i].Npts++; 590 591 /* Save the pointer to this curve */ 592 PrevCurve = c; 593 return(0); 594 } 595 596 597 int demanddata() 598 /* 599 **-------------------------------------------------------------- 600 ** Input: none ;输入:无 601 ** Output: returns error code ;输出:错误代码 602 ** Purpose: processes node demand data ;作用:定义连接节点的多模式需水,是对[JUNCTIONS]的补充。 603 ** Format: ;格式: 604 ** [DEMANDS] 605 ** MULTIPLY factor 606 ** node base_demand (pattern) 607 ** 608 ** NOTE: Demands entered in this section replace those ;注意:这部分的需水量将替换[JUNCTIONS]部分录入的需水量 609 ** entered in the [JUNCTIONS] section 610 **-------------------------------------------------------------- 611 */ 612 { //j:表示节点在Node中的索引值 613 int j,n,p = 0; //p:当前的用水模式在Pattern中的索引 /*n表示 当前行数据项个数*/ 614 double y; 615 Pdemand demand; 616 STmplist *pat; 617 618 /* Extract data from tokens 从当前行中提取需水量数据*/ 619 n = Ntokens; 620 if (n < 2) return(201); 621 if (!getfloat(Tok[1],&y)) return(202); 622 623 /* If MULTIPLY command, save multiplier */ 624 if (match(Tok[0],w_MULTIPLY)) 625 { 626 if (y <= 0.0) return(202); 627 else Dmult = y; 628 return(0); 629 } 630 631 /* Otherwise find node (and pattern) being referenced 找到节点所引用的用水模式的索引值*/ 632 if ((j = findnode(Tok[0])) == 0) return(208); 633 if (j > Njuncs) return(208); 634 if (n >= 3) 635 { 636 pat = findID(Tok[2],Patlist); 637 if (pat == NULL) return(205); 638 p = pat->i; 639 } 640 641 /* Replace any demand entered in [JUNCTIONS] section */ 642 /* (Such demand was temporarily stored in D[]) */ 643 644 /*** Updated 6/24/02 ***/ 645 demand = Node[j].D; 646 if (demand && D[j] != MISSING) 647 { 648 demand->Base = y; 649 demand->Pat = p; 650 D[j] = MISSING;//通过这个MISSING来做替换标记 651 } 652 /*** End of update ***/ 653 654 /* Otherwise add a new demand to this junction */ 655 else 656 { 657 demand = (struct Sdemand *) malloc(sizeof(struct Sdemand)); 658 if (demand == NULL) return(101); 659 demand->Base = y; 660 demand->Pat = p; 661 demand->next = Node[j].D; 662 Node[j].D = demand; 663 } 664 return(0); 665 } /* end of demanddata */ 666 667 668 int controldata() 669 /* 670 **-------------------------------------------------------------- 671 ** Input: none ;输入:无 672 ** Output: returns error code ;输出:错误代码 673 ** Purpose: processes simple controls ;作用:处理简单控制规则 674 ** Formats: ;格式: 675 ** [CONTROLS] 676 ** LINK linkID setting IF NODE nodeID {BELOW/ABOVE} value 677 ** LINK linkID setting AT TIME time (units) 678 ** LINK linkID setting AT CLOCKTIME clocktime (units) 679 ** (0) (1) (2) (3) (4) (5) (6) (7) 680 其中: 681 linkID——管段ID标签; 682 setting——OPEN或者CLOSED,水泵速度设置或者控制阀门设置 683 nodeID——节点ID标签; 684 value ——连接节点压力或者水池水位; 685 time——从模拟开始起算的时间,以小数或者小时:分钟计; 686 clocktime——24小时的钟表时间(小时:分钟)。 687 备注: 688 a. 简单控制将根据水池水位、节点压强、进入模拟时间或者一日中的时间,改变管 689 段状态或者设置。 690 b. 对于用在指定管段状态和设置的常规情况,尤其阀门控制,参见[STATUS] 节的 691 备注。 692 示例: 693 [CONTROLS] 694 ;如果Tank 23的水位超过20 ft ,关闭Link 695 LINK 12 CLOSED IF NODE 23 ABOVE 20 696 697 ;如果Node 130 的压力低于30 psi,开启Link 12 698 LINK 12 OPEN IF NODE 130 BELOW 30 699 700 ;在进入模拟16小时后水泵PUMP02的转速比设置为1.5 701 LINK PUMP02 1.5 AT TIME 16 702 703 ;整个模拟过程中Lin 12在上午10时关闭,下午8时开启 704 LINK 12 CLOSED AT CLOCKTIME 10 AM 705 LINK 12 OPEN AT CLOCKTIME 8 PM 706 **-------------------------------------------------------------- 707 */ 708 { 709 int i = 0, /* Node index Node中的索引值*/ 710 k, /* Link index Link中的索引值*/ 711 n; /* # data items 当前行数据项个数*/ 712 char status = ACTIVE, /* Link status 管段状态*/ 713 type; /* Link or control type 管段类型*/ 714 double setting = MISSING, /* Link setting */ 715 time = 0.0, /* Simulation time 从模拟开始起算的时间*/ 716 level = 0.0; /* Pressure or tank level 水池水位或节点压强*/ 717 718 /* Check for sufficient number of input tokens 检查字段个数是否合法,不小于6个*/ 719 n = Ntokens; 720 if (n < 6) return(201); 721 722 /* Check that controlled link exists 判断控制的管段是否存在*/ 723 k = findlink(Tok[1]); 724 if (k == 0) return(204); 725 type = Link[k].Type; 726 if (type == CV) return(207); /* Cannot control check valve. 不能够控制检查阀*/ 727 728 /*** Updated 9/7/00 ***/ 729 /* Parse control setting into a status level or numerical setting. 获取并设置控制管段的状态*/ 730 if (match(Tok[2],w_OPEN)) 731 { 732 status = OPEN; 733 if (type == PUMP) setting = 1.0; 734 if (type == GPV) setting = Link[k].Kc; 735 } 736 else if (match(Tok[2],w_CLOSED)) 737 { 738 status = CLOSED; 739 if (type == PUMP) setting = 0.0; 740 if (type == GPV) setting = Link[k].Kc; 741 } 742 else if (type == GPV) return(206); 743 else if (!getfloat(Tok[2],&setting)) return(202); 744 745 /*** Updated 3/1/01 ***/ 746 /* Set status for pump in case speed setting was supplied 根据setting来设置水泵或者管段的status*/ 747 /* or for pipe if numerical setting was supplied */ 748 749 if (type == PUMP || type == PIPE) 750 { 751 if (setting != MISSING) 752 { 753 if (setting < 0.0) return(202); 754 else if (setting == 0.0) status = CLOSED; 755 else status = OPEN; 756 } 757 } 758 759 /* Determine type of control 获取该条控制规则的类型*/ 760 if (match(Tok[4],w_TIME)) type = TIMER; 761 else if (match(Tok[4],w_CLOCKTIME)) type = TIMEOFDAY; 762 else 763 {//对节点或者水池的控制 764 if (n < 8) return(201); 765 if ((i = findnode(Tok[5])) == 0) return(203); 766 if (match(Tok[6],w_BELOW)) type = LOWLEVEL; 767 else if (match(Tok[6],w_ABOVE)) type = HILEVEL; 768 else return(201); 769 } 770 771 /* Parse control level or time 获取控制的时间或者水位*/ 772 switch (type) 773 { 774 case TIMER: 775 case TIMEOFDAY: 776 if (n == 6) time = hour(Tok[5],""); 777 if (n == 7) time = hour(Tok[5],Tok[6]); 778 if (time < 0.0) return(201); 779 break; 780 case LOWLEVEL: 781 case HILEVEL: 782 if (!getfloat(Tok[7],&level)) return(202); 783 break; 784 } 785 786 /* Fill in fields of control data structure 将上述获取到的值填充到控制规则线性链表中*/ 787 Ncontrols++; 788 if (Ncontrols > MaxControls) return(200); 789 Control[Ncontrols].Link = k; 790 Control[Ncontrols].Node = i; 791 Control[Ncontrols].Type = type; 792 Control[Ncontrols].Status = status; 793 Control[Ncontrols].Setting = setting; 794 Control[Ncontrols].Time = (long)(3600.0*time); 795 if (type == TIMEOFDAY) 796 Control[Ncontrols].Time %= SECperDAY; 797 Control[Ncontrols].Grade = level; 798 return(0); 799 } /* end of controldata */ 800 801 802 int sourcedata() 803 /* 804 **-------------------------------------------------------------- 805 ** Input: none ;输入:无 806 ** Output: returns error code ;输出:错误代码 807 ** Purpose: processes water quality source data ;目的:定义水质源头的位置。 808 ** Formats: ;格式: 809 ** [SOURCE] 810 ** node sourcetype quality (pattern start stop) 811 ** 812 格式: 813 每一水质源头为一输入行,包括: 814 node 节点ID标签 815 sourcetype 源头类型(CONCEN, MASS, FLOWPACED 或SETPOINT ) 816 quality 基准源头强度 817 pattern 时间模式ID(可选) 818 819 ** NOTE: units of mass-based source are mass/min 820 备注: 821 a. MASS类型源头的强度以质量流量每分钟计。所有其它类型以浓度单位来计量源头强度。 822 b. 源头强度可以指定时间模式,使其随时间变化。 823 c. CONCEN源头为: 824 表示节点的任何外部源头进流浓度 825 仅仅在节点具有净负需水量时使用(水从节点进入管网) 826 如果节点为连接节点,报告浓度时混合了源流量和从管网其它部分的进流 827 如果节点为水库,报告的浓度为源头浓度 828 如果节点为水池,报告的浓度为水池的内部浓度 829 用于节点,表示了源水供应或者处理厂(例如,水库或者节点具有负的需水量) 830 不可用于同时具有进流/出流的蓄水池。 831 d. MASS, FLOWPACED 或SETPOINT 源头: 832 表示了增强源头,这里物质被直接注射到管网,不考虑节点的需水量怎样 833 以下方式影响了离开节点到管网的其它部分的水: 834 - MASS 注入,增加了固定的质量流量到节点的进流 835 - FLOWPACED 注入,增加了固定浓度到节点的进流浓度 836 - SETPOINT 注入,固定了任何离开节点的浓度(只要进流带来的浓度低于设置值) 837 连接节点或者水库注入源头报告的浓度,是在注入之后的浓度;报告具有注入源头的水 838 池的浓度,为水池的内部浓度 839 适合于模拟示踪剂或者消毒剂直接注入到管网,或者为了模拟污染物的入侵。 840 e.对于模拟水龄或者源头跟踪,[SOURCES]节是不需要的。 841 **-------------------------------------------------------------- 842 */ 843 { 844 int i, /* Token with quality value 水质值这列所在的当前行的数据项列表中的索引值*/ 845 j, /* Node index Node中的索引值*/ 846 n, /* # data items 行的数据项数*/ 847 p = 0; /* Time pattern 模式*/ 848 char type = CONCEN; /* Source type 源头类型*/ 849 double c0 = 0; /* Init. quality 初始水质*/ 850 STmplist *pat; 851 Psource source; 852 853 n = Ntokens; 854 if (n < 2) return(201); 855 if ((j = findnode(Tok[0])) == 0) return(203); 856 /* NOTE: Under old format, SourceType not supplied so let 如果是老版本那么i=1,因为源头类型不存在*/ 857 /* i = index of token that contains quality value. 水质值这列所在的当前行的数据项列表中的索引值*/ 858 i = 2; 859 if (match(Tok[1],w_CONCEN)) type = CONCEN; 860 else if (match(Tok[1],w_MASS)) type = MASS; 861 else if (match(Tok[1],w_SETPOINT)) type = SETPOINT; 862 else if (match(Tok[1],w_FLOWPACED)) type = FLOWPACED; 863 else i = 1; 864 if (!getfloat(Tok[i],&c0)) return(202); /* Illegal WQ value 检查水质数据类型*/ 865 866 if (n > i+1 && strlen(Tok[i+1]) > 0 && strcmp(Tok[i+1], "*") != 0 ) //(2.00.11 - LR) 867 { 868 pat = findID(Tok[i+1],Patlist); 869 if (pat == NULL) return(205); /* Illegal pattern. 找不到指定模式*/ 870 p = pat->i; //返回该模式在Pattern中的索引值 871 } 872 873 source = (struct Ssource *) malloc(sizeof(struct Ssource)); 874 if (source == NULL) return(101); 875 source->C0 = c0; 876 source->Pat = p; 877 source->Type = type; 878 Node[j].S = source; 879 return(0); 880 } /* end of sourcedata */ 881 882 883 int emitterdata() 884 /* 885 **-------------------------------------------------------------- 886 ** Input: none ;输入:无 887 ** Output: returns error code ;输出:错误代码 888 ** Purpose: processes junction emitter data ;目的:将模拟节点定义为扩散器(喷嘴或者孔口)。 889 ** Formats: ;格式: 890 ** [EMITTER] 891 ** node K 892 备注: 893 a. 扩散器用于模拟通过喷水或者管道渗漏的流量。 894 b. 扩散器的出流等于流量系数与提升的连接节点压力乘积。 895 c. 功率可以利用[OPTIONS]节的EMITTER EXPONENT选项指定。缺省功率为0.5 ,通常用于喷嘴。 896 d. 程序结果中报告的实际需水量,包括节点的常规需水量加上通过扩散器的流量。 897 e. [EMITTERS] 节是可选的。 898 **-------------------------------------------------------------- 899 */ 900 { 901 int j, /* Node index Node中的索引值*/ 902 n, /* # data items 行的数据项数*/ 903 double k; /* Flow coeff, 流量系数,在1米(1 psi )压降下的流量单位。*/ 904 905 n = Ntokens; 906 if (n < 2) return(201); 907 if ((j = findnode(Tok[0])) == 0) return(203); 908 if (j > Njuncs) return(209); /* Not a junction.*/ 909 if (!getfloat(Tok[1],&k)) return(202); 910 if (k < 0.0) return(202); 911 Node[j].Ke = k; 912 return(0); 913 } 914 915 916 int qualdata() 917 /* 918 **-------------------------------------------------------------- 919 ** Input: none ;输入:无 920 ** Output: returns error code ;输出:错误代码 921 ** Purpose: processes initial water quality data ;目的:定义节点的初始水质。 922 ** Formats: 923 ** [QUALITY] 924 ** node initqual 925 ** node1 node2 initqual 926 每一节点为一输入行,包括: 927 node 节点ID标签 928 initqual 初始水质 929 930 备注: 931 a. 对于没有列入的节点,水质假设为零。 932 b. 水质表示了化学成分的浓度、水龄的小时或源头跟踪的百分比。 933 c. [QUALITY]节是可选的。 934 **-------------------------------------------------------------- 935 */ 936 { 937 int j,n; 938 long i,i0,i1; 939 double c0; 940 941 if (Nnodes == 0) return(208); /* No nodes defined yet */ 942 n = Ntokens; 943 if (n < 2) return(0); 944 if (n == 2) /* Single node entered 单个节点*/ 945 { 946 if ( (j = findnode(Tok[0])) == 0) return(0); 947 if (!getfloat(Tok[1],&c0)) return(209); 948 Node[j].C0 = c0; 949 } 950 else /* Node range entered 批量节点*/ 951 { 952 if (!getfloat(Tok[2],&c0)) return(209); 953 954 /* If numerical range supplied, then use numerical comparison 这块代码有些奇怪,不过一般没有这种输入格式的*/ 955 if ((i0 = atol(Tok[0])) > 0 && (i1 = atol(Tok[1])) > 0) 956 { 957 for (j=1; j<=Nnodes; j++) 958 { 959 i = atol(Node[j].ID); 960 if (i >= i0 && i <= i1) Node[j].C0 = c0; 961 } 962 } 963 else 964 { 965 for (j=1; j<=Nnodes; j++) 966 if ((strcmp(Tok[0],Node[j].ID) <= 0) && 967 (strcmp(Tok[1],Node[j].ID) >= 0) 968 ) Node[j].C0 = c0; 969 } 970 } 971 return(0); 972 } /* end of qualdata */ 973 974 975 int reactdata() 976 /* 977 **-------------------------------------------------------------- 978 ** Input: none ;输入:无 979 ** Output: returns error code ;输出:错误代码 980 ** Purpose: processes reaction coeff. data ;目的:定义对应于管网中与化学成分反应的参数。 981 ** Formats: ;格式: 982 ** [REACTIONS] 983 ** ORDER {BULK/WALL/TANK} value 984 ** GLOBAL {BULK/WALL} coeff 985 ** BULK link1 (link2) coeff 986 ** WALL link1 (link2) coeff 987 ** TANK node1 (node2) coeff 988 ** LIMITING POTENTIAL value 989 ** ROUGHNESS CORRELATION value 990 格式: 991 ORDER BULK/WALL/TANK value 992 GLOBAL BULK/WALL value 993 BULK/WALL/TANK pipeID value 994 LIMITING POTENTIAL value 995 ROUGHNESS CORRELATION value 996 定义: 997 ORDER 用于设置分别发生在主流水体、管壁或者水池中的反应级数。管壁反应的数值必须 998 为0或者1。如果没有提供,缺省反应级数为1.0 。 999 GLOBAL用于设置所有主流水体反应系数(管道和水池)或者所有管壁系数的全局数值。缺 1000 省值为零。 1001 BULK, WALL 和TANK用于对指定管道和水池重新设置全局反应系数。 1002 LIMITING POTENTIAL 指定了反应速率正比于当前浓度和一些限制值之间的差异。 1003 ROUGHNESS CORRELATION将使所有缺省管壁反应系数,以以下方式,相关于管道粗糙系 1004 数: 1005 水头损失公式 粗糙相关性 1006 Hazen-Williams F/C 1007 Darcy-Weisbach F/log(e/D) 1008 Chezy-Manning F*n 1009 105 1010 式中F——粗糙系数相关性; 1011 C——Hazen-Williams C因子; 1012 e——Darcy-Weisbach粗糙系数; 1013 D——管道直径; 1014 n——Chezy-Manning 粗糙系数。 1015 这种方式计算的缺省值能够通过利用WALL格式,对于任何使用特定数值管道重载。 1016 备注: 1017 a. 注意增长反应系数采用正值,衰减反应系数为负值。 1018 b. 所有反应系数的时间单位为1/日。 1019 c. 本节所有输入为可选的,反斜杠(/)之后的事项说明了允许选项。 1020 **-------------------------------------------------------------- 1021 */ 1022 { 1023 int item,j,n; 1024 long i,i1,i2; 1025 double y; 1026 1027 /* Skip line if insufficient data */ 1028 n = Ntokens; 1029 if (n < 3) return(0); 1030 1031 /* Process input depending on keyword */ 1032 if (match(Tok[0],w_ORDER)) /* Reaction order */ 1033 { 1034 if (!getfloat(Tok[n-1],&y)) return(213); 1035 if (match(Tok[1],w_BULK)) BulkOrder = y; 1036 else if (match(Tok[1],w_TANK)) TankOrder = y; 1037 else if (match(Tok[1],w_WALL)) 1038 { 1039 if (y == 0.0) WallOrder = 0.0; 1040 else if (y == 1.0) WallOrder = 1.0; 1041 else return(213); 1042 } 1043 else return(213); 1044 return(0); 1045 } 1046 if (match(Tok[0],w_ROUGHNESS)) /* Roughness factor */ 1047 { 1048 if (!getfloat(Tok[n-1],&y)) return(213); 1049 Rfactor = y; 1050 return(0); 1051 } 1052 if (match(Tok[0],w_LIMITING)) /* Limiting potential */ 1053 { 1054 if (!getfloat(Tok[n-1],&y)) return(213); 1055 /*if (y < 0.0) return(213);*/ 1056 Climit = y; 1057 return(0); 1058 } 1059 if (match(Tok[0],w_GLOBAL)) /* Global rates */ 1060 { 1061 if (!getfloat(Tok[n-1],&y)) return(213); 1062 if (match(Tok[1],w_BULK)) Kbulk = y; 1063 else if (match(Tok[1],w_WALL)) Kwall = y; 1064 else return(201); 1065 return(0); 1066 } 1067 if (match(Tok[0],w_BULK)) item = 1; /* Individual rates */ 1068 else if (match(Tok[0],w_WALL)) item = 2; 1069 else if (match(Tok[0],w_TANK)) item = 3; 1070 else return(201); 1071 strcpy(Tok[0],Tok[1]); /* Save id in Tok[0] */ 1072 if (item == 3) /* Tank rates */ 1073 { 1074 if (!getfloat(Tok[n-1],&y)) return(209); /* Rate coeff. */ 1075 if (n == 3) 1076 { 1077 if ( (j = findnode(Tok[1])) <= Njuncs) return(0); 1078 Tank[j-Njuncs].Kb = y; 1079 } 1080 else 1081 { 1082 /* If numerical range supplied, then use numerical comparison */ 1083 if ((i1 = atol(Tok[1])) > 0 && (i2 = atol(Tok[2])) > 0) 1084 { 1085 for (j=Njuncs+1; j<=Nnodes; j++) 1086 { 1087 i = atol(Node[j].ID); 1088 if (i >= i1 && i <= i2) Tank[j-Njuncs].Kb = y; 1089 } 1090 } 1091 else for (j=Njuncs+1; j<=Nnodes; j++) 1092 if ((strcmp(Tok[1],Node[j].ID) <= 0) && 1093 (strcmp(Tok[2],Node[j].ID) >= 0) 1094 ) Tank[j-Njuncs].Kb = y; 1095 } 1096 } 1097 else /* Link rates */ 1098 { 1099 if (!getfloat(Tok[n-1],&y)) return(211); /* Rate coeff. */ 1100 if (Nlinks == 0) return(0); 1101 if (n == 3) /* Single link */ 1102 { 1103 if ( (j = findlink(Tok[1])) == 0) return(0); 1104 if (item == 1) Link[j].Kb = y; 1105 else Link[j].Kw = y; 1106 } 1107 else /* Range of links */ 1108 { 1109 /* If numerical range supplied, then use numerical comparison */ 1110 if ((i1 = atol(Tok[1])) > 0 && (i2 = atol(Tok[2])) > 0) 1111 { 1112 for (j=1; j<=Nlinks; j++) 1113 { 1114 i = atol(Link[j].ID); 1115 if (i >= i1 && i <= i2) 1116 { 1117 if (item == 1) Link[j].Kb = y; 1118 else Link[j].Kw = y; 1119 } 1120 } 1121 } 1122 else for (j=1; j<=Nlinks; j++) 1123 if ((strcmp(Tok[1],Link[j].ID) <= 0) && 1124 (strcmp(Tok[2],Link[j].ID) >= 0) ) 1125 { 1126 if (item == 1) Link[j].Kb = y; 1127 else Link[j].Kw = y; 1128 } 1129 } 1130 } 1131 return(0); 1132 } /* end of reactdata */ 1133 1134 1135 int mixingdata() 1136 /* 1137 **------------------------------------------------------------- 1138 ** Input: none ;输入:无 1139 ** Output: returns error code ;输出:错误代码 1140 ** Purpose: processes tank mixing data ;目的:确定控制蓄水池混合的模型。 1141 ** Format: 1142 ** [MIXING] 1143 ** TankID MixModel FractVolume 1144 格式: 1145 一个水池占一输入行,包括: 1146 z 水池ID标签 1147 z 混合模型(MIXED, 2COMP, FIFO 或LIFO) 1148 z 室的容积(小数) 1149 备注: 1150 a. 混合模型包括: 1151 z 完全混合(MIXED ) 1152 z 双室混合(2COMP ) 1153 z 先进先出(FIFO) 1154 z 后进先出(LIFO) 1155 b. 室容积参数仅仅用于双室模型,代表了总水池容积贡献于进水/出水室的部分。 1156 c. [MIXING]节是可选的。不在本节描述的水池假设为完全混合。 1157 **------------------------------------------------------------- 1158 */ 1159 { 1160 int i,j,n; 1161 double v; 1162 1163 if (Nnodes == 0) return(208); /* No nodes defined yet */ 1164 n = Ntokens; 1165 if (n < 2) return(0); 1166 if ( (j = findnode(Tok[0])) <= Njuncs) return(0); 1167 if ( (i = findmatch(Tok[1],MixTxt)) < 0) return(201); 1168 v = 1.0; 1169 if ( (i == MIX2) && 1170 (n == 3) && 1171 (!getfloat(Tok[2],&v)) /* Get frac. vol. for 2COMP model */ 1172 ) return(209); 1173 if (v == 0.0) v = 1.0; /* v can't be zero */ 1174 n = j - Njuncs; 1175 if (Tank[n].A == 0.0) return(0); /* Tank is a reservoir */ 1176 Tank[n].MixModel = (char)i; 1177 Tank[n].V1max = v; 1178 return(0); 1179 } 1180 1181 1182 int statusdata() 1183 /* 1184 **-------------------------------------------------------------- 1185 ** Input: none ;输入:无 1186 ** Output: returns error code ;输出:错误代码 1187 ** Purpose: processes link initial status data ;目的:定义模拟开始时被选管段的初始状态。 1188 ** Formats: 1189 ** [STATUS] 1190 ** link value 1191 ** link1 (link2) value 1192 格式: 1193 每一控制管段占一输入行,包括: 1194 z 管段ID标签 1195 z 状态或者设置 1196 备注: 1197 a. 该部分没有列出的管段,缺省状态为OPEN(对于管道和水泵)或者ACTIVE(对于阀门)。 1198 b. 状态值可以为OPEN或者CLOSED。对于控制阀(例如PRV, FCV 等),意味着阀门是全开或 1199 者全闭,在其控制点不是活动的。 1200 c. 设置数值可以是水泵的转速设置,或者阀门的开启度设置。 1201 d. 管道的初始状态也可以在[PIPES]节设置。 1202 e. 止回阀不能够预先设置它们的状态。 1203 **-------------------------------------------------------------- 1204 */ 1205 { 1206 int j,n; //j:Link中的索引值;n:行数据项个数 1207 long i,i0,i1; 1208 double y = 0.0; 1209 char status = ACTIVE; 1210 1211 if (Nlinks == 0) return(210); 1212 n = Ntokens - 1; 1213 if (n < 1) return(201); 1214 1215 /* Check for legal status setting 检查状态数据的合法性*/ 1216 if (match(Tok[n],w_OPEN)) status = OPEN; 1217 else if (match(Tok[n],w_CLOSED)) status = CLOSED; 1218 else if (!getfloat(Tok[n],&y)) return(211); 1219 if (y < 0.0) return(211); 1220 1221 /* Single link ID supplied */ 1222 if (n == 1) 1223 { 1224 if ( (j = findlink(Tok[0])) == 0) return(0); 1225 /* Cannot change status of a Check Valve */ 1226 if (Link[j].Type == CV) return(211); 1227 1228 /*** Updated 9/7/00 ***/ 1229 /* Cannot change setting for a GPV */ 1230 if (Link[j].Type == GPV 1231 && status == ACTIVE) return(211); 1232 1233 changestatus(j,status,y); 1234 } 1235 1236 /* Range of ID's supplied 对间与某2个ID之间的管段的状态的批量设置*/ 1237 else 1238 { 1239 /* Numerical range supplied */ 1240 if ((i0 = atol(Tok[0])) > 0 && (i1 = atol(Tok[1])) > 0) 1241 { 1242 for (j=1; j<=Nlinks; j++) 1243 { 1244 i = atol(Link[j].ID); 1245 if (i >= i0 && i <= i1) changestatus(j,status,y); 1246 } 1247 } 1248 else 1249 for (j=1; j<=Nlinks; j++) 1250 if ( (strcmp(Tok[0],Link[j].ID) <= 0) && 1251 (strcmp(Tok[1],Link[j].ID) >= 0) 1252 ) changestatus(j,status,y); 1253 } 1254 return(0); 1255 } /* end of statusdata */ 1256 1257 1258 int energydata() 1259 /* 1260 **-------------------------------------------------------------- 1261 ** Input: none ;输入:无 1262 ** Output: returns error code ;输出:错误代码 1263 ** Purpose: processes pump energy data ;目的:定义计算水泵提升能量和成本的参数。 1264 ** Formats: ;格式: 1265 ** [ENERGY] 1266 ** GLOBAL {PRICE/PATTERN/EFFIC} value 1267 ** PUMP id {PRICE/PATTERN/EFFIC} value 1268 ** DEMAND CHARGE value 1269 格式: 1270 GLOBAL PRICE/PATTERN/EFFIC value 1271 PUMP PumpID PRICE/PATTERN/EFFIC value 1272 DEMAND CHARGE value 1273 备注: 1274 a. 以关键词GLOBAL为开头的行,用于设置所有水泵的能量价格、价格模式和水泵效 1275 率的全局缺省。 1276 b. 以关键词PUMP为开头的行,用于对特定水泵重新设置全局缺省。 1277 c. 参数定义如下: 1278 z PRICE ——每千瓦时的平均成本, 1279 z PATTERN——描述能量价格怎样变化的时间模式ID标签, 1280 z EFFIC ——对于全局设置的单一百分比效率,或者指定水泵的效率曲线ID标 1281 签, 1282 z DEMAND CHARGE ——模拟时段每最大kW用量增加的成本。 1283 d. 缺省全局水泵效率为75% ,缺省全局能量价格为0。 1284 e. 本节的所有输入是可选的。反斜杠(/)后的项说明允许选项。 1285 示例: 1286 [ENERGY] 1287 GLOBAL PRICE 0.05 ;设置全局能量价格 1288 GLOBAL PATTERN PAT1 ;和一日内时间模式 1289 PUMP 23 PRICE 0.10 ; 重载Pump 23的价格 1290 PUMP 23 EFFIC E23 ;将效率曲线赋给Pump 23 1291 **-------------------------------------------------------------- 1292 */ 1293 { 1294 int j,k,n; 1295 double y; 1296 STmplist *t; 1297 1298 /* Check for sufficient data */ 1299 n = Ntokens; 1300 if (n < 3) return(201); 1301 1302 /* Check first keyword */ 1303 if (match(Tok[0],w_DMNDCHARGE)) /* Demand charge */ 1304 { 1305 if (!getfloat(Tok[2], &y)) return(213); 1306 Dcost = y; 1307 return(0); 1308 } 1309 if (match(Tok[0],w_GLOBAL)) /* Global parameter */ 1310 { 1311 j = 0; 1312 } 1313 else if (match(Tok[0],w_PUMP)) /* Pump-specific parameter */ 1314 { 1315 if (n < 4) return(201); 1316 k = findlink(Tok[1]); /* Check that pump exists */ 1317 if (k == 0) return(216); 1318 if (Link[k].Type != PUMP) return(216); 1319 j = PUMPINDEX(k); 1320 } 1321 else return(201); 1322 1323 /* Find type of energy parameter */ 1324 if (match(Tok[n-2],w_PRICE)) /* Energy price */ 1325 { 1326 if (!getfloat(Tok[n-1],&y)) 1327 { 1328 if (j == 0) return(213); 1329 else return(217); 1330 } 1331 if (j == 0) Ecost = y; 1332 else Pump[j].Ecost = y; 1333 return(0); 1334 } 1335 else if (match(Tok[n-2],w_PATTERN)) /* Price pattern */ 1336 { 1337 t = findID(Tok[n-1],Patlist); /* Check if pattern exists */ 1338 if (t == NULL) 1339 { 1340 if (j == 0) return(213); 1341 else return(217); 1342 } 1343 if (j == 0) Epat = t->i; 1344 else Pump[j].Epat = t->i; 1345 return(0); 1346 } 1347 else if (match(Tok[n-2],w_EFFIC)) /* Pump efficiency */ 1348 { 1349 if (j == 0) 1350 { 1351 if (!getfloat(Tok[n-1], &y)) return(213); 1352 if (y <= 0.0) return(213); 1353 Epump = y; 1354 } 1355 else 1356 { 1357 t = findID(Tok[n-1],Curvelist); /* Check if curve exists */ 1358 if (t == NULL) return(217); 1359 Pump[j].Ecurve = t->i; 1360 } 1361 return(0); 1362 } 1363 return(201); 1364 } 1365 1366 1367 int reportdata() 1368 /* 1369 **-------------------------------------------------------------- 1370 ** Input: none ;输入:无 1371 ** Output: returns error code ;输出:错误代码 1372 ** Purpose: processes report options data ;目的:描述模拟生成的输出报表内容。 1373 ** Formats: 1374 ** PAGE linesperpage 1375 ** STATUS {NONE/YES/FULL} 1376 ** SUMMARY {YES/NO} 1377 ** MESSAGES {YES/NO} 1378 ** ENERGY {NO/YES} 1379 ** NODES {NONE/ALL} 1380 ** NODES node1 node2 ... 1381 ** LINKS {NONE/ALL} 1382 ** LINKS link1 link2 ... 1383 ** FILE filename 1384 ** variable {YES/NO} 1385 ** variable {BELOW/ABOVE/PRECISION} value 1386 格式: 1387 PAGESIZE value 1388 FILE filename 1389 STATUS YES/NO/FULL 1390 SUMMARY YES/NO 1391 ENERGY YES/NO 1392 NODES NONE/ALL/ node1 node2 ... 1393 LINKS NONE/ALL/ link1 link2 ... 1394 parameter YES/NO 1395 parameter BELOW/ABOVE/PRECISION value 1396 定义: 1397 PAGESIZES设置了输出报表中每一页中的行数。缺省为0,意味着事实上每一页没有行数限 1398 制。 1399 对于将要写入的输出报告(在EPANETH的Windows版本中忽略),FILE提供了文件的名字。 1400 STATUS确定了应怎样生成水力状态报告。如果YES 被选择,在模拟的每一时间步长中改变 1401 状态的所有管网组件将输出到报告。如果FULL被选择,那么也将包括每一水力分析的每一试算 1402 中的信息输出到报告。详细水平仅仅对于调试管网是有用的,这时水力不平衡。缺省为NO。 1403 SUMMARY确定了管网组件数量的总结表,以及产生的关键分析选项。缺省为YES 。 1404 ENERGY确定是否提供表格报告平均能量使用和每一台水泵的成本。缺省为NO。 1405 NODES 确定了哪些节点将被报告。可以列出单个节点ID标签,或者利用关键词NONE或者 1406 ALL 。额外NODES 行可用于继续该表。缺省为NONE。 1407 LINKS 确定了哪些管段将被报告。可以列出单个管段ID标签,或者使用关键词NONE或者 1408 106 1409 ALL 。额外LINKS 行可用于继续该表。缺省为NONE。 1410 “参数”报告选项,用于确定报告哪些量,多少小数位被显示,哪种类型的过滤用于限制输 1411 出报告。可以被报告的节点参数包括: 1412 z 标高; 1413 z 需水量; 1414 z 水头; 1415 z 压强; 1416 z 水质。 1417 管段参数包括: 1418 z 长度; 1419 z 直径; 1420 z 流量; 1421 z 流速; 1422 z 水头损失; 1423 z 位置(与状态相同-开启、活动、关闭); 1424 z 设置(对应于管道的粗糙系数、水泵的转速、阀门的压力/流量设置); 1425 z 反应(反应速率); 1426 z F-因子(摩擦因子)。 1427 报告的缺省量对于节点的需水量、水头、压强和水质,以及管段的流量、流速和水头损失。 1428 缺省精度为两个小数位。 1429 备注: 1430 a. 如果在本节没有明确指出,所有选项假设为它们的缺省数值。 1431 b. 反斜杠(/)后的项为可选项。 1432 c. 缺省值对应于任何节点或者管段没有报告,因此如果希望报告这些事项的结果, 1433 必须提供NODES 或者LINKS 选项。 1434 d. 对于EPANETH的Windows版本,仅仅意识到的[REPORT] 选项为STATUS。所 有 其 1435 它被忽略。 1436 示例: 1437 以下示例报告了节点N1, N2, N3 和N17 ,以及所有流速大于3.0 的管段。标准节点参数(需水 1438 量、水头、压强和水质)被报告,同时仅仅管段的流量、流速和F因子(摩擦因子)被报告。 1439 [REPORT] 1440 NODES N1 N2 N3 N17 1441 LINKS ALL 1442 FLOW YES 1443 VELOCITY PRECISION 4 1444 F-FACTOR PRECISION 4 1445 VELOCITY ABOVE 3.0 1446 **-------------------------------------------------------------- 1447 */ 1448 { 1449 int i,j,n; 1450 double y; 1451 1452 n = Ntokens - 1; 1453 if (n < 1) return(201); 1454 1455 /* Value for page size */ 1456 if (match(Tok[0],w_PAGE)) 1457 { 1458 if (!getfloat(Tok[n],&y)) return(213); 1459 if (y < 0.0 || y > 255.0) return(213); 1460 PageSize = (int) y; 1461 return(0); 1462 } 1463 1464 /* Request that status reports be written */ 1465 if (match(Tok[0],w_STATUS)) 1466 { 1467 if (match(Tok[n],w_NO)) Statflag = FALSE; 1468 if (match(Tok[n],w_YES)) Statflag = TRUE; 1469 if (match(Tok[n],w_FULL)) Statflag = FULL; 1470 return(0); 1471 } 1472 1473 /* Request summary report */ 1474 if (match(Tok[0],w_SUMMARY)) 1475 { 1476 if (match(Tok[n],w_NO)) Summaryflag = FALSE; 1477 if (match(Tok[n],w_YES)) Summaryflag = TRUE; 1478 return(0); 1479 } 1480 1481 /* Request error/warning message reporting */ 1482 if (match(Tok[0],w_MESSAGES)) 1483 { 1484 if (match(Tok[n],w_NO)) Messageflag = FALSE; 1485 if (match(Tok[n],w_YES)) Messageflag = TRUE; 1486 return(0); 1487 } 1488 1489 1490 /* Request an energy usage report */ 1491 if (match(Tok[0],w_ENERGY)) 1492 { 1493 if (match(Tok[n],w_NO)) Energyflag = FALSE; 1494 if (match(Tok[n],w_YES)) Energyflag = TRUE; 1495 return(0); 1496 } 1497 1498 /* Particular reporting nodes specified */ 1499 if (match(Tok[0],w_NODE)) 1500 { 1501 if (match(Tok[n],w_NONE)) Nodeflag = 0; /* No nodes */ 1502 else if (match(Tok[n],w_ALL)) Nodeflag = 1; /* All nodes */ 1503 else 1504 { 1505 if (Nnodes == 0) return(208); 1506 for (i=1; i<=n; i++) 1507 { 1508 if ( (j = findnode(Tok[i])) == 0) return(208); 1509 Node[j].Rpt = 1; 1510 } 1511 Nodeflag = 2; 1512 } 1513 return(0); 1514 } 1515 1516 /* Particular reporting links specified */ 1517 if (match(Tok[0],w_LINK)) 1518 { 1519 if (match(Tok[n],w_NONE)) Linkflag = 0; 1520 else if (match(Tok[n],w_ALL)) Linkflag = 1; 1521 else 1522 { 1523 if (Nlinks == 0) return(210); 1524 for (i=1; i<=n; i++) 1525 { 1526 if ( (j = findlink(Tok[i])) == 0) return(210); 1527 Link[j].Rpt = 1; 1528 } 1529 Linkflag = 2; 1530 } 1531 return(0); 1532 } 1533 1534 /* Check if input is a reporting criterion. */ 1535 1536 /*** Special case needed to distinguish "HEAD" from "HEADLOSS" ***/ //(2.00.11 - LR) 1537 if (strcomp(Tok[0], w_HEADLOSS)) i = HEADLOSS; //(2.00.11 - LR) 1538 else i = findmatch(Tok[0],Fldname); //(2.00.11 - LR) 1539 if (i >= 0) //(2.00.11 - LR) 1540 /*****************************************************************/ //(2.00.11 - LR) 1541 { 1542 if (i > FRICTION) return(201); 1543 if (Ntokens == 1 || match(Tok[1],w_YES)) 1544 { 1545 Field[i].Enabled = TRUE; 1546 return(0); 1547 } 1548 if (match(Tok[1],w_NO)) 1549 { 1550 Field[i].Enabled = FALSE; 1551 return(0); 1552 } 1553 if (Ntokens < 3) return(201); 1554 if (match(Tok[1],w_BELOW)) j = LOW; /* Get relation operator */ 1555 else if (match(Tok[1],w_ABOVE)) j = HI; /* or precision keyword */ 1556 else if (match(Tok[1],w_PRECISION)) j = PREC; 1557 else return(201); 1558 if (!getfloat(Tok[2],&y)) return(201); 1559 if (j == PREC) 1560 { 1561 Field[i].Enabled = TRUE; 1562 Field[i].Precision = ROUND(y); 1563 } 1564 else Field[i].RptLim[j] = y; /* Report limit value */ 1565 return(0); 1566 } 1567 1568 /* Name of external report file */ 1569 if (match(Tok[0],w_FILE)) 1570 { 1571 strncpy(Rpt2Fname,Tok[1],MAXFNAME); 1572 return(0); 1573 } 1574 1575 /* If get to here then return error condition */ 1576 return(201); 1577 } /* end of reportdata */ 1578 1579 1580 int timedata() 1581 /* 1582 **-------------------------------------------------------------- 1583 ** Input: none ;输入:无 1584 ** Output: returns error code ;输出:错误代码 1585 ** Purpose: processes time options data ;目的:定义模拟中的各种事件时间步长参数。 1586 ** Formats: 1587 ** STATISTIC {NONE/AVERAGE/MIN/MAX/RANGE} 1588 ** DURATION value (units) 1589 ** HYDRAULIC TIMESTEP value (units) 1590 ** QUALITY TIMESTEP value (units) 1591 ** MINIMUM TRAVELTIME value (units) 1592 ** RULE TIMESTEP value (units) 1593 ** PATTERN TIMESTEP value (units) 1594 ** PATTERN START value (units) 1595 ** REPORT TIMESTEP value (units) 1596 ** REPORT START value (units) 1597 ** START CLOCKTIME value (AM PM) 1598 定义: 1599 DURATION 是模拟的历时。设为0来运行简单的瞬时分析。缺省为0。 1600 HYDRAULIC TIMESTEP 定义了管网新的水力状态计算频率。如果它大于PATTERN或者 1601 REPORT时间步长,将自动降低。缺省为1小时。 1602 QUALITY TIMESTEP用于跟踪水质通过管网变化的时间步长。缺省为水力时间步长的1/10。 1603 RULE TIMESTEP 用于检查水力时间步长之间,基于规则控制引起的系统状态变化的时间步长。 1604 缺省为1/10的水力时间步长。 1605 PATTERN TIMESTEP是所有事件模式中时段之间的间隔。缺省为1小时。 1606 PATTERN START 是所有模式开始时的时间分量。例如,6小时的数值将开始模拟,在时段 1607 中的每一模式,对应于6小时。缺省为0。 1608 REPORT TIMESTEP 设置了输出结果被报告的时间间隔。缺省为1小时。 1609 REPORT START是进入模拟的时间长度,此时输出结果开始报告。缺省为0。 1610 START CLOCKTIME 是模拟开始的钟表时间(例如3:00 PM)。缺省为子夜12:00 AM 。 1611 STATISTICS 确定了在产生模拟结果的时间序列中,统计后处理的类型。AVERAGED 报告了 1612 时间平均结果集合,MINIMUM仅仅报告最小值,MAXIMUM为最大值,以及RANGE 报告了最大值 1613 和最小值之间的差异。NONE报告了所有节点和管段量的完整时间序列,它为缺省的。 1614 备注: 1615 a. 单位应为SECONDS(SEC), MINUTES(MIN), HOURS 或DAYS。缺省为小时。 1616 b. 如果没有提供计量单位,该时间数值可能输入为小数小时或者为小时:分钟。 1617 c. 所有在[TIMES]节的输入是可选的。在反斜杠(/)后的事项说明了可选情况。 1618 **------------------------------------------------------------- 1619 */ 1620 { 1621 int n; 1622 long t; 1623 double y; 1624 1625 n = Ntokens - 1; 1626 if (n < 1) return(201); 1627 1628 /* Check if setting time statistic flag */ 1629 if (match(Tok[0],w_STATISTIC)) 1630 { 1631 if (match(Tok[n],w_NONE)) Tstatflag = SERIES; 1632 else if (match(Tok[n],w_NO)) Tstatflag = SERIES; 1633 else if (match(Tok[n],w_AVG)) Tstatflag = AVG; 1634 else if (match(Tok[n],w_MIN)) Tstatflag = MIN; 1635 else if (match(Tok[n],w_MAX)) Tstatflag = MAX; 1636 else if (match(Tok[n],w_RANGE)) Tstatflag = RANGE; 1637 else return(201); 1638 return(0); 1639 } 1640 1641 /* Convert text time value to numerical value in seconds 将文本时间值转换为数值时间值单位为秒*/ 1642 /* Examples: 1643 ** 5 = 5 * 3600 sec 1644 ** 5 MINUTES = 5 * 60 sec 1645 ** 13:50 = 13*3600 + 50*60 sec 1646 ** 1:50 pm = (12+1)*3600 + 50*60 sec 1647 */ 1648 1649 if (!getfloat(Tok[n],&y)) 1650 { 1651 if ( (y = hour(Tok[n],"")) < 0.0) 1652 { 1653 if ( (y = hour(Tok[n-1],Tok[n])) < 0.0) return(213); 1654 } 1655 } 1656 t = (long)(3600.0*y); 1657 1658 /* Process the value assigned to the matched parameter */ 1659 if (match(Tok[0],w_DURATION)) Dur = t; /* Simulation duration */ 1660 else if (match(Tok[0],w_HYDRAULIC)) Hstep = t; /* Hydraulic time step */ 1661 else if (match(Tok[0],w_QUALITY)) Qstep = t; /* Quality time step */ 1662 else if (match(Tok[0],w_RULE)) Rulestep = t; /* Rule time step */ 1663 else if (match(Tok[0],w_MINIMUM)) return(0); /* Not used anymore */ 1664 else if (match(Tok[0],w_PATTERN)) 1665 { 1666 if (match(Tok[1],w_TIME)) Pstep = t; /* Pattern time step */ 1667 else if (match(Tok[1],w_START)) Pstart = t; /* Pattern start time */ 1668 else return(201); 1669 } 1670 else if (match(Tok[0],w_REPORT)) 1671 { 1672 if (match(Tok[1],w_TIME)) Rstep = t; /* Reporting time step */ 1673 else if (match(Tok[1],w_START)) Rstart = t; /* Reporting start time */ 1674 else return(201); 1675 } /* Simulation start time*/ 1676 else if (match(Tok[0],w_START)) Tstart = t % SECperDAY; 1677 else return(201); 1678 return(0); 1679 } /* end of timedata */ 1680 1681 1682 int optiondata() 1683 /* 1684 **-------------------------------------------------------------- 1685 ** Input: none ;输入:无 1686 ** Output: returns error code ;输出:错误代码 1687 ** Purpose: processes [OPTIONS] data ;目的:定义不同的模拟选项。 1688 **-------------------------------------------------------------- 1689 */ 1690 { 1691 int i,n; 1692 1693 n = Ntokens - 1; 1694 i = optionchoice(n); /* Option is a named choice */ 1695 if (i >= 0) return(i); 1696 return(optionvalue(n)); /* Option is a numerical value */ 1697 } /* end of optiondata */ 1698 1699 1700 int optionchoice(int n) 1701 /* 1702 **-------------------------------------------------------------- 1703 ** Input: n = index of last input token saved in Tok[] 1704 ** Output: returns error code or 0 if option belongs to 1705 ** those listed below, or -1 otherwise 1706 ** Purpose: processes fixed choice [OPTIONS] data 1707 ** Formats: 1708 ** UNITS CFS/GPM/MGD/IMGD/AFD/LPS/LPM/MLD/CMH/CMD/SI 1709 ** PRESSURE PSI/KPA/M 1710 ** HEADLOSS H-W/D-W/C-M 1711 ** HYDRAULICS USE/SAVE filename 1712 ** QUALITY NONE/AGE/TRACE/CHEMICAL (TraceNode) 1713 ** MAP filename 1714 ** VERIFY filename 1715 ** UNBALANCED STOP/CONTINUE {Niter} 1716 ** PATTERN id 1717 **-------------------------------------------------------------- 1718 */ 1719 { 1720 /* Check if 1st token matches a parameter name and */ 1721 /* process the input for the matched parameter */ 1722 if (n < 0) return(201); 1723 if (match(Tok[0],w_UNITS)) 1724 { 1725 if (n < 1) return(0); 1726 else if (match(Tok[1],w_CFS)) Flowflag = CFS; 1727 else if (match(Tok[1],w_GPM)) Flowflag = GPM; 1728 else if (match(Tok[1],w_AFD)) Flowflag = AFD; 1729 else if (match(Tok[1],w_MGD)) Flowflag = MGD; 1730 else if (match(Tok[1],w_IMGD)) Flowflag = IMGD; 1731 else if (match(Tok[1],w_LPS)) Flowflag = LPS; 1732 else if (match(Tok[1],w_LPM)) Flowflag = LPM; 1733 else if (match(Tok[1],w_CMH)) Flowflag = CMH; 1734 else if (match(Tok[1],w_CMD)) Flowflag = CMD; 1735 else if (match(Tok[1],w_MLD)) Flowflag = MLD; 1736 else if (match(Tok[1],w_SI)) Flowflag = LPS; 1737 else return(201); 1738 } 1739 else if (match(Tok[0],w_PRESSURE)) 1740 { 1741 if (n < 1) return(0); 1742 else if (match(Tok[1],w_PSI)) Pressflag = PSI; 1743 else if (match(Tok[1],w_KPA)) Pressflag = KPA; 1744 else if (match(Tok[1],w_METERS)) Pressflag = METERS; 1745 else return(201); 1746 } 1747 else if (match(Tok[0],w_HEADLOSS)) 1748 { 1749 if (n < 1) return(0); 1750 else if (match(Tok[1],w_HW)) Formflag = HW; 1751 else if (match(Tok[1],w_DW)) Formflag = DW; 1752 else if (match(Tok[1],w_CM)) Formflag = CM; 1753 else return(201); 1754 } 1755 else if (match(Tok[0],w_HYDRAULIC)) 1756 { 1757 if (n < 2) return(0); 1758 else if (match(Tok[1],w_USE)) Hydflag = USE; 1759 else if (match(Tok[1],w_SAVE)) Hydflag = SAVE; 1760 else return(201); 1761 strncpy(HydFname,Tok[2],MAXFNAME); 1762 } 1763 else if (match(Tok[0],w_QUALITY)) 1764 { 1765 if (n < 1) return(0); 1766 else if (match(Tok[1],w_NONE)) Qualflag = NONE; 1767 else if (match(Tok[1],w_CHEM)) Qualflag = CHEM; 1768 else if (match(Tok[1],w_AGE)) Qualflag = AGE; 1769 else if (match(Tok[1],w_TRACE)) Qualflag = TRACE; 1770 else 1771 { 1772 Qualflag = CHEM; 1773 strncpy(ChemName,Tok[1],MAXID); 1774 if (n >= 2) strncpy(ChemUnits,Tok[2],MAXID); 1775 } 1776 if (Qualflag == TRACE) /* Source tracing option */ 1777 { 1778 /* Copy Trace Node ID to Tok[0] for error reporting */ 1779 strcpy(Tok[0],""); 1780 if (n < 2) return(212); 1781 strcpy(Tok[0],Tok[2]); 1782 TraceNode = findnode(Tok[2]); 1783 if (TraceNode == 0) return(212); 1784 strncpy(ChemName,u_PERCENT,MAXID); 1785 strncpy(ChemUnits,Tok[2],MAXID); 1786 } 1787 if (Qualflag == AGE) 1788 { 1789 strncpy(ChemName,w_AGE,MAXID); 1790 strncpy(ChemUnits,u_HOURS,MAXID); 1791 } 1792 } 1793 else if (match(Tok[0],w_MAP)) 1794 { 1795 if (n < 1) return(0); 1796 strncpy(MapFname,Tok[1],MAXFNAME); /* Map file name */ 1797 } 1798 else if (match(Tok[0],w_VERIFY)) 1799 { 1800 /* Backward compatibility for verification file */ 1801 } 1802 else if (match(Tok[0],w_UNBALANCED)) /* Unbalanced option */ 1803 { 1804 if (n < 1) return(0); 1805 if (match(Tok[1],w_STOP)) ExtraIter = -1; 1806 else if (match(Tok[1],w_CONTINUE)) 1807 { 1808 if (n >= 2) ExtraIter = atoi(Tok[2]); 1809 else ExtraIter = 0; 1810 } 1811 else return(201); 1812 } 1813 else if (match(Tok[0],w_PATTERN)) /* Pattern option */ 1814 { 1815 if (n < 1) return(0); 1816 strncpy(DefPatID,Tok[1],MAXID); 1817 } 1818 else return(-1); 1819 return(0); 1820 } /* end of optionchoice */ 1821 1822 1823 int optionvalue(int n) 1824 /* 1825 **------------------------------------------------------------- 1826 ** Input: *line = line read from input file 1827 ** Output: returns error code 1828 ** Purpose: processes numerical value [OPTIONS] data 1829 ** Formats: 1830 ** DEMAND MULTIPLIER value 1831 ** EMITTER EXPONENT value 1832 ** VISCOSITY value 1833 ** DIFFUSIVITY value 1834 ** SPECIFIC GRAVITY value 1835 ** TRIALS value 1836 ** ACCURACY value 1837 ** TOLERANCE value 1838 ** SEGMENTS value (not used) 1839 ** ------ Undocumented Options ----- 1840 ** HTOL value 1841 ** QTOL value 1842 ** RQTOL value 1843 ** CHECKFREQ value 1844 ** MAXCHECK value 1845 ** DAMPLIMIT value //(2.00.12 - LR) 1846 **-------------------------------------------------------------- 1847 UNITS 设置了流量被表达的单位: 1848 LPS ——升/秒 1849 LPM ——升/分 1850 MLD ——百万升/日 1851 CMH ——立方米/小时 1852 CMG ——立方米/日 1853 CFS ——立方英尺/秒 1854 GPM ——加仑/分 1855 MGD ——百万加仑/日 1856 IMGD——英制MGD 1857 AFD ——英亩-英尺/日 1858 如果流量单位为升或者立方米,那么公制单位也必须用于所有其它输入量。对于CFS, GPM, 1859 MGD, IMGD和AFD ,其它输入量表达为美制单位。(计量单位参见附录A)。缺省流量单位为GPM 。 1860 HEADLOSS 选择了用于计算通过管道水流的水头损失公式。包括Hazen-Williams(H-W ), 1861 Darcy-Weisbach(D-W)或Chezy-Manning (C-M)公式。缺省为H-W 。 1862 HYDRAULICS 选项允许将当前水力结果SAVE(保存)到文件,或者USE (利用)以前保存的 1863 水力结果。当研究仅仅影响水质行为的因子时,很有用。 1864 QUALITY选择了执行水质分析的类型。选择包括NONE(无), CHEMICAL(化学药剂), AGE 1865 (水龄)和TRACE (跟踪)。在CHEMI CAL 情况中,化合物的实际名称之后为其浓度单位(例如 1866 CHLORINE mg/L )。如果TRACE 被选择,必须跟踪节点的ID标签。缺省选项为NONE(没有水 1867 质分析)。 1868 VISCOSITY是被模拟流体的运动粘度,相对于20摄氏度时的情况(1.0 厘斯)。缺省值为 1869 1.0 。 1870 DIFFUSIVITY是化合物的分析扩散系数,相对于水中的氯情况。缺省值为1.0 。扩散系数 1871 仅仅适用于管壁反应中考虑质量转换限制时。数值0将造成EPANETH忽略质量转换限制。 1872 SPECIFIC GRAVITY是被模拟流体密度与4摄氏度水的密度之比(无量纲)。 1873 TRIALS是在每一模拟水力时间步长中,求解管网水力特性使用的最大试算次数。缺省为40。 1874 ACCURACY 指定了确定何时达到水力结果的收敛准则。当所有流量总和改变,来自原先求解 1875 除以所有管段的总流量低于该数值时,试算中止。缺省为0.001 。 1876 UNBALANCED 确定了何时进行,如果水力结果在指定的TRIAL 数值内不能够达到,对于水力 1877 时间步长来模拟。“STOP”将在该点终止整个分析。“CONTINUE ”将在公布警告消息的情况 1878 下继续分析。“CONTINUE n ”将在另外“n”次试算中搜索结果,所有管线状态保持它们的当 1879 前设置。模拟将在该点继续,关于收敛是否达到,具有消息公布。缺省选项为“STOP”。 1880 PATTERN提供了用于没有指定需水量模式的所有节点缺省需水量模式的ID标签。如果没有 1881 这样的模式在[PATTERNS] 节中存在,那么通过缺省的模式,包含一个等于1.0 的单一乘子。如 1882 果没有使用该选项,总体缺省需水量模式具有标签“1”。 1883 DEMAND MULTIPLIER用于调整所有连接节点的基本需水量数值,以及所有需水量的类型。 1884 例如,数值2为两倍的基准需水量,而数值0.5 将为它们的一半。缺省值为1.0 。 1885 EMITTER EXPONENT指定了当计算扩散器的流量时,节点压力上升的幂指数。缺省为0.5 。 1886 MAP 用于提供包含了管网节点坐标的文件名称,以便绘制管网地图。对于任何水力或者水质 1887 计算这是无用的。 1888 TOLERANCE是水质水平精度。对于所有水质分析(化合物、水龄(以小时度量),或者源 1889 头跟踪(以百分比度量)),缺省值为0.01。 1890 备注: 1891 a. 如果在本节没有明确指定,所有选项假设为缺省数值。 1892 b. 反斜杠(/)后的项说明为允许选项。 1893 */ 1894 { 1895 int nvalue = 1; /* Index of token with numerical value */ 1896 double y; 1897 1898 /* Check for obsolete SEGMENTS keyword */ 1899 if (match(Tok[0],w_SEGMENTS)) return(0); 1900 1901 /* Check for missing value (which is permissible) */ 1902 if (match(Tok[0],w_SPECGRAV) || match(Tok[0],w_EMITTER) 1903 || match(Tok[0],w_DEMAND)) nvalue = 2; 1904 if (n < nvalue) return(0); 1905 1906 /* Check for valid numerical input */ 1907 if (!getfloat(Tok[nvalue],&y)) return(213); 1908 1909 /* Check for WQ tolerance option (which can be 0) */ 1910 if (match(Tok[0],w_TOLERANCE)) 1911 { 1912 if (y < 0.0) return(213); 1913 Ctol = y; /* Quality tolerance*/ 1914 return(0); 1915 } 1916 1917 /* Check for Diffusivity option */ 1918 if (match(Tok[0],w_DIFFUSIVITY)) 1919 { 1920 if (y < 0.0) return(213); 1921 Diffus = y; 1922 return(0); 1923 } 1924 1925 /* Check for Damping Limit option */ //(2.00.12 - LR) 1926 if (match(Tok[0],w_DAMPLIMIT)) 1927 { 1928 DampLimit = y; 1929 return(0); 1930 } 1931 1932 /* All other options must be > 0 */ 1933 if (y <= 0.0) return(213); 1934 1935 /* Assign value to specified option */ 1936 if (match(Tok[0],w_VISCOSITY)) Viscos = y; /* Viscosity */ 1937 else if (match(Tok[0],w_SPECGRAV)) SpGrav = y; /* Spec. gravity */ 1938 else if (match(Tok[0],w_TRIALS)) MaxIter = (int)y; /* Max. trials */ 1939 else if (match(Tok[0],w_ACCURACY)) /* Accuracy */ 1940 { 1941 y = MAX(y,1.e-5); 1942 y = MIN(y,1.e-1); 1943 Hacc = y; 1944 } 1945 else if (match(Tok[0],w_HTOL)) Htol = y; 1946 else if (match(Tok[0],w_QTOL)) Qtol = y; 1947 else if (match(Tok[0],w_RQTOL)) 1948 { 1949 if (y >= 1.0) return(213); 1950 RQtol = y; 1951 } 1952 else if (match(Tok[0],w_CHECKFREQ)) CheckFreq = (int)y; 1953 else if (match(Tok[0],w_MAXCHECK)) MaxCheck = (int)y; 1954 else if (match(Tok[0],w_EMITTER)) Qexp = 1.0/y; 1955 else if (match(Tok[0],w_DEMAND)) Dmult = y; 1956 else return(201); 1957 return(0); 1958 } /* end of optionvalue */ 1959 1960 1961 int getpumpcurve(int n) 1962 /* 1963 **-------------------------------------------------------- 1964 ** Input: n = number of parameters for pump curve 1965 ** Output: returns error code 1966 ** Purpose: processes pump curve data for Version 1.1- 1967 ** style input data 1968 ** Notes: 1969 ** 1. Called by pumpdata() in INPUT3.C 1970 ** 2. Current link index & pump index of pump being 1971 ** processed is found in global variables Nlinks 1972 ** and Npumps, respectively 1973 ** 3. Curve data read from input line is found in 1974 ** global variables X[0],...X[n-1] 1975 **--------------------------------------------------------- 1976 */ 1977 { 1978 double a,b,c,h0,h1,h2,q1,q2; 1979 1980 if (n == 1) /* Const. HP curve */ 1981 { 1982 if (X[0] <= 0.0) return(202); 1983 Pump[Npumps].Ptype = CONST_HP; 1984 Link[Nlinks].Km = X[0]; 1985 } 1986 else 1987 { 1988 if (n == 2) /* Generic power curve */ 1989 { 1990 q1 = X[1]; 1991 h1 = X[0]; 1992 h0 = 1.33334*h1; 1993 q2 = 2.0*q1; 1994 h2 = 0.0; 1995 } 1996 else if (n >= 5) /* 3-pt. power curve */ 1997 { 1998 h0 = X[0]; 1999 h1 = X[1]; 2000 q1 = X[2]; 2001 h2 = X[3]; 2002 q2 = X[4]; 2003 } 2004 else return(202); 2005 Pump[Npumps].Ptype = POWER_FUNC; 2006 if (!powercurve(h0,h1,h2,q1,q2,&a,&b,&c)) return(206); 2007 Pump[Npumps].H0 = -a; 2008 Pump[Npumps].R = -b; 2009 Pump[Npumps].N = c; 2010 Pump[Npumps].Q0 = q1; 2011 Pump[Npumps].Qmax = pow((-a/b),(1.0/c)); 2012 Pump[Npumps].Hmax = h0; 2013 } 2014 return(0); 2015 } 2016 2017 2018 int powercurve(double h0, double h1, double h2, double q1, 2019 double q2, double *a, double *b, double *c) 2020 /* 2021 **--------------------------------------------------------- 2022 ** Input: h0 = shutoff head 输入:h0 =最大水头 2023 ** h1 = design head h1 =设计水头 2024 ** h2 = head at max. flow h2 =最大流量时的水头 2025 ** q1 = design flow q1 =设计流量 2026 ** q2 = max. flow q2 =最大流量 2027 ** Output: *a, *b, *c = pump curve coeffs. (H = a-bQ^c),输出:返回水泵扬程-流量公式中的3参数 2028 ** Returns 1 if sucessful, 0 otherwise. 2029 ** Purpose: computes coeffs. for pump curve 目的:计算水泵曲线的方程系数 2030 **---------------------------------------------------------- 2031 */ 2032 { 2033 double h4,h5; 2034 if ( 2035 h0 < TINY || 2036 h0 - h1 < TINY || 2037 h1 - h2 < TINY || 2038 q1 < TINY || 2039 q2 - q1 < TINY 2040 ) return(0); 2041 *a = h0; 2042 h4 = h0 - h1; 2043 h5 = h0 - h2; 2044 *c = log(h5/h4)/log(q2/q1); 2045 if (*c <= 0.0 || *c > 20.0) return(0); 2046 *b = -h4/pow(q1,*c); 2047 2048 /*** Updated 6/24/02 ***/ 2049 if (*b >= 0.0) return(0); 2050 2051 return(1); 2052 } 2053 2054 2055 int valvecheck(int type, int j1, int j2) 2056 /* 2057 **-------------------------------------------------------------- 2058 ** Input: type = valve type 阀门类型 2059 ** j1 = index of upstream node 上游节点索引值 2060 ** j2 = index of downstream node 下游节点索引值 2061 ** Output: returns 1 for legal connection, 0 otherwise 返回1为合法阀门情况 2062 ** Purpose: checks for legal connections between PRVs & PSVs 作用:检查多个阀门之间共享节点等非法情况 2063 **-------------------------------------------------------------- 2064 */ 2065 { 2066 int k, vk, vj1, vj2, vtype; 2067 2068 /* Examine each existing valve */ 2069 for (k=1; k<=Nvalves; k++) 2070 { 2071 vk = Valve[k].Link; 2072 vj1 = Link[vk].N1; 2073 vj2 = Link[vk].N2; 2074 vtype = Link[vk].Type; 2075 2076 /* Cannot have two PRVs sharing downstream nodes or in series */ 2077 if (vtype == PRV && type == PRV) 2078 { 2079 if (vj2 == j2 || 2080 vj2 == j1 || 2081 vj1 == j2 ) return(0); 2082 } 2083 2084 /* Cannot have two PSVs sharing upstream nodes or in series */ 2085 if (vtype == PSV && type == PSV) 2086 { 2087 if (vj1 == j1 || 2088 vj1 == j2 || 2089 vj2 == j1 ) return(0); 2090 } 2091 2092 /* Cannot have PSV connected to downstream node of PRV */ 2093 if (vtype == PSV && type == PRV && vj1 == j2) return(0); 2094 if (vtype == PRV && type == PSV && vj2 == j1) return(0); 2095 2096 /*** Updated 3/1/01 ***/ 2097 /* Cannot have PSV connected to downstream node of FCV */ 2098 /* nor have PRV connected to upstream node of FCV */ 2099 if (vtype == FCV && type == PSV && vj2 == j1) return(0); 2100 if (vtype == FCV && type == PRV && vj1 == j2) return(0); 2101 2102 /*** Updated 4/14/05 ***/ 2103 if (vtype == PSV && type == FCV && vj1 == j2) return (0); 2104 if (vtype == PRV && type == FCV && vj2 == j1) return (0); 2105 } 2106 return(1); 2107 } /* End of valvecheck */ 2108 2109 2110 void changestatus(int j, char status, double y) 2111 /* 2112 **-------------------------------------------------------------- 2113 ** Input: j = link index 2114 ** status = status setting (OPEN, CLOSED) //这里就阐释了status与setting的关系 2115 ** y = numerical setting (pump speed, valve 2116 ** setting) 2117 ** Output: none 2118 ** Purpose: changes status or setting of a link 2119 ** 2120 ** NOTE: If status = ACTIVE, then a numerical setting (y) was //如果status = ACTIVE,那么一个数值设定y将提供用于表示水泵的转速,或者阀门的开度等 2121 ** supplied. If status = OPEN/CLOSED, then numerical 2122 ** setting is 0. 2123 **-------------------------------------------------------------- 2124 */ 2125 { 2126 if (Link[j].Type == PIPE || Link[j].Type == GPV) 2127 { 2128 if (status != ACTIVE) Link[j].Stat = status; 2129 } 2130 else if (Link[j].Type == PUMP) 2131 { 2132 if (status == ACTIVE) 2133 { 2134 Link[j].Kc = y; 2135 status = OPEN; 2136 if (y == 0.0) status = CLOSED; 2137 } 2138 else if (status == OPEN) Link[j].Kc = 1.0; 2139 Link[j].Stat = status; 2140 } 2141 else if (Link[j].Type >= PRV) 2142 { 2143 Link[j].Kc = y; 2144 Link[j].Stat = status; 2145 if (status != ACTIVE) Link[j].Kc = MISSING; 2146 } 2147 } /* end of changestatus */ 2148 2149 /********************** END OF INPUT3.C ************************/