/* ********************************************************************** INPUT3.C -- Input data parser for EPANET VERSION: 2.00 DATE: 5/30/00 9/7/00 10/25/00 3/1/01 6/24/02 8/15/07 (2.00.11) 2/14/08 (2.00.12) AUTHOR: L. Rossman US EPA - NRMRL This module parses data from each line of input from file InFile. All functions in this module are called from newline() in INPUT2.C. ********************************************************************** */ #include #include #include #ifndef __APPLE__ #include #endif #include "epanet2.h" #include "funcs.h" #include "hash.h" #include "text.h" #include "types.h" #include #define EXTERN extern #include "vars.h" /* Defined in enumstxt.h in EPANET.C */ extern char *MixTxt[]; extern char *Fldname[]; /* Defined in INPUT2.C */ int juncdata(EN_Project *pr) /* **-------------------------------------------------------------- ** Input: none ** Output: returns error code ** Purpose: processes junction data ** Format: ** [JUNCTIONS] ** id elev. (demand) (demand pattern) **-------------------------------------------------------------- */ { int p = 0; double el, y = 0.0; Pdemand demand; STmplist *pat; EN_Network *net = &pr->network; parser_data_t *par = &pr->parser; hydraulics_t *hyd = &pr->hydraulics; int n; int Njuncs; Snode *node; /* Add new junction to data base */ n = par->Ntokens; if (net->Nnodes == par->MaxNodes) { return (200); } net->Njuncs++; net->Nnodes++; Njuncs = net->Njuncs; if (!addnodeID(net, net->Njuncs, par->Tok[0])) { return (215); } /* Check for valid data */ if (n < 2) return (201); if (!getfloat(par->Tok[1], &el)) return (202); if (n >= 3 && !getfloat(par->Tok[2], &y)) return (202); if (n >= 4) { pat = findID(par->Tok[3], par->Patlist); if (pat == NULL) return (205); p = pat->i; } /* Save junction data */ node = &net->Node[Njuncs]; node->El = el; node->C0 = 0.0; node->S = NULL; node->Ke = 0.0; node->Rpt = 0; node->Type = EN_JUNCTION; strcpy(node->Comment, par->Comment); // create a demand record, even if no demand is specified here. // perhaps the [DEMANDS] section contains data, but not always. demand = (struct Sdemand *) malloc(sizeof(struct Sdemand)); if (demand == NULL) { return(101); } demand->Base = y; demand->Pat = p; demand->next = NULL; node->D = demand; hyd->NodeDemand[Njuncs] = y; return (0); } /* end of juncdata */ int tankdata(EN_Project *pr) /* **-------------------------------------------------------------- ** Input: none ** Output: returns error code ** Purpose: processes tank & reservoir data ** Format: ** [RESERVOIRS] ** id elev (pattern) ** [TANKS] ** id elev (pattern) ** id elev initlevel minlevel maxlevel diam (minvol vcurve) **-------------------------------------------------------------- */ { int n, /* # data items */ p = 0, /* Fixed grade time pattern index */ vcurve = 0; /* Volume curve index */ double el = 0.0, /* Elevation */ initlevel = 0.0, /* Initial level */ minlevel = 0.0, /* Minimum level */ maxlevel = 0.0, /* Maximum level */ minvol = 0.0, /* Minimum volume */ diam = 0.0, /* Diameter */ area; /* X-sect. area */ STmplist *t; EN_Network *net = &pr->network; parser_data_t *par = &pr->parser; Snode *node; Stank *tank; int i; /* Add new tank to data base */ n = par->Ntokens; if (net->Ntanks == par->MaxTanks || net->Nnodes == par->MaxNodes) { return (200); } net->Ntanks++; net->Nnodes++; i = par->MaxJuncs + net->Ntanks; /* i = node index. */ if (!addnodeID(net, i, par->Tok[0])) { return (215); /* Add ID to database. */ } /* Check for valid data */ if (n < 2) return (201); /* Too few fields. */ if (!getfloat(par->Tok[1], &el)) return (202); /* Read elevation */ if (n <= 3) { /* Tank is reservoir.*/ if (n == 3) { /* Pattern supplied */ t = findID(par->Tok[2], par->Patlist); if (t == NULL) return (205); p = t->i; } } else if (n < 6) { return (201); /* Too few fields for tank.*/ } else { /* Check for valid input data */ if (!getfloat(par->Tok[2], &initlevel)) return (202); if (!getfloat(par->Tok[3], &minlevel)) return (202); if (!getfloat(par->Tok[4], &maxlevel)) return (202); if (!getfloat(par->Tok[5], &diam)) return (202); if (diam < 0.0) return (202); if (n >= 7 && !getfloat(par->Tok[6], &minvol)) return (202); /* If volume curve supplied check it exists */ if (n == 8) { t = findID(par->Tok[7], par->Curvelist); if (t == NULL) { return (202); } vcurve = t->i; net->Curve[t->i].Type = V_CURVE; } } node = &net->Node[i]; tank = &net->Tank[net->Ntanks]; node->Rpt = 0; node->El = el; /* Elevation. */ node->C0 = 0.0; /* Init. quality. */ node->S = NULL; /* WQ source data */ node->Ke = 0.0; /* Emitter coeff. */ node->Type = (diam == 0) ? EN_RESERVOIR : EN_TANK; strcpy(node->Comment, par->Comment); tank->Node = i; /* Node index. */ tank->H0 = initlevel; /* Init. level. */ tank->Hmin = minlevel; /* Min. level. */ tank->Hmax = maxlevel; /* Max level. */ tank->A = diam; /* Diameter. */ tank->Pat = p; /* Fixed grade pattern. */ tank->Kb = MISSING; /* Reaction coeff. */ /* ******************************************************************* NOTE: The min, max, & initial volumes set here are based on a nominal tank diameter. They will be modified in INPUT1.C if a volume curve is supplied for this tank. ******************************************************************* */ area = PI * SQR(diam) / 4.0; tank->Vmin = area * minlevel; if (minvol > 0.0) tank->Vmin = minvol; tank->V0 = tank->Vmin + area * (initlevel - minlevel); tank->Vmax = tank->Vmin + area * (maxlevel - minlevel); tank->Vcurve = vcurve; /* Volume curve */ tank->MixModel = MIX1; /* Completely mixed */ tank->V1max = 1.0; /* Compart. size ratio */ return (0); } /* end of tankdata */ int pipedata(EN_Project *pr) /* **-------------------------------------------------------------- ** Input: none ** Output: returns error code ** Purpose: processes pipe data ** Format: ** [PIPE] ** id node1 node2 length diam rcoeff (lcoeff) (status) **-------------------------------------------------------------- */ { int j1, /* Start-node index */ j2, /* End-node index */ n; /* # data items */ EN_LinkType type = EN_PIPE; /* Link type */ StatType status = OPEN; /* Link status */ double length, /* Link length */ diam, /* Link diameter */ rcoeff, /* Roughness coeff. */ lcoeff = 0.0; /* Minor loss coeff. */ EN_Network *net = &pr->network; parser_data_t *par = &pr->parser; Slink *link; /* Add new pipe to data base */ n = par->Ntokens; if (net->Nlinks == par->MaxLinks) return (200); net->Npipes++; net->Nlinks++; if (!addlinkID(net, net->Nlinks, par->Tok[0])) return (215); /* Check for valid data */ if (n < 6) return (201); if ((j1 = findnode(net,par->Tok[1])) == 0 || (j2 = findnode(net,par->Tok[2])) == 0) return (203); /*** Updated 10/25/00 ***/ if (j1 == j2) return (222); if (!getfloat(par->Tok[3], &length) || !getfloat(par->Tok[4], &diam) || !getfloat(par->Tok[5], &rcoeff)) return (202); if (length <= 0.0 || diam <= 0.0 || rcoeff <= 0.0) return (202); /* Case where either loss coeff. or status supplied */ if (n == 7) { if (match(par->Tok[6], w_CV)) type = EN_CVPIPE; else if (match(par->Tok[6], w_CLOSED)) status = CLOSED; else if (match(par->Tok[6], w_OPEN)) status = OPEN; else if (!getfloat(par->Tok[6], &lcoeff)) return (202); } /* Case where both loss coeff. and status supplied */ if (n == 8) { if (!getfloat(par->Tok[6], &lcoeff)) return (202); if (match(par->Tok[7], w_CV)) type = EN_CVPIPE; else if (match(par->Tok[7], w_CLOSED)) status = CLOSED; else if (match(par->Tok[7], w_OPEN)) status = OPEN; else return (202); } if (lcoeff < 0.0) return (202); /* Save pipe data */ link = &net->Link[net->Nlinks]; link->N1 = j1; /* Start-node index */ link->N2 = j2; /* End-node index */ link->Len = length; /* Length */ link->Diam = diam; /* Diameter */ link->Kc = rcoeff; /* Rough. coeff */ link->Km = lcoeff; /* Loss coeff */ link->Kb = MISSING; /* Bulk coeff */ link->Kw = MISSING; /* Wall coeff */ link->Type = type; /* Link type */ link->Stat = status; /* Link status */ link->Rpt = 0; /* Report flag */ strcpy(link->Comment, par->Comment); return (0); } /* end of pipedata */ int pumpdata(EN_Project *pr) /* **-------------------------------------------------------------- ** Input: none ** Output: returns error code ** Purpose: processes pump data ** Formats: ** [PUMP] ** (Version 1.x Format): ** id node1 node2 power ** id node1 node2 h1 q1 ** id node1 node2 h0 h1 q1 h2 q2 ** (Version 2 Format): ** id node1 node2 KEYWORD value {KEYWORD value ...} ** where KEYWORD = [POWER,HEAD,PATTERN,SPEED] **-------------------------------------------------------------- */ { int j, j1, /* Start-node index */ j2, /* End-node index */ m, n; /* # data items */ double y; STmplist *t; /* Pattern record */ EN_Network *net = &pr->network; parser_data_t *par = &pr->parser; Slink *link; Spump *pump; /* Add new pump to data base */ n = par->Ntokens; if (net->Nlinks == par->MaxLinks || net->Npumps == par->MaxPumps) return (200); net->Nlinks++; net->Npumps++; if (!addlinkID(net, net->Nlinks, par->Tok[0])) return (215); /* Check for valid data */ if (n < 4) return (201); if ((j1 = findnode(net,par->Tok[1])) == 0 || (j2 = findnode(net,par->Tok[2])) == 0) return (203); /*** Updated 10/25/00 ***/ if (j1 == j2) return (222); /* Save pump data */ link = &net->Link[net->Nlinks]; pump = &net->Pump[net->Npumps]; link->N1 = j1; /* Start-node index. */ link->N2 = j2; /* End-node index. */ link->Diam = 0; /* no longer Pump index. */ link->Len = 0.0; /* Link length. */ link->Kc = 1.0; /* Speed factor. */ link->Km = 0.0; /* Horsepower. */ link->Kb = 0.0; link->Kw = 0.0; link->Type = EN_PUMP; /* Link type. */ link->Stat = OPEN; /* Link status. */ link->Rpt = 0; /* Report flag. */ strcpy(link->Comment, par->Comment); pump->Link = net->Nlinks; /* Link index. */ pump->Ptype = NOCURVE; /* Type of pump curve -- "NOCURVE" is a placeholder. this may be modified in getpumpparams() */ pump->Hcurve = 0; /* Pump curve index */ pump->Ecurve = 0; /* Effic. curve index */ pump->Upat = 0; /* Utilization pattern*/ pump->Ecost = 0.0; /* Unit energy cost */ pump->Epat = 0; /* Energy cost pattern*/ /* If 4-th token is a number then input follows Version 1.x format */ /* so retrieve pump curve parameters */ if (getfloat(par->Tok[3], &par->X[0])) { m = 1; for (j = 4; j < n; j++) { if (!getfloat(par->Tok[j], &par->X[m])) { return (202); } m++; } return (getpumpcurve(pr,m)); /* Get pump curve params */ } /* Otherwise input follows Version 2 format */ /* so retrieve keyword/value pairs. */ m = 4; while (m < n) { if (match(par->Tok[m - 1], w_POWER)) /* Const. HP curve */ { y = atof(par->Tok[m]); if (y <= 0.0) return (202); pump->Ptype = CONST_HP; link->Km = y; } else if (match(par->Tok[m - 1], w_HEAD)) /* Custom pump curve */ { t = findID(par->Tok[m], par->Curvelist); if (t == NULL) return (206); pump->Hcurve = t->i; } else if (match(par->Tok[m - 1], w_PATTERN)) /* Speed/status pattern */ { t = findID(par->Tok[m], par->Patlist); if (t == NULL) return (205); pump->Upat = t->i; } else if (match(par->Tok[m - 1], w_SPEED)) /* Speed setting */ { if (!getfloat(par->Tok[m], &y)) return (202); if (y < 0.0) return (202); link->Kc = y; } else { return (201); } m = m + 2; /* Skip to next keyword token */ } return (0); } /* end of pumpdata */ int valvedata(EN_Project *pr) /* **-------------------------------------------------------------- ** Input: none ** Output: returns error code ** Purpose: processes valve data ** Format: ** [VALVE] ** id node1 node2 diam type setting (lcoeff) **-------------------------------------------------------------- */ { int j1, /* Start-node index */ j2, /* End-node index */ n; /* # data items */ char status = ACTIVE, /* Valve status */ type; /* Valve type */ double diam = 0.0, /* Valve diameter */ setting, /* Valve setting */ lcoeff = 0.0; /* Minor loss coeff. */ STmplist *t; /* Curve record */ EN_Network *net = &pr->network; parser_data_t *par = &pr->parser; Slink *link; /* Add new valve to data base */ n = par->Ntokens; if (net->Nlinks == par->MaxLinks || net->Nvalves == par->MaxValves) return (200); net->Nvalves++; net->Nlinks++; if (!addlinkID(net, net->Nlinks, par->Tok[0])) return (215); /* Check for valid data */ if (n < 6) return (201); if ((j1 = findnode(net,par->Tok[1])) == 0 || (j2 = findnode(net,par->Tok[2])) == 0) return (203); /*** Updated 10/25/00 ***/ if (j1 == j2) return (222); if (match(par->Tok[4], w_PRV)) type = EN_PRV; else if (match(par->Tok[4], w_PSV)) type = EN_PSV; else if (match(par->Tok[4], w_PBV)) type = EN_PBV; else if (match(par->Tok[4], w_FCV)) type = EN_FCV; else if (match(par->Tok[4], w_TCV)) type = EN_TCV; else if (match(par->Tok[4], w_GPV)) type = EN_GPV; else return (201); /* Illegal valve type.*/ if (!getfloat(par->Tok[3], &diam)) return (202); if (diam <= 0.0) return (202); /* Illegal diameter.*/ if (type == EN_GPV) { /* Headloss curve for GPV */ t = findID(par->Tok[5], par->Curvelist); if (t == NULL) { return (206); } setting = t->i; net->Curve[t->i].Type = H_CURVE; /*** Updated 9/7/00 ***/ status = OPEN; } else if (!getfloat(par->Tok[5], &setting)) return (202); if (n >= 7 && !getfloat(par->Tok[6], &lcoeff)) return (202); /* Check that PRV, PSV, or FCV not connected to a tank & */ /* check for illegal connections between pairs of valves.*/ if ((j1 > net->Njuncs || j2 > net->Njuncs) && (type == EN_PRV || type == EN_PSV || type == EN_FCV)) return (219); if (!valvecheck(pr, type, j1, j2)) return (220); /* Save valve data */ link = &net->Link[net->Nlinks]; link->N1 = j1; /* Start-node index. */ link->N2 = j2; /* End-node index. */ link->Diam = diam; /* Valve diameter. */ link->Len = 0.0; /* Link length. */ link->Kc = setting; /* Valve setting. */ link->Km = lcoeff; /* Loss coeff */ link->Kb = 0.0; link->Kw = 0.0; link->Type = type; /* Valve type. */ link->Stat = status; /* Valve status. */ link->Rpt = 0; /* Report flag. */ strcpy(link->Comment, par->Comment); net->Valve[net->Nvalves].Link = net->Nlinks; /* Link index. */ return (0); } /* end of valvedata */ int patterndata(EN_Project *pr) /* **-------------------------------------------------------------- ** Input: none ** Output: returns error code ** Purpose: processes time pattern data ** Format: ** [PATTERNS] ** id mult1 mult2 ..... **-------------------------------------------------------------- */ { EN_Network *net = &pr->network; parser_data_t *par = &pr->parser; int i, n; double x; SFloatlist *f; STmplist *p; n = par->Ntokens - 1; if (n < 1) return (201); /* Too few values */ if ( /* Check for new pattern */ par->PrevPat != NULL && strcmp(par->Tok[0], par->PrevPat->ID) == 0) p = par->PrevPat; else p = findID(par->Tok[0], par->Patlist); if (p == NULL) return (205); for (i = 1; i <= n; i++) /* Add multipliers to list */ { if (!getfloat(par->Tok[i], &x)) return (202); f = (SFloatlist *)malloc(sizeof(SFloatlist)); if (f == NULL) return (101); f->value = x; f->next = p->x; p->x = f; } net->Pattern[p->i].Length += n; /* Save # multipliers for pattern */ par->PrevPat = p; /* Set previous pattern pointer */ return (0); } /* end of patterndata */ int curvedata(EN_Project *pr) /* **------------------------------------------------------ ** Input: none ** Output: returns error code ** Purpose: processes curve data ** Format: ** [CURVES] ** CurveID x-value y-value **------------------------------------------------------ */ { EN_Network *net = &pr->network; parser_data_t *par = &pr->parser; double x, y; SFloatlist *fx, *fy; STmplist *c; /* Check for valid curve ID */ if (par->Ntokens < 3) return (201); if (par->PrevCurve != NULL && strcmp(par->Tok[0], par->PrevCurve->ID) == 0) c = par->PrevCurve; else c = findID(par->Tok[0], par->Curvelist); if (c == NULL) return (205); /* Check for valid data */ if (!getfloat(par->Tok[1], &x)) return (202); if (!getfloat(par->Tok[2], &y)) return (202); /* Add new data point to curve's linked list */ fx = (SFloatlist *)malloc(sizeof(SFloatlist)); fy = (SFloatlist *)malloc(sizeof(SFloatlist)); if (fx == NULL || fy == NULL) { return (101); } fx->value = x; fx->next = c->x; c->x = fx; fy->value = y; fy->next = c->y; c->y = fy; net->Curve[c->i].Npts++; /* Save the pointer to this curve */ par->PrevCurve = c; return (0); } int coordata(EN_Project *pr) /* **-------------------------------------------------------------- ** Input: none ** Output: returns error code ** Purpose: processes coordinate data ** Format: ** [COORD] ** id x y **-------------------------------------------------------------- */ { EN_Network *net = &pr->network; parser_data_t *par = &pr->parser; double x, y; int j; Scoord *coord; Snode *node; /* Check for valid node ID */ if (par->Ntokens < 3) return (201); /* Check for valid data */ if ((j = findnode(net, par->Tok[0])) == 0) return (203); if (!getfloat(par->Tok[1], &x)) return (202); if (!getfloat(par->Tok[2], &y)) return (202); /* Save coord data */ coord = &net->Coord[j]; node = &net->Node[j]; strncpy(coord->ID, node->ID, MAXID); coord->X = x; coord->Y = y; coord->HaveCoords = TRUE; return (0); } /* end of coordata */ int demanddata(EN_Project *pr) /* **-------------------------------------------------------------- ** Input: none ** Output: returns error code ** Purpose: processes node demand data ** Format: ** [DEMANDS] ** MULTIPLY factor ** node base_demand (pattern) ** ** NOTE: Demands entered in this section replace those ** entered in the [JUNCTIONS] section **-------------------------------------------------------------- */ { EN_Network *net = &pr->network; hydraulics_t *hyd = &pr->hydraulics; parser_data_t *par = &pr->parser; int j, n, p = 0; double y; Pdemand demand; Pdemand cur_demand; STmplist *pat; /* Extract data from tokens */ n = par->Ntokens; if (n < 2) return (201); if (!getfloat(par->Tok[1], &y)) return (202); /* If MULTIPLY command, save multiplier */ if (match(par->Tok[0], w_MULTIPLY)) { if (y <= 0.0) return (202); else hyd->Dmult = y; return (0); } /* Otherwise find node (and pattern) being referenced */ if ((j = findnode(net, par->Tok[0])) == 0) return (208); if (j > net->Njuncs) return (208); if (n >= 3) { pat = findID(par->Tok[2], par->Patlist); if (pat == NULL) return (205); p = pat->i; } /* Replace any demand entered in [JUNCTIONS] section */ demand = net->Node[j].D; if (hyd->NodeDemand[j] != MISSING) { // first category encountered will overwrite "dummy" demand category // with what is specified in this section demand->Base = y; demand->Pat = p; hyd->NodeDemand[j] = MISSING; // marker - next iteration will append a new category. } else { // add new demand to junction cur_demand = net->Node[j].D; while (cur_demand->next != NULL) { cur_demand = cur_demand->next; } demand = (struct Sdemand *)malloc(sizeof(struct Sdemand)); if (demand == NULL) return (101); demand->Base = y; demand->Pat = p; demand->next = NULL; cur_demand->next = demand; } return (0); } /* end of demanddata */ int controldata(EN_Project *pr) /* **-------------------------------------------------------------- ** Input: none ** Output: returns error code ** Purpose: processes simple controls ** Formats: ** [CONTROLS] ** LINK linkID setting IF NODE nodeID {BELOW/ABOVE} level ** LINK linkID setting AT TIME value (units) ** LINK linkID setting AT CLOCKTIME value (units) ** (0) (1) (2) (3) (4) (5) (6) (7) **-------------------------------------------------------------- */ { EN_Network *net = &pr->network; parser_data_t *par = &pr->parser; int i = 0, /* Node index */ k, /* Link index */ n; /* # data items */ StatType status = ACTIVE; /* Link status */ ControlType c_type; /* control type */ EN_LinkType l_type; /* Link Type */ double setting = MISSING, /* Link setting */ time = 0.0, /* Simulation time */ level = 0.0; /* Pressure or tank level */ Scontrol *control; /* Check for sufficient number of input tokens */ n = par->Ntokens; if (n < 6) return (201); /* Check that controlled link exists */ k = findlink(net,par->Tok[1]); if (k == 0) return (204); l_type = net->Link[k].Type; if (l_type == EN_CVPIPE) { return (207); /* Cannot control check valve. */ } /*** Updated 9/7/00 ***/ /* Parse control setting into a status level or numerical setting. */ if (match(par->Tok[2], w_OPEN)) { status = OPEN; if (l_type == EN_PUMP) setting = 1.0; if (l_type == EN_GPV) setting = net->Link[k].Kc; } else if (match(par->Tok[2], w_CLOSED)) { status = CLOSED; if (l_type == EN_PUMP) setting = 0.0; if (l_type == EN_GPV) setting = net->Link[k].Kc; } else if (l_type == EN_GPV) { return (206); } else if (!getfloat(par->Tok[2], &setting)) { return (202); } /*** Updated 3/1/01 ***/ /* Set status for pump in case speed setting was supplied */ /* or for pipe if numerical setting was supplied */ if (l_type == EN_PUMP || l_type == EN_PIPE) { if (setting != MISSING) { if (setting < 0.0) return (202); else if (setting == 0.0) status = CLOSED; else status = OPEN; } } /* Determine type of control */ if (match(par->Tok[4], w_TIME)) { c_type = TIMER; } else if (match(par->Tok[4], w_CLOCKTIME)) { c_type = TIMEOFDAY; } else { if (n < 8) return (201); if ((i = findnode(net, par->Tok[5])) == 0) return (203); if (match(par->Tok[6], w_BELOW)) c_type = LOWLEVEL; else if (match(par->Tok[6], w_ABOVE)) c_type = HILEVEL; else return (201); } /* Parse control level or time */ switch (c_type) { case TIMER: case TIMEOFDAY: if (n == 6) time = hour(par->Tok[5], ""); if (n == 7) time = hour(par->Tok[5], par->Tok[6]); if (time < 0.0) return (201); break; case LOWLEVEL: case HILEVEL: if (!getfloat(par->Tok[7], &level)) return (202); break; } /* Fill in fields of control data structure */ net->Ncontrols++; if (net->Ncontrols > par->MaxControls) return (200); control = &net->Control[net->Ncontrols]; control->Link = k; control->Node = i; control->Type = c_type; control->Status = status; control->Setting = setting; control->Time = (long)(3600.0 * time); if (c_type == TIMEOFDAY) control->Time %= SECperDAY; control->Grade = level; return (0); } /* end of controldata */ int sourcedata(EN_Project *pr) /* **-------------------------------------------------------------- ** Input: none ** Output: returns error code ** Purpose: processes water quality source data ** Formats: ** [SOURCE] ** node sourcetype quality (pattern start stop) ** ** NOTE: units of mass-based source are mass/min **-------------------------------------------------------------- */ { EN_Network *net = &pr->network; parser_data_t *par = &pr->parser; int i, /* Token with quality value */ j, /* Node index */ n, /* # data items */ p = 0; /* Time pattern */ char type = CONCEN; /* Source type */ double c0 = 0; /* Init. quality */ STmplist *pat; Psource source; n = par->Ntokens; if (n < 2) return (201); if ((j = findnode(net, par->Tok[0])) == 0) return (203); /* NOTE: Under old format, SourceType not supplied so let */ /* i = index of token that contains quality value. */ i = 2; if (match(par->Tok[1], w_CONCEN)) type = CONCEN; else if (match(par->Tok[1], w_MASS)) type = MASS; else if (match(par->Tok[1], w_SETPOINT)) type = SETPOINT; else if (match(par->Tok[1], w_FLOWPACED)) type = FLOWPACED; else i = 1; if (!getfloat(par->Tok[i], &c0)) return (202); /* Illegal WQ value */ if (n > i + 1 && strlen(par->Tok[i + 1]) > 0 && strcmp(par->Tok[i + 1], "*") != 0) { pat = findID(par->Tok[i + 1], par->Patlist); if (pat == NULL) return (205); /* Illegal pattern. */ p = pat->i; } source = (struct Ssource *)malloc(sizeof(struct Ssource)); if (source == NULL) return (101); source->C0 = c0; source->Pat = p; source->Type = type; net->Node[j].S = source; return (0); } /* end of sourcedata */ int emitterdata(EN_Project *pr) /* **-------------------------------------------------------------- ** Input: none ** Output: returns error code ** Purpose: processes junction emitter data ** Format: ** [EMITTER] ** node K **-------------------------------------------------------------- */ { EN_Network *net = &pr->network; parser_data_t *par = &pr->parser; int j, /* Node index */ n; /* # data items */ double k; /* Flow coeff, */ n = par->Ntokens; if (n < 2) return (201); if ((j = findnode(net,par->Tok[0])) == 0) return (203); if (j > net->Njuncs) return (209); /* Not a junction.*/ if (!getfloat(par->Tok[1], &k)) return (202); if (k < 0.0) return (202); net->Node[j].Ke = k; return (0); } int qualdata(EN_Project *pr) /* **-------------------------------------------------------------- ** Input: none ** Output: returns error code ** Purpose: processes initial water quality data ** Formats: ** [QUALITY] ** node initqual ** node1 node2 initqual **-------------------------------------------------------------- */ { EN_Network *net = &pr->network; parser_data_t *par = &pr->parser; Snode *Node = net->Node; int j, n; long i, i0, i1; double c0; if (net->Nnodes == 0) return (208); /* No nodes defined yet */ n = par->Ntokens; if (n < 2) return (0); if (n == 2) /* Single node entered */ { if ((j = findnode(net,par->Tok[0])) == 0) return (0); if (!getfloat(par->Tok[1], &c0)) return (209); Node[j].C0 = c0; } else /* Node range entered */ { if (!getfloat(par->Tok[2], &c0)) return (209); /* If numerical range supplied, then use numerical comparison */ if ((i0 = atol(par->Tok[0])) > 0 && (i1 = atol(par->Tok[1])) > 0) { for (j = 1; j <= net->Nnodes; j++) { i = atol(Node[j].ID); if (i >= i0 && i <= i1) Node[j].C0 = c0; } } else { for (j = 1; j <= net->Nnodes; j++) if ((strcmp(par->Tok[0], Node[j].ID) <= 0) && (strcmp(par->Tok[1], Node[j].ID) >= 0)) Node[j].C0 = c0; } } return (0); } /* end of qualdata */ int reactdata(EN_Project *pr) /* **-------------------------------------------------------------- ** Input: none ** Output: returns error code ** Purpose: processes reaction coeff. data ** Formats: ** [REACTIONS] ** ORDER {BULK/WALL/TANK} value ** GLOBAL BULK coeff ** GLOBAL WALL coeff ** BULK link1 (link2) coeff ** WALL link1 (link2) coeff ** TANK node1 (node2) coeff ** LIMITING POTENTIAL value ** ROUGHNESS CORRELATION value **-------------------------------------------------------------- */ { EN_Network *net = &pr->network; quality_t *qu = &pr->quality; parser_data_t *par = &pr->parser; int item, j, n; long i, i1, i2; double y; /* Skip line if insufficient data */ n = par->Ntokens; if (n < 3) return (0); /* Process input depending on keyword */ if (match(par->Tok[0], w_ORDER)) /* Reaction order */ { if (!getfloat(par->Tok[n - 1], &y)) return (213); if (match(par->Tok[1], w_BULK)) qu->BulkOrder = y; else if (match(par->Tok[1], w_TANK)) qu->TankOrder = y; else if (match(par->Tok[1], w_WALL)) { if (y == 0.0) qu->WallOrder = 0.0; else if (y == 1.0) qu->WallOrder = 1.0; else return (213); } else return (213); return (0); } if (match(par->Tok[0], w_ROUGHNESS)) /* Roughness factor */ { if (!getfloat(par->Tok[n - 1], &y)) return (213); qu->Rfactor = y; return (0); } if (match(par->Tok[0], w_LIMITING)) /* Limiting potential */ { if (!getfloat(par->Tok[n - 1], &y)) return (213); /*if (y < 0.0) return(213);*/ qu->Climit = y; return (0); } if (match(par->Tok[0], w_GLOBAL)) /* Global rates */ { if (!getfloat(par->Tok[n - 1], &y)) return (213); if (match(par->Tok[1], w_BULK)) qu->Kbulk = y; else if (match(par->Tok[1], w_WALL)) qu->Kwall = y; else return (201); return (0); } if (match(par->Tok[0], w_BULK)) item = 1; /* Individual rates */ else if (match(par->Tok[0], w_WALL)) item = 2; else if (match(par->Tok[0], w_TANK)) item = 3; else return (201); strcpy(par->Tok[0], par->Tok[1]); /* Save id in par->Tok[0] */ if (item == 3) /* Tank rates */ { if (!getfloat(par->Tok[n - 1], &y)) return (209); /* Rate coeff. */ if (n == 3) { if ((j = findnode(net,par->Tok[1])) <= net->Njuncs) return (0); net->Tank[j - net->Njuncs].Kb = y; } else { /* If numerical range supplied, then use numerical comparison */ if ((i1 = atol(par->Tok[1])) > 0 && (i2 = atol(par->Tok[2])) > 0) { for (j = net->Njuncs + 1; j <= net->Nnodes; j++) { i = atol(net->Node[j].ID); if (i >= i1 && i <= i2) net->Tank[j - net->Njuncs].Kb = y; } } else for (j = net->Njuncs + 1; j <= net->Nnodes; j++) if ((strcmp(par->Tok[1], net->Node[j].ID) <= 0) && (strcmp(par->Tok[2], net->Node[j].ID) >= 0)) net->Tank[j - net->Njuncs].Kb = y; } } else /* Link rates */ { if (!getfloat(par->Tok[n - 1], &y)) return (211); /* Rate coeff. */ if (net->Nlinks == 0) return (0); if (n == 3) /* Single link */ { if ((j = findlink(net, par->Tok[1])) == 0) return (0); if (item == 1) net->Link[j].Kb = y; else net->Link[j].Kw = y; } else /* Range of links */ { /* If numerical range supplied, then use numerical comparison */ if ((i1 = atol(par->Tok[1])) > 0 && (i2 = atol(par->Tok[2])) > 0) { for (j = 1; j <= net->Nlinks; j++) { i = atol(net->Link[j].ID); if (i >= i1 && i <= i2) { if (item == 1) net->Link[j].Kb = y; else net->Link[j].Kw = y; } } } else for (j = 1; j <= net->Nlinks; j++) if ((strcmp(par->Tok[1], net->Link[j].ID) <= 0) && (strcmp(par->Tok[2], net->Link[j].ID) >= 0)) { if (item == 1) net->Link[j].Kb = y; else net->Link[j].Kw = y; } } } return (0); } /* end of reactdata */ int mixingdata(EN_Project *pr) /* **------------------------------------------------------------- ** Input: none ** Output: returns error code ** Purpose: processes tank mixing data ** Format: ** [MIXING] ** TankID MixModel FractVolume **------------------------------------------------------------- */ { EN_Network *net = &pr->network; parser_data_t *par = &pr->parser; int i, j, n; double v; if (net->Nnodes == 0) return (208); /* No nodes defined yet */ n = par->Ntokens; if (n < 2) return (0); if ((j = findnode(net, par->Tok[0])) <= net->Njuncs) return (0); if ((i = findmatch(par->Tok[1], MixTxt)) < 0) return (201); v = 1.0; if ((i == MIX2) && (n == 3) && (!getfloat(par->Tok[2], &v)) /* Get frac. vol. for 2COMP model */ ) return (209); if (v == 0.0) v = 1.0; /* v can't be zero */ n = j - net->Njuncs; if (net->Tank[n].A == 0.0) return (0); /* Tank is a reservoir */ net->Tank[n].MixModel = (char)i; net->Tank[n].V1max = v; return (0); } int statusdata(EN_Project *pr) /* **-------------------------------------------------------------- ** Input: none ** Output: returns error code ** Purpose: processes link initial status data ** Formats: ** [STATUS] ** link value ** link1 (link2) value **-------------------------------------------------------------- */ { EN_Network *net = &pr->network; parser_data_t *par = &pr->parser; int j, n; long i, i0, i1; double y = 0.0; char status = ACTIVE; if (net->Nlinks == 0) return (210); n = par->Ntokens - 1; if (n < 1) return (201); /* Check for legal status setting */ if (match(par->Tok[n], w_OPEN)) status = OPEN; else if (match(par->Tok[n], w_CLOSED)) status = CLOSED; else if (!getfloat(par->Tok[n], &y)) return (211); if (y < 0.0) return (211); /* Single link ID supplied */ if (n == 1) { if ((j = findlink(net, par->Tok[0])) == 0) return (0); /* Cannot change status of a Check Valve */ if (net->Link[j].Type == EN_CVPIPE) return (211); /*** Updated 9/7/00 ***/ /* Cannot change setting for a GPV */ if (net->Link[j].Type == EN_GPV && status == ACTIVE) return (211); changestatus(net, j, status, y); } /* Range of ID's supplied */ else { /* Numerical range supplied */ if ((i0 = atol(par->Tok[0])) > 0 && (i1 = atol(par->Tok[1])) > 0) { for (j = 1; j <= net->Nlinks; j++) { i = atol(net->Link[j].ID); if (i >= i0 && i <= i1) changestatus(net, j, status, y); } } else for (j = 1; j <= net->Nlinks; j++) if ((strcmp(par->Tok[0], net->Link[j].ID) <= 0) && (strcmp(par->Tok[1], net->Link[j].ID) >= 0)) changestatus(net, j, status, y); } return (0); } /* end of statusdata */ int energydata(EN_Project *pr) /* **-------------------------------------------------------------- ** Input: none ** Output: returns error code ** Purpose: processes pump energy data ** Formats: ** [ENERGY] ** GLOBAL {PRICE/PATTERN/EFFIC} value ** PUMP id {PRICE/PATTERN/EFFIC} value ** DEMAND CHARGE value **-------------------------------------------------------------- */ { EN_Network *net = &pr->network; hydraulics_t *hyd = &pr->hydraulics; parser_data_t *par = &pr->parser; Slink *Link = net->Link; Spump *Pump = net->Pump; int j, k, n; double y; STmplist *t; /* Check for sufficient data */ n = par->Ntokens; if (n < 3) return (201); /* Check first keyword */ if (match(par->Tok[0], w_DMNDCHARGE)) /* Demand charge */ { if (!getfloat(par->Tok[2], &y)) return (213); hyd->Dcost = y; return (0); } if (match(par->Tok[0], w_GLOBAL)) /* Global parameter */ { j = 0; } else if (match(par->Tok[0], w_PUMP)) /* Pump-specific parameter */ { if (n < 4) return (201); k = findlink(net,par->Tok[1]); /* Check that pump exists */ if (k == 0) return (216); if (Link[k].Type != EN_PUMP) return (216); j = findpump(net, k); } else return (201); /* Find type of energy parameter */ if (match(par->Tok[n - 2], w_PRICE)) /* Energy price */ { if (!getfloat(par->Tok[n - 1], &y)) { if (j == 0) return (213); else return (217); } if (j == 0) hyd->Ecost = y; else Pump[j].Ecost = y; return (0); } else if (match(par->Tok[n - 2], w_PATTERN)) /* Price pattern */ { t = findID(par->Tok[n - 1], par->Patlist); /* Check if pattern exists */ if (t == NULL) { if (j == 0) return (213); else return (217); } if (j == 0) hyd->Epat = t->i; else Pump[j].Epat = t->i; return (0); } else if (match(par->Tok[n - 2], w_EFFIC)) /* Pump efficiency */ { if (j == 0) { if (!getfloat(par->Tok[n - 1], &y)) return (213); if (y <= 0.0) return (213); hyd->Epump = y; } else { t = findID(par->Tok[n - 1], par->Curvelist); /* Check if curve exists */ if (t == NULL) return (217); Pump[j].Ecurve = t->i; net->Curve[t->i].Type = E_CURVE; } return (0); } return (201); } int reportdata(EN_Project *pr) /* **-------------------------------------------------------------- ** Input: none ** Output: returns error code ** Purpose: processes report options data ** Formats: ** PAGE linesperpage ** STATUS {NONE/YES/FULL} ** SUMMARY {YES/NO} ** MESSAGES {YES/NO} ** ENERGY {NO/YES} ** NODES {NONE/ALL} ** NODES node1 node2 ... ** LINKS {NONE/ALL} ** LINKS link1 link2 ... ** FILE filename ** variable {YES/NO} ** variable {BELOW/ABOVE/PRECISION} value **-------------------------------------------------------------- */ { EN_Network *net = &pr->network; report_options_t *rep = &pr->report; parser_data_t *par = &pr->parser; int i, j, n; double y; n = par->Ntokens - 1; if (n < 1) return (201); /* Value for page size */ if (match(par->Tok[0], w_PAGE)) { if (!getfloat(par->Tok[n], &y)) return (213); if (y < 0.0 || y > 255.0) return (213); rep->PageSize = (int)y; return (0); } /* Request that status reports be written */ if (match(par->Tok[0], w_STATUS)) { if (match(par->Tok[n], w_NO)) rep->Statflag = FALSE; if (match(par->Tok[n], w_YES)) rep->Statflag = TRUE; if (match(par->Tok[n], w_FULL)) rep->Statflag = FULL; return (0); } /* Request summary report */ if (match(par->Tok[0], w_SUMMARY)) { if (match(par->Tok[n], w_NO)) rep->Summaryflag = FALSE; if (match(par->Tok[n], w_YES)) rep->Summaryflag = TRUE; return (0); } /* Request error/warning message reporting */ if (match(par->Tok[0], w_MESSAGES)) { if (match(par->Tok[n], w_NO)) rep->Messageflag = FALSE; if (match(par->Tok[n], w_YES)) rep->Messageflag = TRUE; return (0); } /* Request an energy usage report */ if (match(par->Tok[0], w_ENERGY)) { if (match(par->Tok[n], w_NO)) rep->Energyflag = FALSE; if (match(par->Tok[n], w_YES)) rep->Energyflag = TRUE; return (0); } /* Particular reporting nodes specified */ if (match(par->Tok[0], w_NODE)) { if (match(par->Tok[n], w_NONE)) rep->Nodeflag = 0; /* No nodes */ else if (match(par->Tok[n], w_ALL)) rep->Nodeflag = 1; /* All nodes */ else { if (net->Nnodes == 0) return (208); for (i = 1; i <= n; i++) { if ((j = findnode(net,par->Tok[i])) == 0) return (208); net->Node[j].Rpt = 1; } rep->Nodeflag = 2; } return (0); } /* Particular reporting links specified */ if (match(par->Tok[0], w_LINK)) { if (match(par->Tok[n], w_NONE)) rep->Linkflag = 0; else if (match(par->Tok[n], w_ALL)) rep->Linkflag = 1; else { if (net->Nlinks == 0) return (210); for (i = 1; i <= n; i++) { if ((j = findlink(net,par->Tok[i])) == 0) return (210); net->Link[j].Rpt = 1; } rep->Linkflag = 2; } return (0); } /* Check if input is a reporting criterion. */ /*** Special case needed to distinguish "HEAD" from "HEADLOSS" ***/ //(2.00.11 //- LR) if (strcomp(par->Tok[0], t_HEADLOSS)) i = HEADLOSS; else i = findmatch(par->Tok[0], Fldname); if (i >= 0) /*****************************************************************/ //(2.00.11 //- LR) { if (i > FRICTION) return (201); if (par->Ntokens == 1 || match(par->Tok[1], w_YES)) { rep->Field[i].Enabled = TRUE; return (0); } if (match(par->Tok[1], w_NO)) { rep->Field[i].Enabled = FALSE; return (0); } if (par->Ntokens < 3) return (201); if (match(par->Tok[1], w_BELOW)) j = LOW; /* Get relation operator */ else if (match(par->Tok[1], w_ABOVE)) j = HI; /* or precision keyword */ else if (match(par->Tok[1], w_PRECISION)) j = PREC; else return (201); if (!getfloat(par->Tok[2], &y)) return (201); if (j == PREC) { rep->Field[i].Enabled = TRUE; rep->Field[i].Precision = ROUND(y); } else rep->Field[i].RptLim[j] = y; /* Report limit value */ return (0); } /* Name of external report file */ if (match(par->Tok[0], w_FILE)) { strncpy(rep->Rpt2Fname, par->Tok[1], MAXFNAME); return (0); } /* If get to here then return error condition */ return (201); } /* end of reportdata */ int timedata(EN_Project *pr) /* **-------------------------------------------------------------- ** Input: none ** Output: returns error code ** Purpose: processes time options data ** Formats: ** STATISTIC {NONE/AVERAGE/MIN/MAX/RANGE} ** DURATION value (units) ** HYDRAULIC TIMESTEP value (units) ** QUALITY TIMESTEP value (units) ** MINIMUM TRAVELTIME value (units) ** RULE TIMESTEP value (units) ** PATTERN TIMESTEP value (units) ** PATTERN START value (units) ** REPORT TIMESTEP value (units) ** REPORT START value (units) ** START CLOCKTIME value (AM PM) **------------------------------------------------------------- */ { report_options_t *rep = &pr->report; quality_t *qu = &pr->quality; parser_data_t *par = &pr->parser; time_options_t *time = &pr->time_options; int n; long t; double y; n = par->Ntokens - 1; if (n < 1) return (201); /* Check if setting time statistic flag */ if (match(par->Tok[0], w_STATISTIC)) { if (match(par->Tok[n], w_NONE)) rep->Tstatflag = SERIES; else if (match(par->Tok[n], w_NO)) rep->Tstatflag = SERIES; else if (match(par->Tok[n], w_AVG)) rep->Tstatflag = AVG; else if (match(par->Tok[n], w_MIN)) rep->Tstatflag = MIN; else if (match(par->Tok[n], w_MAX)) rep->Tstatflag = MAX; else if (match(par->Tok[n], w_RANGE)) rep->Tstatflag = RANGE; else return (201); return (0); } /* Convert text time value to numerical value in seconds */ /* Examples: ** 5 = 5 * 3600 sec ** 5 MINUTES = 5 * 60 sec ** 13:50 = 13*3600 + 50*60 sec ** 1:50 pm = (12+1)*3600 + 50*60 sec */ if (!getfloat(par->Tok[n], &y)) { if ((y = hour(par->Tok[n], "")) < 0.0) { if ((y = hour(par->Tok[n - 1], par->Tok[n])) < 0.0) return (213); } } t = (long)(3600.0 * y + 0.5); /* Process the value assigned to the matched parameter */ if (match(par->Tok[0], w_DURATION)) time->Dur = t; /* Simulation duration */ else if (match(par->Tok[0], w_HYDRAULIC)) time->Hstep = t; /* Hydraulic time step */ else if (match(par->Tok[0], w_QUALITY)) qu->Qstep = t; /* Quality time step */ else if (match(par->Tok[0], w_RULE)) time->Rulestep = t; /* Rule time step */ else if (match(par->Tok[0], w_MINIMUM)) return (0); /* Not used anymore */ else if (match(par->Tok[0], w_PATTERN)) { if (match(par->Tok[1], w_TIME)) time->Pstep = t; /* Pattern time step */ else if (match(par->Tok[1], w_START)) time->Pstart = t; /* Pattern start time */ else return (201); } else if (match(par->Tok[0], w_REPORT)) { if (match(par->Tok[1], w_TIME)) time->Rstep = t; /* Reporting time step */ else if (match(par->Tok[1], w_START)) time->Rstart = t; /* Reporting start time */ else return (201); } /* Simulation start time*/ else if (match(par->Tok[0], w_START)) time->Tstart = t % SECperDAY; else return (201); return (0); } /* end of timedata */ int optiondata(EN_Project *pr) /* **-------------------------------------------------------------- ** Input: none ** Output: returns error code ** Purpose: processes [OPTIONS] data **-------------------------------------------------------------- */ { parser_data_t *par = &pr->parser; int i, n; n = par->Ntokens - 1; i = optionchoice(pr,n); /* Option is a named choice */ if (i >= 0) return (i); return (optionvalue(pr,n)); /* Option is a numerical value */ } /* end of optiondata */ int optionchoice(EN_Project *pr, int n) /* **-------------------------------------------------------------- ** Input: n = index of last input token saved in par->Tok[] ** Output: returns error code or 0 if option belongs to ** those listed below, or -1 otherwise ** Purpose: processes fixed choice [OPTIONS] data ** Formats: ** UNITS CFS/GPM/MGD/IMGD/AFD/LPS/LPM/MLD/CMH/CMD/SI ** PRESSURE PSI/KPA/M ** HEADLOSS H-W/D-W/C-M ** HYDRAULICS USE/SAVE filename ** QUALITY NONE/AGE/TRACE/CHEMICAL (TraceNode) ** MAP filename ** VERIFY filename ** UNBALANCED STOP/CONTINUE {Niter} ** PATTERN id **-------------------------------------------------------------- */ { EN_Network *net = &pr->network; hydraulics_t *hyd = &pr->hydraulics; quality_t *qu = &pr->quality; parser_data_t *par = &pr->parser; out_file_t *out = &pr->out_files; /* Check if 1st token matches a parameter name and */ /* process the input for the matched parameter */ if (n < 0) return (201); if (match(par->Tok[0], w_UNITS)) { if (n < 1) return (0); else if (match(par->Tok[1], w_CFS)) par->Flowflag = CFS; else if (match(par->Tok[1], w_GPM)) par->Flowflag = GPM; else if (match(par->Tok[1], w_AFD)) par->Flowflag = AFD; else if (match(par->Tok[1], w_MGD)) par->Flowflag = MGD; else if (match(par->Tok[1], w_IMGD)) par->Flowflag = IMGD; else if (match(par->Tok[1], w_LPS)) par->Flowflag = LPS; else if (match(par->Tok[1], w_LPM)) par->Flowflag = LPM; else if (match(par->Tok[1], w_CMH)) par->Flowflag = CMH; else if (match(par->Tok[1], w_CMD)) par->Flowflag = CMD; else if (match(par->Tok[1], w_MLD)) par->Flowflag = MLD; else if (match(par->Tok[1], w_SI)) par->Flowflag = LPS; else return (201); } else if (match(par->Tok[0], w_PRESSURE)) { if (n < 1) return (0); else if (match(par->Tok[1], w_PSI)) par->Pressflag = PSI; else if (match(par->Tok[1], w_KPA)) par->Pressflag = KPA; else if (match(par->Tok[1], w_METERS)) par->Pressflag = METERS; else return (201); } else if (match(par->Tok[0], w_HEADLOSS)) { if (n < 1) return (0); else if (match(par->Tok[1], w_HW)) hyd->Formflag = HW; else if (match(par->Tok[1], w_DW)) hyd->Formflag = DW; else if (match(par->Tok[1], w_CM)) hyd->Formflag = CM; else return (201); } else if (match(par->Tok[0], w_HYDRAULIC)) { if (n < 2) return (0); else if (match(par->Tok[1], w_USE)) out->Hydflag = USE; else if (match(par->Tok[1], w_SAVE)) out->Hydflag = SAVE; else return (201); strncpy(out->HydFname, par->Tok[2], MAXFNAME); } else if (match(par->Tok[0], w_QUALITY)) { if (n < 1) return (0); else if (match(par->Tok[1], w_NONE)) qu->Qualflag = NONE; else if (match(par->Tok[1], w_CHEM)) qu->Qualflag = CHEM; else if (match(par->Tok[1], w_AGE)) qu->Qualflag = AGE; else if (match(par->Tok[1], w_TRACE)) qu->Qualflag = TRACE; else { qu->Qualflag = CHEM; strncpy(qu->ChemName, par->Tok[1], MAXID); if (n >= 2) strncpy(qu->ChemUnits, par->Tok[2], MAXID); } if (qu->Qualflag == TRACE) /* Source tracing option */ { /* Copy Trace Node ID to par->Tok[0] for error reporting */ strcpy(par->Tok[0], ""); if (n < 2) return (212); strcpy(par->Tok[0], par->Tok[2]); qu->TraceNode = findnode(net,par->Tok[2]); if (qu->TraceNode == 0) return (212); strncpy(qu->ChemName, u_PERCENT, MAXID); strncpy(qu->ChemUnits, par->Tok[2], MAXID); } if (qu->Qualflag == AGE) { strncpy(qu->ChemName, w_AGE, MAXID); strncpy(qu->ChemUnits, u_HOURS, MAXID); } } else if (match(par->Tok[0], w_MAP)) { if (n < 1) return (0); strncpy(pr->MapFname, par->Tok[1], MAXFNAME); /* Map file name */ } else if (match(par->Tok[0], w_VERIFY)) { /* Backward compatibility for verification file */ } else if (match(par->Tok[0], w_UNBALANCED)) /* Unbalanced option */ { if (n < 1) return (0); if (match(par->Tok[1], w_STOP)) hyd->ExtraIter = -1; else if (match(par->Tok[1], w_CONTINUE)) { if (n >= 2) hyd->ExtraIter = atoi(par->Tok[2]); else hyd->ExtraIter = 0; } else return (201); } else if (match(par->Tok[0], w_PATTERN)) /* Pattern option */ { if (n < 1) return (0); strncpy(par->DefPatID, par->Tok[1], MAXID); } else return (-1); return (0); } /* end of optionchoice */ int optionvalue(EN_Project *pr, int n) /* **------------------------------------------------------------- ** Input: *line = line read from input file ** Output: returns error code ** Purpose: processes numerical value [OPTIONS] data ** Formats: ** DEMAND MULTIPLIER value ** EMITTER EXPONENT value ** VISCOSITY value ** DIFFUSIVITY value ** SPECIFIC GRAVITY value ** TRIALS value ** ACCURACY value ** HEADLIMIT value ** FLOWLIMIT value ** TOLERANCE value ** SEGMENTS value (not used) ** ------ Undocumented Options ----- ** HTOL value ** QTOL value ** RQTOL value ** CHECKFREQ value ** MAXCHECK value ** DAMPLIMIT value **-------------------------------------------------------------- */ { hydraulics_t *hyd = &pr->hydraulics; quality_t *qu = &pr->quality; parser_data_t *par = &pr->parser; char* tok0 = par->Tok[0]; int nvalue = 1; /* Index of token with numerical value */ double y; /* Check for obsolete SEGMENTS keyword */ //if (match(par->Tok[0], w_SEGMENTS)) if (match(tok0, w_SEGMENTS)) return (0); /* Check for missing value (which is permissible) */ if (match(tok0, w_SPECGRAV) || match(tok0, w_EMITTER) || match(tok0, w_DEMAND)) nvalue = 2; if (n < nvalue) return (0); /* Check for valid numerical input */ if (!getfloat(par->Tok[nvalue], &y)) return (213); /* Check for WQ tolerance option (which can be 0) */ if (match(tok0, w_TOLERANCE)) { if (y < 0.0) return (213); qu->Ctol = y; /* Quality tolerance*/ return (0); } /* Check for Diffusivity option */ if (match(tok0, w_DIFFUSIVITY)) { if (y < 0.0) return (213); qu->Diffus = y; return (0); } /* Check for Damping Limit option */ if (match(tok0, w_DAMPLIMIT)) { hyd->DampLimit = y; return (0); } /* Check for flow change limit*/ else if (match(tok0, w_FLOWCHANGE)) { if (y < 0.0) return 213; hyd->FlowChangeLimit = y; return 0; } /* Check for head error limit*/ else if (match(tok0, w_HEADERROR)) { if (y < 0.0) return 213; hyd->HeadErrorLimit = y; return 0; } /* All other options must be > 0 */ if (y <= 0.0) return (213); /* Assign value to specified option */ if (match(tok0, w_VISCOSITY)) hyd->Viscos = y; /* Viscosity */ else if (match(tok0, w_SPECGRAV)) hyd->SpGrav = y; /* Spec. gravity */ else if (match(tok0, w_TRIALS)) hyd->MaxIter = (int)y; /* Max. trials */ else if (match(tok0, w_ACCURACY)) /* Accuracy */ { y = MAX(y, 1.e-5); y = MIN(y, 1.e-1); hyd->Hacc = y; } else if (match(tok0, w_HTOL)) hyd->Htol = y; else if (match(tok0, w_QTOL)) hyd->Qtol = y; else if (match(tok0, w_RQTOL)) { if (y >= 1.0) return (213); hyd->RQtol = y; } else if (match(tok0, w_CHECKFREQ)) hyd->CheckFreq = (int)y; else if (match(tok0, w_MAXCHECK)) hyd->MaxCheck = (int)y; else if (match(tok0, w_EMITTER)) hyd->Qexp = 1.0 / y; else if (match(tok0, w_DEMAND)) hyd->Dmult = y; else return (201); return (0); } /* end of optionvalue */ int getpumpcurve(EN_Project *pr, int n) /* **-------------------------------------------------------- ** Input: n = number of parameters for pump curve ** Output: returns error code ** Purpose: processes pump curve data for Version 1.1- ** style input data ** Notes: ** 1. Called by pumpdata() in INPUT3.C ** 2. Current link index & pump index of pump being ** processed is found in global variables Nlinks ** and Npumps, respectively ** 3. Curve data read from input line is found in ** global variables X[0],...X[n-1] **--------------------------------------------------------- */ { EN_Network *net = &pr->network; parser_data_t *par = &pr->parser; double a, b, c, h0, h1, h2, q1, q2; Spump *pump = &net->Pump[net->Npumps]; if (n == 1) /* Const. HP curve */ { if (par->X[0] <= 0.0) return (202); pump->Ptype = CONST_HP; net->Link[net->Nlinks].Km = par->X[0]; } else { if (n == 2) /* Generic power curve */ { q1 = par->X[1]; h1 = par->X[0]; h0 = 1.33334 * h1; q2 = 2.0 * q1; h2 = 0.0; } else if (n >= 5) /* 3-pt. power curve */ { h0 = par->X[0]; h1 = par->X[1]; q1 = par->X[2]; h2 = par->X[3]; q2 = par->X[4]; } else return (202); pump->Ptype = POWER_FUNC; if (!powercurve(h0, h1, h2, q1, q2, &a, &b, &c)) return (206); pump->H0 = -a; pump->R = -b; pump->N = c; pump->Q0 = q1; pump->Qmax = pow((-a / b), (1.0 / c)); pump->Hmax = h0; } return (0); } int powercurve(double h0, double h1, double h2, double q1, double q2, double *a, double *b, double *c) /* **--------------------------------------------------------- ** Input: h0 = shutoff head ** h1 = design head ** h2 = head at max. flow ** q1 = design flow ** q2 = max. flow ** Output: *a, *b, *c = pump curve coeffs. (H = a-bQ^c), ** Returns 1 if sucessful, 0 otherwise. ** Purpose: computes coeffs. for pump curve **---------------------------------------------------------- */ { double h4, h5; if (h0 < TINY || h0 - h1 < TINY || h1 - h2 < TINY || q1 < TINY || q2 - q1 < TINY) return (0); *a = h0; h4 = h0 - h1; h5 = h0 - h2; *c = log(h5 / h4) / log(q2 / q1); if (*c <= 0.0 || *c > 20.0) return (0); *b = -h4 / pow(q1, *c); /*** Updated 6/24/02 ***/ if (*b >= 0.0) return (0); return (1); } int valvecheck(EN_Project *pr, int type, int j1, int j2) /* **-------------------------------------------------------------- ** Input: type = valve type ** j1 = index of upstream node ** j2 = index of downstream node ** Output: returns 1 for legal connection, 0 otherwise ** Purpose: checks for legal connections between PRVs & PSVs **-------------------------------------------------------------- */ { EN_Network *net = &pr->network; int k, vj1, vj2; EN_LinkType vtype; /* Examine each existing valve */ for (k = 1; k <= net->Nvalves; k++) { Svalve *valve = &net->Valve[k]; Slink *link = &net->Link[valve->Link]; vj1 = link->N1; vj2 = link->N2; vtype = link->Type; /* Cannot have two PRVs sharing downstream nodes or in series */ if (vtype == EN_PRV && type == EN_PRV) { if (vj2 == j2 || vj2 == j1 || vj1 == j2) return (0); } /* Cannot have two PSVs sharing upstream nodes or in series */ if (vtype == EN_PSV && type == EN_PSV) { if (vj1 == j1 || vj1 == j2 || vj2 == j1) return (0); } /* Cannot have PSV connected to downstream node of PRV */ if (vtype == EN_PSV && type == EN_PRV && vj1 == j2) return (0); if (vtype == EN_PRV && type == EN_PSV && vj2 == j1) return (0); /*** Updated 3/1/01 ***/ /* Cannot have PSV connected to downstream node of FCV */ /* nor have PRV connected to upstream node of FCV */ if (vtype == EN_FCV && type == EN_PSV && vj2 == j1) return (0); if (vtype == EN_FCV && type == EN_PRV && vj1 == j2) return (0); /*** Updated 4/14/05 ***/ if (vtype == EN_PSV && type == EN_FCV && vj1 == j2) return (0); if (vtype == EN_PRV && type == EN_FCV && vj2 == j1) return (0); } return (1); } /* End of valvecheck */ void changestatus(EN_Network *net, int j, StatType status, double y) /* **-------------------------------------------------------------- ** Input: j = link index ** status = status setting (OPEN, CLOSED) ** y = numerical setting (pump speed, valve ** setting) ** Output: none ** Purpose: changes status or setting of a link ** ** NOTE: If status = ACTIVE, then a numerical setting (y) was ** supplied. If status = OPEN/CLOSED, then numerical ** setting is 0. **-------------------------------------------------------------- */ { Slink *link = &net->Link[j]; if (link->Type == EN_PIPE || link->Type == EN_GPV) { if (status != ACTIVE) link->Stat = status; } else if (link->Type == EN_PUMP) { if (status == ACTIVE) { link->Kc = y; status = OPEN; if (y == 0.0) status = CLOSED; } else if (status == OPEN) { link->Kc = 1.0; } link->Stat = status; } else if (link->Type >= EN_PRV) { link->Kc = y; link->Stat = status; if (status != ACTIVE) { link->Kc = MISSING; } } } /* end of changestatus */ /********************** END OF INPUT3.C ************************/