Files
EPANET/src/input3.c
Lew Rossman b3ab8ea2c7 Addresses issue #161
Adds new options HEADERROR and FLOWCHANGE to provide more rigorous criteria for hydraulic convergence. Also breaks HYDRAUL.C into 3 separate files to improve code readability.
2018-06-16 11:02:18 -04:00

2227 lines
60 KiB
C

/*
**********************************************************************
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 <stdio.h>
#include <stdlib.h>
#include <string.h>
#ifndef __APPLE__
#include <malloc.h>
#endif
#include "epanet2.h"
#include "funcs.h"
#include "hash.h"
#include "text.h"
#include "types.h"
#include <math.h>
#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 ************************/