git-svn-id: https://epanet.svn.sourceforge.net/svnroot/epanet/BASE/trunk@421 c320cabd-cc23-0410-96d8-e60fbf53ed7f
1863 lines
63 KiB
C
Executable File
1863 lines
63 KiB
C
Executable File
/*
|
|
**********************************************************************
|
|
|
|
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 <stdlib.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <malloc.h>
|
|
#include <math.h>
|
|
#include "hash.h"
|
|
#include "text.h"
|
|
#include "types.h"
|
|
#include "funcs.h"
|
|
#define EXTERN extern
|
|
#include "vars.h"
|
|
|
|
/* Defined in enumstxt.h in EPANET.C */
|
|
extern char *MixTxt[];
|
|
extern char *Fldname[];
|
|
|
|
/* Defined in INPUT2.C */
|
|
extern char *Tok[MAXTOKS];
|
|
extern STmplist *PrevPat;
|
|
extern STmplist *PrevCurve;
|
|
extern int Ntokens;
|
|
|
|
|
|
int juncdata()
|
|
/*
|
|
**--------------------------------------------------------------
|
|
** Input: none
|
|
** Output: returns error code
|
|
** Purpose: processes junction data
|
|
** Format:
|
|
** [JUNCTIONS]
|
|
** id elev. (demand) (demand pattern)
|
|
**--------------------------------------------------------------
|
|
*/
|
|
{
|
|
int n, p = 0;
|
|
double el,y = 0.0;
|
|
Pdemand demand;
|
|
STmplist *pat;
|
|
|
|
/* Add new junction to data base */
|
|
n = Ntokens;
|
|
if (Nnodes == MaxNodes) return(200);
|
|
Njuncs++;
|
|
Nnodes++;
|
|
if (!addnodeID(Njuncs,Tok[0])) return(215);
|
|
|
|
/* Check for valid data */
|
|
if (n < 2) return(201);
|
|
if (!getfloat(Tok[1],&el)) return(202);
|
|
if (n >= 3 && !getfloat(Tok[2],&y)) return(202);
|
|
if (n >= 4)
|
|
{
|
|
pat = findID(Tok[3],Patlist);
|
|
if (pat == NULL) return(205);
|
|
p = pat->i;
|
|
}
|
|
|
|
/* Save junction data */
|
|
Node[Njuncs].El = el;
|
|
Node[Njuncs].C0 = 0.0;
|
|
Node[Njuncs].S = NULL;
|
|
Node[Njuncs].Ke = 0.0;
|
|
Node[Njuncs].Rpt = 0;
|
|
|
|
/* Create a new demand record */
|
|
/*** Updated 6/24/02 ***/
|
|
if (n >= 3)
|
|
{
|
|
demand = (struct Sdemand *) malloc(sizeof(struct Sdemand));
|
|
if (demand == NULL) return(101);
|
|
demand->Base = y;
|
|
demand->Pat = p;
|
|
demand->next = Node[Njuncs].D;
|
|
Node[Njuncs].D = demand;
|
|
D[Njuncs] = y;
|
|
}
|
|
else D[Njuncs] = MISSING;
|
|
/*** end of update ***/
|
|
return(0);
|
|
} /* end of juncdata */
|
|
|
|
|
|
int tankdata()
|
|
/*
|
|
**--------------------------------------------------------------
|
|
** 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 i, /* Node index */
|
|
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;
|
|
|
|
/* Add new tank to data base */
|
|
n = Ntokens;
|
|
if (Ntanks == MaxTanks
|
|
|| Nnodes == MaxNodes) return(200);
|
|
Ntanks++;
|
|
Nnodes++;
|
|
i = MaxJuncs + Ntanks; /* i = node index. */
|
|
if (!addnodeID(i,Tok[0])) return(215); /* Add ID to database. */
|
|
|
|
/* Check for valid data */
|
|
if (n < 2) return(201); /* Too few fields. */
|
|
if (!getfloat(Tok[1],&el)) return(202); /* Read elevation */
|
|
if (n <= 3) /* Tank is reservoir.*/
|
|
{
|
|
if (n == 3) /* Pattern supplied */
|
|
{
|
|
t = findID(Tok[2],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(Tok[2],&initlevel)) return(202);
|
|
if (!getfloat(Tok[3],&minlevel)) return(202);
|
|
if (!getfloat(Tok[4],&maxlevel)) return(202);
|
|
if (!getfloat(Tok[5],&diam)) return(202);
|
|
if (diam < 0.0) return(202);
|
|
if (n >= 7
|
|
&& !getfloat(Tok[6],&minvol)) return(202);
|
|
|
|
/* If volume curve supplied check it exists */
|
|
if (n == 8)
|
|
{
|
|
t = findID(Tok[7],Curvelist);
|
|
if (t == NULL) return(202);
|
|
vcurve = t->i;
|
|
}
|
|
}
|
|
|
|
Node[i].Rpt = 0;
|
|
Node[i].El = el; /* Elevation. */
|
|
Node[i].C0 = 0.0; /* Init. quality. */
|
|
Node[i].S = NULL; /* WQ source data */
|
|
Node[i].Ke = 0.0; /* Emitter coeff. */
|
|
Tank[Ntanks].Node = i; /* Node index. */
|
|
Tank[Ntanks].H0 = initlevel; /* Init. level. */
|
|
Tank[Ntanks].Hmin = minlevel; /* Min. level. */
|
|
Tank[Ntanks].Hmax = maxlevel; /* Max level. */
|
|
Tank[Ntanks].A = diam; /* Diameter. */
|
|
Tank[Ntanks].Pat = p; /* Fixed grade pattern. */
|
|
Tank[Ntanks].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[Ntanks].Vmin = area*minlevel;
|
|
if (minvol > 0.0) Tank[Ntanks].Vmin = minvol;
|
|
Tank[Ntanks].V0 = Tank[Ntanks].Vmin + area*(initlevel - minlevel);
|
|
Tank[Ntanks].Vmax = Tank[Ntanks].Vmin + area*(maxlevel - minlevel);
|
|
|
|
Tank[Ntanks].Vcurve = vcurve; /* Volume curve */
|
|
Tank[Ntanks].MixModel = MIX1; /* Completely mixed */
|
|
Tank[Ntanks].V1max = 1.0; /* Compart. size ratio */
|
|
return(0);
|
|
} /* end of tankdata */
|
|
|
|
|
|
int pipedata()
|
|
/*
|
|
**--------------------------------------------------------------
|
|
** 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 */
|
|
char type = PIPE, /* Link type */
|
|
status = OPEN; /* Link status */
|
|
double length, /* Link length */
|
|
diam, /* Link diameter */
|
|
rcoeff, /* Roughness coeff. */
|
|
lcoeff = 0.0; /* Minor loss coeff. */
|
|
|
|
/* Add new pipe to data base */
|
|
n = Ntokens;
|
|
if (Nlinks == MaxLinks) return(200);
|
|
Npipes++;
|
|
Nlinks++;
|
|
if (!addlinkID(Nlinks,Tok[0])) return(215);
|
|
|
|
/* Check for valid data */
|
|
if (n < 6) return(201);
|
|
if ((j1 = findnode(Tok[1])) == 0 ||
|
|
(j2 = findnode(Tok[2])) == 0
|
|
) return(203);
|
|
|
|
/*** Updated 10/25/00 ***/
|
|
if (j1 == j2) return(222);
|
|
|
|
if (!getfloat(Tok[3],&length) ||
|
|
!getfloat(Tok[4],&diam) ||
|
|
!getfloat(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(Tok[6],w_CV)) type = CV;
|
|
else if (match(Tok[6],w_CLOSED)) status = CLOSED;
|
|
else if (match(Tok[6],w_OPEN)) status = OPEN;
|
|
else if (!getfloat(Tok[6],&lcoeff)) return(202);
|
|
}
|
|
|
|
/* Case where both loss coeff. and status supplied */
|
|
if (n == 8)
|
|
{
|
|
if (!getfloat(Tok[6],&lcoeff)) return(202);
|
|
if (match(Tok[7],w_CV)) type = CV;
|
|
else if (match(Tok[7],w_CLOSED)) status = CLOSED;
|
|
else if (match(Tok[7],w_OPEN)) status = OPEN;
|
|
else return(202);
|
|
}
|
|
if (lcoeff < 0.0) return(202);
|
|
|
|
/* Save pipe data */
|
|
Link[Nlinks].N1 = j1; /* Start-node index */
|
|
Link[Nlinks].N2 = j2; /* End-node index */
|
|
Link[Nlinks].Len = length; /* Length */
|
|
Link[Nlinks].Diam = diam; /* Diameter */
|
|
Link[Nlinks].Kc = rcoeff; /* Rough. coeff */
|
|
Link[Nlinks].Km = lcoeff; /* Loss coeff */
|
|
Link[Nlinks].Kb = MISSING; /* Bulk coeff */
|
|
Link[Nlinks].Kw = MISSING; /* Wall coeff */
|
|
Link[Nlinks].Type = type; /* Link type */
|
|
Link[Nlinks].Stat = status; /* Link status */
|
|
Link[Nlinks].Rpt = 0; /* Report flag */
|
|
return(0);
|
|
} /* end of pipedata */
|
|
|
|
|
|
int pumpdata()
|
|
/*
|
|
**--------------------------------------------------------------
|
|
** 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 */
|
|
|
|
/* Add new pump to data base */
|
|
n = Ntokens;
|
|
if (Nlinks == MaxLinks ||
|
|
Npumps == MaxPumps
|
|
) return(200);
|
|
Nlinks++;
|
|
Npumps++;
|
|
if (!addlinkID(Nlinks,Tok[0])) return(215);
|
|
|
|
/* Check for valid data */
|
|
if (n < 4) return(201);
|
|
if ((j1 = findnode(Tok[1])) == 0 ||
|
|
(j2 = findnode(Tok[2])) == 0
|
|
) return(203);
|
|
|
|
/*** Updated 10/25/00 ***/
|
|
if (j1 == j2) return(222);
|
|
|
|
/* Save pump data */
|
|
Link[Nlinks].N1 = j1; /* Start-node index. */
|
|
Link[Nlinks].N2 = j2; /* End-node index. */
|
|
Link[Nlinks].Diam = Npumps; /* Pump index. */
|
|
Link[Nlinks].Len = 0.0; /* Link length. */
|
|
Link[Nlinks].Kc = 1.0; /* Speed factor. */
|
|
Link[Nlinks].Km = 0.0; /* Horsepower. */
|
|
Link[Nlinks].Kb = 0.0;
|
|
Link[Nlinks].Kw = 0.0;
|
|
Link[Nlinks].Type = PUMP; /* Link type. */
|
|
Link[Nlinks].Stat = OPEN; /* Link status. */
|
|
Link[Nlinks].Rpt = 0; /* Report flag. */
|
|
Pump[Npumps].Link = Nlinks; /* Link index. */
|
|
Pump[Npumps].Ptype = NOCURVE; /* Type of pump curve */
|
|
Pump[Npumps].Hcurve = 0; /* Pump curve index */
|
|
Pump[Npumps].Ecurve = 0; /* Effic. curve index */
|
|
Pump[Npumps].Upat = 0; /* Utilization pattern*/
|
|
Pump[Npumps].Ecost = 0.0; /* Unit energy cost */
|
|
Pump[Npumps].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(Tok[3],&X[0]))
|
|
{
|
|
m = 1;
|
|
for (j=4; j<n; j++)
|
|
{
|
|
if (!getfloat(Tok[j],&X[m])) return(202);
|
|
m++;
|
|
}
|
|
return(getpumpcurve(m)); /* Get pump curve params */
|
|
}
|
|
|
|
/* Otherwise input follows Version 2 format */
|
|
/* so retrieve keyword/value pairs. */
|
|
m = 4;
|
|
while (m < n)
|
|
{
|
|
if (match(Tok[m-1],w_POWER)) /* Const. HP curve */
|
|
{
|
|
y = atof(Tok[m]);
|
|
if (y <= 0.0) return(202);
|
|
Pump[Npumps].Ptype = CONST_HP;
|
|
Link[Nlinks].Km = y;
|
|
}
|
|
else if (match(Tok[m-1],w_HEAD)) /* Custom pump curve */
|
|
{
|
|
t = findID(Tok[m],Curvelist);
|
|
if (t == NULL) return(206);
|
|
Pump[Npumps].Hcurve = t->i;
|
|
}
|
|
else if (match(Tok[m-1],w_PATTERN)) /* Speed/status pattern */
|
|
{
|
|
t = findID(Tok[m],Patlist);
|
|
if (t == NULL) return(205);
|
|
Pump[Npumps].Upat = t->i;
|
|
}
|
|
else if (match(Tok[m-1],w_SPEED)) /* Speed setting */
|
|
{
|
|
if (!getfloat(Tok[m],&y)) return(202);
|
|
if (y < 0.0) return(202);
|
|
Link[Nlinks].Kc = y;
|
|
}
|
|
else return(201);
|
|
m = m + 2; /* Skip to next keyword token */
|
|
}
|
|
return(0);
|
|
} /* end of pumpdata */
|
|
|
|
|
|
int valvedata()
|
|
/*
|
|
**--------------------------------------------------------------
|
|
** 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 */
|
|
|
|
/* Add new valve to data base */
|
|
n = Ntokens;
|
|
if (Nlinks == MaxLinks ||
|
|
Nvalves == MaxValves
|
|
) return(200);
|
|
Nvalves++;
|
|
Nlinks++;
|
|
if (!addlinkID(Nlinks,Tok[0])) return(215);
|
|
|
|
/* Check for valid data */
|
|
if (n < 6) return(201);
|
|
if ((j1 = findnode(Tok[1])) == 0 ||
|
|
(j2 = findnode(Tok[2])) == 0
|
|
) return(203);
|
|
|
|
/*** Updated 10/25/00 ***/
|
|
if (j1 == j2) return(222);
|
|
|
|
if (match(Tok[4],w_PRV)) type = PRV;
|
|
else if (match(Tok[4],w_PSV)) type = PSV;
|
|
else if (match(Tok[4],w_PBV)) type = PBV;
|
|
else if (match(Tok[4],w_FCV)) type = FCV;
|
|
else if (match(Tok[4],w_TCV)) type = TCV;
|
|
else if (match(Tok[4],w_GPV)) type = GPV;
|
|
else return(201); /* Illegal valve type.*/
|
|
if (!getfloat(Tok[3],&diam)) return(202);
|
|
if (diam <= 0.0) return(202); /* Illegal diameter.*/
|
|
if (type == GPV) /* Headloss curve for GPV */
|
|
{
|
|
t = findID(Tok[5],Curvelist);
|
|
if (t == NULL) return(206);
|
|
setting = t->i;
|
|
|
|
/*** Updated 9/7/00 ***/
|
|
status = OPEN;
|
|
|
|
}
|
|
else if (!getfloat(Tok[5],&setting)) return(202);
|
|
if (n >= 7 &&
|
|
!getfloat(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 > Njuncs || j2 > Njuncs) &&
|
|
(type == PRV || type == PSV || type == FCV)
|
|
) return(219);
|
|
if (!valvecheck(type,j1,j2)) return(220);
|
|
|
|
/* Save valve data */
|
|
Link[Nlinks].N1 = j1; /* Start-node index. */
|
|
Link[Nlinks].N2 = j2; /* End-node index. */
|
|
Link[Nlinks].Diam = diam; /* Valve diameter. */
|
|
Link[Nlinks].Len = 0.0; /* Link length. */
|
|
Link[Nlinks].Kc = setting; /* Valve setting. */
|
|
Link[Nlinks].Km = lcoeff; /* Loss coeff */
|
|
Link[Nlinks].Kb = 0.0;
|
|
Link[Nlinks].Kw = 0.0;
|
|
Link[Nlinks].Type = type; /* Valve type. */
|
|
Link[Nlinks].Stat = status; /* Valve status. */
|
|
Link[Nlinks].Rpt = 0; /* Report flag. */
|
|
Valve[Nvalves].Link = Nlinks; /* Link index. */
|
|
return(0);
|
|
} /* end of valvedata */
|
|
|
|
|
|
int patterndata()
|
|
/*
|
|
**--------------------------------------------------------------
|
|
** Input: none
|
|
** Output: returns error code
|
|
** Purpose: processes time pattern data
|
|
** Format:
|
|
** [PATTERNS]
|
|
** id mult1 mult2 .....
|
|
**--------------------------------------------------------------
|
|
*/
|
|
{
|
|
int i,n;
|
|
double x;
|
|
SFloatlist *f;
|
|
STmplist *p;
|
|
n = Ntokens - 1;
|
|
if (n < 1) return(201); /* Too few values */
|
|
if ( /* Check for new pattern */
|
|
PrevPat != NULL &&
|
|
strcmp(Tok[0],PrevPat->ID) == 0
|
|
) p = PrevPat;
|
|
else p = findID(Tok[0],Patlist);
|
|
if (p == NULL) return(205);
|
|
for (i=1; i<=n; i++) /* Add multipliers to list */
|
|
{
|
|
if (!getfloat(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;
|
|
}
|
|
Pattern[p->i].Length += n; /* Save # multipliers for pattern */
|
|
PrevPat = p; /* Set previous pattern pointer */
|
|
return(0);
|
|
} /* end of patterndata */
|
|
|
|
|
|
int curvedata()
|
|
/*
|
|
**------------------------------------------------------
|
|
** Input: none
|
|
** Output: returns error code
|
|
** Purpose: processes curve data
|
|
** Format:
|
|
** [CURVES]
|
|
** CurveID x-value y-value
|
|
**------------------------------------------------------
|
|
*/
|
|
{
|
|
double x,y;
|
|
SFloatlist *fx, *fy;
|
|
STmplist *c;
|
|
|
|
/* Check for valid curve ID */
|
|
if (Ntokens < 3) return(201);
|
|
if (
|
|
PrevCurve != NULL &&
|
|
strcmp(Tok[0],PrevCurve->ID) == 0
|
|
) c = PrevCurve;
|
|
else c = findID(Tok[0],Curvelist);
|
|
if (c == NULL) return(205);
|
|
|
|
/* Check for valid data */
|
|
if (!getfloat(Tok[1],&x)) return(202);
|
|
if (!getfloat(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;
|
|
Curve[c->i].Npts++;
|
|
|
|
/* Save the pointer to this curve */
|
|
PrevCurve = c;
|
|
return(0);
|
|
}
|
|
|
|
|
|
int demanddata()
|
|
/*
|
|
**--------------------------------------------------------------
|
|
** 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
|
|
**--------------------------------------------------------------
|
|
*/
|
|
{
|
|
int j,n,p = 0;
|
|
double y;
|
|
Pdemand demand;
|
|
STmplist *pat;
|
|
|
|
/* Extract data from tokens */
|
|
n = Ntokens;
|
|
if (n < 2) return(201);
|
|
if (!getfloat(Tok[1],&y)) return(202);
|
|
|
|
/* If MULTIPLY command, save multiplier */
|
|
if (match(Tok[0],w_MULTIPLY))
|
|
{
|
|
if (y <= 0.0) return(202);
|
|
else Dmult = y;
|
|
return(0);
|
|
}
|
|
|
|
/* Otherwise find node (and pattern) being referenced */
|
|
if ((j = findnode(Tok[0])) == 0) return(208);
|
|
if (j > Njuncs) return(208);
|
|
if (n >= 3)
|
|
{
|
|
pat = findID(Tok[2],Patlist);
|
|
if (pat == NULL) return(205);
|
|
p = pat->i;
|
|
}
|
|
|
|
/* Replace any demand entered in [JUNCTIONS] section */
|
|
/* (Such demand was temporarily stored in D[]) */
|
|
|
|
/*** Updated 6/24/02 ***/
|
|
demand = Node[j].D;
|
|
if (demand && D[j] != MISSING)
|
|
{
|
|
demand->Base = y;
|
|
demand->Pat = p;
|
|
D[j] = MISSING;
|
|
}
|
|
/*** End of update ***/
|
|
|
|
/* Otherwise add a new demand to this junction */
|
|
else
|
|
{
|
|
demand = (struct Sdemand *) malloc(sizeof(struct Sdemand));
|
|
if (demand == NULL) return(101);
|
|
demand->Base = y;
|
|
demand->Pat = p;
|
|
demand->next = Node[j].D;
|
|
Node[j].D = demand;
|
|
}
|
|
return(0);
|
|
} /* end of demanddata */
|
|
|
|
|
|
int controldata()
|
|
/*
|
|
**--------------------------------------------------------------
|
|
** 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)
|
|
**--------------------------------------------------------------
|
|
*/
|
|
{
|
|
int i = 0, /* Node index */
|
|
k, /* Link index */
|
|
n; /* # data items */
|
|
char status = ACTIVE, /* Link status */
|
|
type; /* Link or control type */
|
|
double setting = MISSING, /* Link setting */
|
|
time = 0.0, /* Simulation time */
|
|
level = 0.0; /* Pressure or tank level */
|
|
|
|
/* Check for sufficient number of input tokens */
|
|
n = Ntokens;
|
|
if (n < 6) return(201);
|
|
|
|
/* Check that controlled link exists */
|
|
k = findlink(Tok[1]);
|
|
if (k == 0) return(204);
|
|
type = Link[k].Type;
|
|
if (type == CV) return(207); /* Cannot control check valve. */
|
|
|
|
/*** Updated 9/7/00 ***/
|
|
/* Parse control setting into a status level or numerical setting. */
|
|
if (match(Tok[2],w_OPEN))
|
|
{
|
|
status = OPEN;
|
|
if (type == PUMP) setting = 1.0;
|
|
if (type == GPV) setting = Link[k].Kc;
|
|
}
|
|
else if (match(Tok[2],w_CLOSED))
|
|
{
|
|
status = CLOSED;
|
|
if (type == PUMP) setting = 0.0;
|
|
if (type == GPV) setting = Link[k].Kc;
|
|
}
|
|
else if (type == GPV) return(206);
|
|
else if (!getfloat(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 (type == PUMP || type == 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(Tok[4],w_TIME)) type = TIMER;
|
|
else if (match(Tok[4],w_CLOCKTIME)) type = TIMEOFDAY;
|
|
else
|
|
{
|
|
if (n < 8) return(201);
|
|
if ((i = findnode(Tok[5])) == 0) return(203);
|
|
if (match(Tok[6],w_BELOW)) type = LOWLEVEL;
|
|
else if (match(Tok[6],w_ABOVE)) type = HILEVEL;
|
|
else return(201);
|
|
}
|
|
|
|
/* Parse control level or time */
|
|
switch (type)
|
|
{
|
|
case TIMER:
|
|
case TIMEOFDAY:
|
|
if (n == 6) time = hour(Tok[5],"");
|
|
if (n == 7) time = hour(Tok[5],Tok[6]);
|
|
if (time < 0.0) return(201);
|
|
break;
|
|
case LOWLEVEL:
|
|
case HILEVEL:
|
|
if (!getfloat(Tok[7],&level)) return(202);
|
|
break;
|
|
}
|
|
|
|
/* Fill in fields of control data structure */
|
|
Ncontrols++;
|
|
if (Ncontrols > MaxControls) return(200);
|
|
Control[Ncontrols].Link = k;
|
|
Control[Ncontrols].Node = i;
|
|
Control[Ncontrols].Type = type;
|
|
Control[Ncontrols].Status = status;
|
|
Control[Ncontrols].Setting = setting;
|
|
Control[Ncontrols].Time = (long)(3600.0*time);
|
|
if (type == TIMEOFDAY)
|
|
Control[Ncontrols].Time %= SECperDAY;
|
|
Control[Ncontrols].Grade = level;
|
|
return(0);
|
|
} /* end of controldata */
|
|
|
|
|
|
int sourcedata()
|
|
/*
|
|
**--------------------------------------------------------------
|
|
** 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
|
|
**--------------------------------------------------------------
|
|
*/
|
|
{
|
|
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 = Ntokens;
|
|
if (n < 2) return(201);
|
|
if ((j = findnode(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(Tok[1],w_CONCEN)) type = CONCEN;
|
|
else if (match(Tok[1],w_MASS)) type = MASS;
|
|
else if (match(Tok[1],w_SETPOINT)) type = SETPOINT;
|
|
else if (match(Tok[1],w_FLOWPACED)) type = FLOWPACED;
|
|
else i = 1;
|
|
if (!getfloat(Tok[i],&c0)) return(202); /* Illegal WQ value */
|
|
|
|
if (n > i+1 && strlen(Tok[i+1]) > 0 && strcmp(Tok[i+1], "*") != 0 ) //(2.00.11 - LR)
|
|
{
|
|
pat = findID(Tok[i+1],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;
|
|
Node[j].S = source;
|
|
return(0);
|
|
} /* end of sourcedata */
|
|
|
|
|
|
int emitterdata()
|
|
/*
|
|
**--------------------------------------------------------------
|
|
** Input: none
|
|
** Output: returns error code
|
|
** Purpose: processes junction emitter data
|
|
** Format:
|
|
** [EMITTER]
|
|
** node K
|
|
**--------------------------------------------------------------
|
|
*/
|
|
{
|
|
int j, /* Node index */
|
|
n; /* # data items */
|
|
double k; /* Flow coeff, */
|
|
|
|
n = Ntokens;
|
|
if (n < 2) return(201);
|
|
if ((j = findnode(Tok[0])) == 0) return(203);
|
|
if (j > Njuncs) return(209); /* Not a junction.*/
|
|
if (!getfloat(Tok[1],&k)) return(202);
|
|
if (k < 0.0) return(202);
|
|
Node[j].Ke = k;
|
|
return(0);
|
|
}
|
|
|
|
|
|
int qualdata()
|
|
/*
|
|
**--------------------------------------------------------------
|
|
** Input: none
|
|
** Output: returns error code
|
|
** Purpose: processes initial water quality data
|
|
** Formats:
|
|
** [QUALITY]
|
|
** node initqual
|
|
** node1 node2 initqual
|
|
**--------------------------------------------------------------
|
|
*/
|
|
{
|
|
int j,n;
|
|
long i,i0,i1;
|
|
double c0;
|
|
|
|
if (Nnodes == 0) return(208); /* No nodes defined yet */
|
|
n = Ntokens;
|
|
if (n < 2) return(0);
|
|
if (n == 2) /* Single node entered */
|
|
{
|
|
if ( (j = findnode(Tok[0])) == 0) return(0);
|
|
if (!getfloat(Tok[1],&c0)) return(209);
|
|
Node[j].C0 = c0;
|
|
}
|
|
else /* Node range entered */
|
|
{
|
|
if (!getfloat(Tok[2],&c0)) return(209);
|
|
|
|
/* If numerical range supplied, then use numerical comparison */
|
|
if ((i0 = atol(Tok[0])) > 0 && (i1 = atol(Tok[1])) > 0)
|
|
{
|
|
for (j=1; j<=Nnodes; j++)
|
|
{
|
|
i = atol(Node[j].ID);
|
|
if (i >= i0 && i <= i1) Node[j].C0 = c0;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for (j=1; j<=Nnodes; j++)
|
|
if ((strcmp(Tok[0],Node[j].ID) <= 0) &&
|
|
(strcmp(Tok[1],Node[j].ID) >= 0)
|
|
) Node[j].C0 = c0;
|
|
}
|
|
}
|
|
return(0);
|
|
} /* end of qualdata */
|
|
|
|
|
|
int reactdata()
|
|
/*
|
|
**--------------------------------------------------------------
|
|
** 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
|
|
**--------------------------------------------------------------
|
|
*/
|
|
{
|
|
int item,j,n;
|
|
long i,i1,i2;
|
|
double y;
|
|
|
|
/* Skip line if insufficient data */
|
|
n = Ntokens;
|
|
if (n < 3) return(0);
|
|
|
|
/* Process input depending on keyword */
|
|
if (match(Tok[0],w_ORDER)) /* Reaction order */
|
|
{
|
|
if (!getfloat(Tok[n-1],&y)) return(213);
|
|
if (match(Tok[1],w_BULK)) BulkOrder = y;
|
|
else if (match(Tok[1],w_TANK)) TankOrder = y;
|
|
else if (match(Tok[1],w_WALL))
|
|
{
|
|
if (y == 0.0) WallOrder = 0.0;
|
|
else if (y == 1.0) WallOrder = 1.0;
|
|
else return(213);
|
|
}
|
|
else return(213);
|
|
return(0);
|
|
}
|
|
if (match(Tok[0],w_ROUGHNESS)) /* Roughness factor */
|
|
{
|
|
if (!getfloat(Tok[n-1],&y)) return(213);
|
|
Rfactor = y;
|
|
return(0);
|
|
}
|
|
if (match(Tok[0],w_LIMITING)) /* Limiting potential */
|
|
{
|
|
if (!getfloat(Tok[n-1],&y)) return(213);
|
|
/*if (y < 0.0) return(213);*/
|
|
Climit = y;
|
|
return(0);
|
|
}
|
|
if (match(Tok[0],w_GLOBAL)) /* Global rates */
|
|
{
|
|
if (!getfloat(Tok[n-1],&y)) return(213);
|
|
if (match(Tok[1],w_BULK)) Kbulk = y;
|
|
else if (match(Tok[1],w_WALL)) Kwall = y;
|
|
else return(201);
|
|
return(0);
|
|
}
|
|
if (match(Tok[0],w_BULK)) item = 1; /* Individual rates */
|
|
else if (match(Tok[0],w_WALL)) item = 2;
|
|
else if (match(Tok[0],w_TANK)) item = 3;
|
|
else return(201);
|
|
strcpy(Tok[0],Tok[1]); /* Save id in Tok[0] */
|
|
if (item == 3) /* Tank rates */
|
|
{
|
|
if (!getfloat(Tok[n-1],&y)) return(209); /* Rate coeff. */
|
|
if (n == 3)
|
|
{
|
|
if ( (j = findnode(Tok[1])) <= Njuncs) return(0);
|
|
Tank[j-Njuncs].Kb = y;
|
|
}
|
|
else
|
|
{
|
|
/* If numerical range supplied, then use numerical comparison */
|
|
if ((i1 = atol(Tok[1])) > 0 && (i2 = atol(Tok[2])) > 0)
|
|
{
|
|
for (j=Njuncs+1; j<=Nnodes; j++)
|
|
{
|
|
i = atol(Node[j].ID);
|
|
if (i >= i1 && i <= i2) Tank[j-Njuncs].Kb = y;
|
|
}
|
|
}
|
|
else for (j=Njuncs+1; j<=Nnodes; j++)
|
|
if ((strcmp(Tok[1],Node[j].ID) <= 0) &&
|
|
(strcmp(Tok[2],Node[j].ID) >= 0)
|
|
) Tank[j-Njuncs].Kb = y;
|
|
}
|
|
}
|
|
else /* Link rates */
|
|
{
|
|
if (!getfloat(Tok[n-1],&y)) return(211); /* Rate coeff. */
|
|
if (Nlinks == 0) return(0);
|
|
if (n == 3) /* Single link */
|
|
{
|
|
if ( (j = findlink(Tok[1])) == 0) return(0);
|
|
if (item == 1) Link[j].Kb = y;
|
|
else Link[j].Kw = y;
|
|
}
|
|
else /* Range of links */
|
|
{
|
|
/* If numerical range supplied, then use numerical comparison */
|
|
if ((i1 = atol(Tok[1])) > 0 && (i2 = atol(Tok[2])) > 0)
|
|
{
|
|
for (j=1; j<=Nlinks; j++)
|
|
{
|
|
i = atol(Link[j].ID);
|
|
if (i >= i1 && i <= i2)
|
|
{
|
|
if (item == 1) Link[j].Kb = y;
|
|
else Link[j].Kw = y;
|
|
}
|
|
}
|
|
}
|
|
else for (j=1; j<=Nlinks; j++)
|
|
if ((strcmp(Tok[1],Link[j].ID) <= 0) &&
|
|
(strcmp(Tok[2],Link[j].ID) >= 0) )
|
|
{
|
|
if (item == 1) Link[j].Kb = y;
|
|
else Link[j].Kw = y;
|
|
}
|
|
}
|
|
}
|
|
return(0);
|
|
} /* end of reactdata */
|
|
|
|
|
|
int mixingdata()
|
|
/*
|
|
**-------------------------------------------------------------
|
|
** Input: none
|
|
** Output: returns error code
|
|
** Purpose: processes tank mixing data
|
|
** Format:
|
|
** [MIXING]
|
|
** TankID MixModel FractVolume
|
|
**-------------------------------------------------------------
|
|
*/
|
|
{
|
|
int i,j,n;
|
|
double v;
|
|
|
|
if (Nnodes == 0) return(208); /* No nodes defined yet */
|
|
n = Ntokens;
|
|
if (n < 2) return(0);
|
|
if ( (j = findnode(Tok[0])) <= Njuncs) return(0);
|
|
if ( (i = findmatch(Tok[1],MixTxt)) < 0) return(201);
|
|
v = 1.0;
|
|
if ( (i == MIX2) &&
|
|
(n == 3) &&
|
|
(!getfloat(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 - Njuncs;
|
|
if (Tank[n].A == 0.0) return(0); /* Tank is a reservoir */
|
|
Tank[n].MixModel = (char)i;
|
|
Tank[n].V1max = v;
|
|
return(0);
|
|
}
|
|
|
|
|
|
int statusdata()
|
|
/*
|
|
**--------------------------------------------------------------
|
|
** Input: none
|
|
** Output: returns error code
|
|
** Purpose: processes link initial status data
|
|
** Formats:
|
|
** [STATUS]
|
|
** link value
|
|
** link1 (link2) value
|
|
**--------------------------------------------------------------
|
|
*/
|
|
{
|
|
int j,n;
|
|
long i,i0,i1;
|
|
double y = 0.0;
|
|
char status = ACTIVE;
|
|
|
|
if (Nlinks == 0) return(210);
|
|
n = Ntokens - 1;
|
|
if (n < 1) return(201);
|
|
|
|
/* Check for legal status setting */
|
|
if (match(Tok[n],w_OPEN)) status = OPEN;
|
|
else if (match(Tok[n],w_CLOSED)) status = CLOSED;
|
|
else if (!getfloat(Tok[n],&y)) return(211);
|
|
if (y < 0.0) return(211);
|
|
|
|
/* Single link ID supplied */
|
|
if (n == 1)
|
|
{
|
|
if ( (j = findlink(Tok[0])) == 0) return(0);
|
|
/* Cannot change status of a Check Valve */
|
|
if (Link[j].Type == CV) return(211);
|
|
|
|
/*** Updated 9/7/00 ***/
|
|
/* Cannot change setting for a GPV */
|
|
if (Link[j].Type == GPV
|
|
&& status == ACTIVE) return(211);
|
|
|
|
changestatus(j,status,y);
|
|
}
|
|
|
|
/* Range of ID's supplied */
|
|
else
|
|
{
|
|
/* Numerical range supplied */
|
|
if ((i0 = atol(Tok[0])) > 0 && (i1 = atol(Tok[1])) > 0)
|
|
{
|
|
for (j=1; j<=Nlinks; j++)
|
|
{
|
|
i = atol(Link[j].ID);
|
|
if (i >= i0 && i <= i1) changestatus(j,status,y);
|
|
}
|
|
}
|
|
else
|
|
for (j=1; j<=Nlinks; j++)
|
|
if ( (strcmp(Tok[0],Link[j].ID) <= 0) &&
|
|
(strcmp(Tok[1],Link[j].ID) >= 0)
|
|
) changestatus(j,status,y);
|
|
}
|
|
return(0);
|
|
} /* end of statusdata */
|
|
|
|
|
|
int energydata()
|
|
/*
|
|
**--------------------------------------------------------------
|
|
** 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
|
|
**--------------------------------------------------------------
|
|
*/
|
|
{
|
|
int j,k,n;
|
|
double y;
|
|
STmplist *t;
|
|
|
|
/* Check for sufficient data */
|
|
n = Ntokens;
|
|
if (n < 3) return(201);
|
|
|
|
/* Check first keyword */
|
|
if (match(Tok[0],w_DMNDCHARGE)) /* Demand charge */
|
|
{
|
|
if (!getfloat(Tok[2], &y)) return(213);
|
|
Dcost = y;
|
|
return(0);
|
|
}
|
|
if (match(Tok[0],w_GLOBAL)) /* Global parameter */
|
|
{
|
|
j = 0;
|
|
}
|
|
else if (match(Tok[0],w_PUMP)) /* Pump-specific parameter */
|
|
{
|
|
if (n < 4) return(201);
|
|
k = findlink(Tok[1]); /* Check that pump exists */
|
|
if (k == 0) return(216);
|
|
if (Link[k].Type != PUMP) return(216);
|
|
j = PUMPINDEX(k);
|
|
}
|
|
else return(201);
|
|
|
|
/* Find type of energy parameter */
|
|
if (match(Tok[n-2],w_PRICE)) /* Energy price */
|
|
{
|
|
if (!getfloat(Tok[n-1],&y))
|
|
{
|
|
if (j == 0) return(213);
|
|
else return(217);
|
|
}
|
|
if (j == 0) Ecost = y;
|
|
else Pump[j].Ecost = y;
|
|
return(0);
|
|
}
|
|
else if (match(Tok[n-2],w_PATTERN)) /* Price pattern */
|
|
{
|
|
t = findID(Tok[n-1],Patlist); /* Check if pattern exists */
|
|
if (t == NULL)
|
|
{
|
|
if (j == 0) return(213);
|
|
else return(217);
|
|
}
|
|
if (j == 0) Epat = t->i;
|
|
else Pump[j].Epat = t->i;
|
|
return(0);
|
|
}
|
|
else if (match(Tok[n-2],w_EFFIC)) /* Pump efficiency */
|
|
{
|
|
if (j == 0)
|
|
{
|
|
if (!getfloat(Tok[n-1], &y)) return(213);
|
|
if (y <= 0.0) return(213);
|
|
Epump = y;
|
|
}
|
|
else
|
|
{
|
|
t = findID(Tok[n-1],Curvelist); /* Check if curve exists */
|
|
if (t == NULL) return(217);
|
|
Pump[j].Ecurve = t->i;
|
|
}
|
|
return(0);
|
|
}
|
|
return(201);
|
|
}
|
|
|
|
|
|
int reportdata()
|
|
/*
|
|
**--------------------------------------------------------------
|
|
** 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
|
|
**--------------------------------------------------------------
|
|
*/
|
|
{
|
|
int i,j,n;
|
|
double y;
|
|
|
|
n = Ntokens - 1;
|
|
if (n < 1) return(201);
|
|
|
|
/* Value for page size */
|
|
if (match(Tok[0],w_PAGE))
|
|
{
|
|
if (!getfloat(Tok[n],&y)) return(213);
|
|
if (y < 0.0 || y > 255.0) return(213);
|
|
PageSize = (int) y;
|
|
return(0);
|
|
}
|
|
|
|
/* Request that status reports be written */
|
|
if (match(Tok[0],w_STATUS))
|
|
{
|
|
if (match(Tok[n],w_NO)) Statflag = FALSE;
|
|
if (match(Tok[n],w_YES)) Statflag = TRUE;
|
|
if (match(Tok[n],w_FULL)) Statflag = FULL;
|
|
return(0);
|
|
}
|
|
|
|
/* Request summary report */
|
|
if (match(Tok[0],w_SUMMARY))
|
|
{
|
|
if (match(Tok[n],w_NO)) Summaryflag = FALSE;
|
|
if (match(Tok[n],w_YES)) Summaryflag = TRUE;
|
|
return(0);
|
|
}
|
|
|
|
/* Request error/warning message reporting */
|
|
if (match(Tok[0],w_MESSAGES))
|
|
{
|
|
if (match(Tok[n],w_NO)) Messageflag = FALSE;
|
|
if (match(Tok[n],w_YES)) Messageflag = TRUE;
|
|
return(0);
|
|
}
|
|
|
|
|
|
/* Request an energy usage report */
|
|
if (match(Tok[0],w_ENERGY))
|
|
{
|
|
if (match(Tok[n],w_NO)) Energyflag = FALSE;
|
|
if (match(Tok[n],w_YES)) Energyflag = TRUE;
|
|
return(0);
|
|
}
|
|
|
|
/* Particular reporting nodes specified */
|
|
if (match(Tok[0],w_NODE))
|
|
{
|
|
if (match(Tok[n],w_NONE)) Nodeflag = 0; /* No nodes */
|
|
else if (match(Tok[n],w_ALL)) Nodeflag = 1; /* All nodes */
|
|
else
|
|
{
|
|
if (Nnodes == 0) return(208);
|
|
for (i=1; i<=n; i++)
|
|
{
|
|
if ( (j = findnode(Tok[i])) == 0) return(208);
|
|
Node[j].Rpt = 1;
|
|
}
|
|
Nodeflag = 2;
|
|
}
|
|
return(0);
|
|
}
|
|
|
|
/* Particular reporting links specified */
|
|
if (match(Tok[0],w_LINK))
|
|
{
|
|
if (match(Tok[n],w_NONE)) Linkflag = 0;
|
|
else if (match(Tok[n],w_ALL)) Linkflag = 1;
|
|
else
|
|
{
|
|
if (Nlinks == 0) return(210);
|
|
for (i=1; i<=n; i++)
|
|
{
|
|
if ( (j = findlink(Tok[i])) == 0) return(210);
|
|
Link[j].Rpt = 1;
|
|
}
|
|
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(Tok[0], w_HEADLOSS)) i = HEADLOSS; //(2.00.11 - LR)
|
|
else i = findmatch(Tok[0],Fldname); //(2.00.11 - LR)
|
|
if (i >= 0) //(2.00.11 - LR)
|
|
/*****************************************************************/ //(2.00.11 - LR)
|
|
{
|
|
if (i > FRICTION) return(201);
|
|
if (Ntokens == 1 || match(Tok[1],w_YES))
|
|
{
|
|
Field[i].Enabled = TRUE;
|
|
return(0);
|
|
}
|
|
if (match(Tok[1],w_NO))
|
|
{
|
|
Field[i].Enabled = FALSE;
|
|
return(0);
|
|
}
|
|
if (Ntokens < 3) return(201);
|
|
if (match(Tok[1],w_BELOW)) j = LOW; /* Get relation operator */
|
|
else if (match(Tok[1],w_ABOVE)) j = HI; /* or precision keyword */
|
|
else if (match(Tok[1],w_PRECISION)) j = PREC;
|
|
else return(201);
|
|
if (!getfloat(Tok[2],&y)) return(201);
|
|
if (j == PREC)
|
|
{
|
|
Field[i].Enabled = TRUE;
|
|
Field[i].Precision = ROUND(y);
|
|
}
|
|
else Field[i].RptLim[j] = y; /* Report limit value */
|
|
return(0);
|
|
}
|
|
|
|
/* Name of external report file */
|
|
if (match(Tok[0],w_FILE))
|
|
{
|
|
strncpy(Rpt2Fname,Tok[1],MAXFNAME);
|
|
return(0);
|
|
}
|
|
|
|
/* If get to here then return error condition */
|
|
return(201);
|
|
} /* end of reportdata */
|
|
|
|
|
|
int timedata()
|
|
/*
|
|
**--------------------------------------------------------------
|
|
** 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)
|
|
**-------------------------------------------------------------
|
|
*/
|
|
{
|
|
int n;
|
|
long t;
|
|
double y;
|
|
|
|
n = Ntokens - 1;
|
|
if (n < 1) return(201);
|
|
|
|
/* Check if setting time statistic flag */
|
|
if (match(Tok[0],w_STATISTIC))
|
|
{
|
|
if (match(Tok[n],w_NONE)) Tstatflag = SERIES;
|
|
else if (match(Tok[n],w_NO)) Tstatflag = SERIES;
|
|
else if (match(Tok[n],w_AVG)) Tstatflag = AVG;
|
|
else if (match(Tok[n],w_MIN)) Tstatflag = MIN;
|
|
else if (match(Tok[n],w_MAX)) Tstatflag = MAX;
|
|
else if (match(Tok[n],w_RANGE)) 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(Tok[n],&y))
|
|
{
|
|
if ( (y = hour(Tok[n],"")) < 0.0)
|
|
{
|
|
if ( (y = hour(Tok[n-1],Tok[n])) < 0.0) return(213);
|
|
}
|
|
}
|
|
t = (long)(3600.0*y+0.5);
|
|
/* Process the value assigned to the matched parameter */
|
|
if (match(Tok[0],w_DURATION)) Dur = t; /* Simulation duration */
|
|
else if (match(Tok[0],w_HYDRAULIC)) Hstep = t; /* Hydraulic time step */
|
|
else if (match(Tok[0],w_QUALITY)) Qstep = t; /* Quality time step */
|
|
else if (match(Tok[0],w_RULE)) Rulestep = t; /* Rule time step */
|
|
else if (match(Tok[0],w_MINIMUM)) return(0); /* Not used anymore */
|
|
else if (match(Tok[0],w_PATTERN))
|
|
{
|
|
if (match(Tok[1],w_TIME)) Pstep = t; /* Pattern time step */
|
|
else if (match(Tok[1],w_START)) Pstart = t; /* Pattern start time */
|
|
else return(201);
|
|
}
|
|
else if (match(Tok[0],w_REPORT))
|
|
{
|
|
if (match(Tok[1],w_TIME)) Rstep = t; /* Reporting time step */
|
|
else if (match(Tok[1],w_START)) Rstart = t; /* Reporting start time */
|
|
else return(201);
|
|
} /* Simulation start time*/
|
|
else if (match(Tok[0],w_START)) Tstart = t % SECperDAY;
|
|
else return(201);
|
|
return(0);
|
|
} /* end of timedata */
|
|
|
|
|
|
int optiondata()
|
|
/*
|
|
**--------------------------------------------------------------
|
|
** Input: none
|
|
** Output: returns error code
|
|
** Purpose: processes [OPTIONS] data
|
|
**--------------------------------------------------------------
|
|
*/
|
|
{
|
|
int i,n;
|
|
|
|
n = Ntokens - 1;
|
|
i = optionchoice(n); /* Option is a named choice */
|
|
if (i >= 0) return(i);
|
|
return(optionvalue(n)); /* Option is a numerical value */
|
|
} /* end of optiondata */
|
|
|
|
|
|
int optionchoice(int n)
|
|
/*
|
|
**--------------------------------------------------------------
|
|
** Input: n = index of last input token saved in 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
|
|
**--------------------------------------------------------------
|
|
*/
|
|
{
|
|
/* Check if 1st token matches a parameter name and */
|
|
/* process the input for the matched parameter */
|
|
if (n < 0) return(201);
|
|
if (match(Tok[0],w_UNITS))
|
|
{
|
|
if (n < 1) return(0);
|
|
else if (match(Tok[1],w_CFS)) Flowflag = CFS;
|
|
else if (match(Tok[1],w_GPM)) Flowflag = GPM;
|
|
else if (match(Tok[1],w_AFD)) Flowflag = AFD;
|
|
else if (match(Tok[1],w_MGD)) Flowflag = MGD;
|
|
else if (match(Tok[1],w_IMGD)) Flowflag = IMGD;
|
|
else if (match(Tok[1],w_LPS)) Flowflag = LPS;
|
|
else if (match(Tok[1],w_LPM)) Flowflag = LPM;
|
|
else if (match(Tok[1],w_CMH)) Flowflag = CMH;
|
|
else if (match(Tok[1],w_CMD)) Flowflag = CMD;
|
|
else if (match(Tok[1],w_MLD)) Flowflag = MLD;
|
|
else if (match(Tok[1],w_SI)) Flowflag = LPS;
|
|
else return(201);
|
|
}
|
|
else if (match(Tok[0],w_PRESSURE))
|
|
{
|
|
if (n < 1) return(0);
|
|
else if (match(Tok[1],w_PSI)) Pressflag = PSI;
|
|
else if (match(Tok[1],w_KPA)) Pressflag = KPA;
|
|
else if (match(Tok[1],w_METERS)) Pressflag = METERS;
|
|
else return(201);
|
|
}
|
|
else if (match(Tok[0],w_HEADLOSS))
|
|
{
|
|
if (n < 1) return(0);
|
|
else if (match(Tok[1],w_HW)) Formflag = HW;
|
|
else if (match(Tok[1],w_DW)) Formflag = DW;
|
|
else if (match(Tok[1],w_CM)) Formflag = CM;
|
|
else return(201);
|
|
}
|
|
else if (match(Tok[0],w_HYDRAULIC))
|
|
{
|
|
if (n < 2) return(0);
|
|
else if (match(Tok[1],w_USE)) Hydflag = USE;
|
|
else if (match(Tok[1],w_SAVE)) Hydflag = SAVE;
|
|
else return(201);
|
|
strncpy(HydFname,Tok[2],MAXFNAME);
|
|
}
|
|
else if (match(Tok[0],w_QUALITY))
|
|
{
|
|
if (n < 1) return(0);
|
|
else if (match(Tok[1],w_NONE)) Qualflag = NONE;
|
|
else if (match(Tok[1],w_CHEM)) Qualflag = CHEM;
|
|
else if (match(Tok[1],w_AGE)) Qualflag = AGE;
|
|
else if (match(Tok[1],w_TRACE)) Qualflag = TRACE;
|
|
else
|
|
{
|
|
Qualflag = CHEM;
|
|
strncpy(ChemName,Tok[1],MAXID);
|
|
if (n >= 2) strncpy(ChemUnits,Tok[2],MAXID);
|
|
}
|
|
if (Qualflag == TRACE) /* Source tracing option */
|
|
{
|
|
/* Copy Trace Node ID to Tok[0] for error reporting */
|
|
strcpy(Tok[0],"");
|
|
if (n < 2) return(212);
|
|
strcpy(Tok[0],Tok[2]);
|
|
TraceNode = findnode(Tok[2]);
|
|
if (TraceNode == 0) return(212);
|
|
strncpy(ChemName,u_PERCENT,MAXID);
|
|
strncpy(ChemUnits,Tok[2],MAXID);
|
|
}
|
|
if (Qualflag == AGE)
|
|
{
|
|
strncpy(ChemName,w_AGE,MAXID);
|
|
strncpy(ChemUnits,u_HOURS,MAXID);
|
|
}
|
|
}
|
|
else if (match(Tok[0],w_MAP))
|
|
{
|
|
if (n < 1) return(0);
|
|
strncpy(MapFname,Tok[1],MAXFNAME); /* Map file name */
|
|
}
|
|
else if (match(Tok[0],w_VERIFY))
|
|
{
|
|
/* Backward compatibility for verification file */
|
|
}
|
|
else if (match(Tok[0],w_UNBALANCED)) /* Unbalanced option */
|
|
{
|
|
if (n < 1) return(0);
|
|
if (match(Tok[1],w_STOP)) ExtraIter = -1;
|
|
else if (match(Tok[1],w_CONTINUE))
|
|
{
|
|
if (n >= 2) ExtraIter = atoi(Tok[2]);
|
|
else ExtraIter = 0;
|
|
}
|
|
else return(201);
|
|
}
|
|
else if (match(Tok[0],w_PATTERN)) /* Pattern option */
|
|
{
|
|
if (n < 1) return(0);
|
|
strncpy(DefPatID,Tok[1],MAXID);
|
|
}
|
|
else return(-1);
|
|
return(0);
|
|
} /* end of optionchoice */
|
|
|
|
|
|
int optionvalue(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
|
|
** TOLERANCE value
|
|
** SEGMENTS value (not used)
|
|
** ------ Undocumented Options -----
|
|
** HTOL value
|
|
** QTOL value
|
|
** RQTOL value
|
|
** CHECKFREQ value
|
|
** MAXCHECK value
|
|
** DAMPLIMIT value //(2.00.12 - LR)
|
|
**--------------------------------------------------------------
|
|
*/
|
|
{
|
|
int nvalue = 1; /* Index of token with numerical value */
|
|
double y;
|
|
|
|
/* Check for obsolete SEGMENTS keyword */
|
|
if (match(Tok[0],w_SEGMENTS)) return(0);
|
|
|
|
/* Check for missing value (which is permissible) */
|
|
if (match(Tok[0],w_SPECGRAV) || match(Tok[0],w_EMITTER)
|
|
|| match(Tok[0],w_DEMAND)) nvalue = 2;
|
|
if (n < nvalue) return(0);
|
|
|
|
/* Check for valid numerical input */
|
|
if (!getfloat(Tok[nvalue],&y)) return(213);
|
|
|
|
/* Check for WQ tolerance option (which can be 0) */
|
|
if (match(Tok[0],w_TOLERANCE))
|
|
{
|
|
if (y < 0.0) return(213);
|
|
Ctol = y; /* Quality tolerance*/
|
|
return(0);
|
|
}
|
|
|
|
/* Check for Diffusivity option */
|
|
if (match(Tok[0],w_DIFFUSIVITY))
|
|
{
|
|
if (y < 0.0) return(213);
|
|
Diffus = y;
|
|
return(0);
|
|
}
|
|
|
|
/* Check for Damping Limit option */ //(2.00.12 - LR)
|
|
if (match(Tok[0],w_DAMPLIMIT))
|
|
{
|
|
DampLimit = y;
|
|
return(0);
|
|
}
|
|
|
|
/* All other options must be > 0 */
|
|
if (y <= 0.0) return(213);
|
|
|
|
/* Assign value to specified option */
|
|
if (match(Tok[0],w_VISCOSITY)) Viscos = y; /* Viscosity */
|
|
else if (match(Tok[0],w_SPECGRAV)) SpGrav = y; /* Spec. gravity */
|
|
else if (match(Tok[0],w_TRIALS)) MaxIter = (int)y; /* Max. trials */
|
|
else if (match(Tok[0],w_ACCURACY)) /* Accuracy */
|
|
{
|
|
y = MAX(y,1.e-5);
|
|
y = MIN(y,1.e-1);
|
|
Hacc = y;
|
|
}
|
|
else if (match(Tok[0],w_HTOL)) Htol = y;
|
|
else if (match(Tok[0],w_QTOL)) Qtol = y;
|
|
else if (match(Tok[0],w_RQTOL))
|
|
{
|
|
if (y >= 1.0) return(213);
|
|
RQtol = y;
|
|
}
|
|
else if (match(Tok[0],w_CHECKFREQ)) CheckFreq = (int)y;
|
|
else if (match(Tok[0],w_MAXCHECK)) MaxCheck = (int)y;
|
|
else if (match(Tok[0],w_EMITTER)) Qexp = 1.0/y;
|
|
else if (match(Tok[0],w_DEMAND)) Dmult = y;
|
|
else return(201);
|
|
return(0);
|
|
} /* end of optionvalue */
|
|
|
|
|
|
int getpumpcurve(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]
|
|
**---------------------------------------------------------
|
|
*/
|
|
{
|
|
double a,b,c,h0,h1,h2,q1,q2;
|
|
|
|
if (n == 1) /* Const. HP curve */
|
|
{
|
|
if (X[0] <= 0.0) return(202);
|
|
Pump[Npumps].Ptype = CONST_HP;
|
|
Link[Nlinks].Km = X[0];
|
|
}
|
|
else
|
|
{
|
|
if (n == 2) /* Generic power curve */
|
|
{
|
|
q1 = X[1];
|
|
h1 = X[0];
|
|
h0 = 1.33334*h1;
|
|
q2 = 2.0*q1;
|
|
h2 = 0.0;
|
|
}
|
|
else if (n >= 5) /* 3-pt. power curve */
|
|
{
|
|
h0 = X[0];
|
|
h1 = X[1];
|
|
q1 = X[2];
|
|
h2 = X[3];
|
|
q2 = X[4];
|
|
}
|
|
else return(202);
|
|
Pump[Npumps].Ptype = POWER_FUNC;
|
|
if (!powercurve(h0,h1,h2,q1,q2,&a,&b,&c)) return(206);
|
|
Pump[Npumps].H0 = -a;
|
|
Pump[Npumps].R = -b;
|
|
Pump[Npumps].N = c;
|
|
Pump[Npumps].Q0 = q1;
|
|
Pump[Npumps].Qmax = pow((-a/b),(1.0/c));
|
|
Pump[Npumps].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(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
|
|
**--------------------------------------------------------------
|
|
*/
|
|
{
|
|
int k, vk, vj1, vj2, vtype;
|
|
|
|
/* Examine each existing valve */
|
|
for (k=1; k<=Nvalves; k++)
|
|
{
|
|
vk = Valve[k].Link;
|
|
vj1 = Link[vk].N1;
|
|
vj2 = Link[vk].N2;
|
|
vtype = Link[vk].Type;
|
|
|
|
/* Cannot have two PRVs sharing downstream nodes or in series */
|
|
if (vtype == PRV && type == PRV)
|
|
{
|
|
if (vj2 == j2 ||
|
|
vj2 == j1 ||
|
|
vj1 == j2 ) return(0);
|
|
}
|
|
|
|
/* Cannot have two PSVs sharing upstream nodes or in series */
|
|
if (vtype == PSV && type == PSV)
|
|
{
|
|
if (vj1 == j1 ||
|
|
vj1 == j2 ||
|
|
vj2 == j1 ) return(0);
|
|
}
|
|
|
|
/* Cannot have PSV connected to downstream node of PRV */
|
|
if (vtype == PSV && type == PRV && vj1 == j2) return(0);
|
|
if (vtype == PRV && type == 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 == FCV && type == PSV && vj2 == j1) return(0);
|
|
if (vtype == FCV && type == PRV && vj1 == j2) return(0);
|
|
|
|
/*** Updated 4/14/05 ***/
|
|
if (vtype == PSV && type == FCV && vj1 == j2) return (0);
|
|
if (vtype == PRV && type == FCV && vj2 == j1) return (0);
|
|
}
|
|
return(1);
|
|
} /* End of valvecheck */
|
|
|
|
|
|
void changestatus(int j, char 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.
|
|
**--------------------------------------------------------------
|
|
*/
|
|
{
|
|
if (Link[j].Type == PIPE || Link[j].Type == GPV)
|
|
{
|
|
if (status != ACTIVE) Link[j].Stat = status;
|
|
}
|
|
else if (Link[j].Type == PUMP)
|
|
{
|
|
if (status == ACTIVE)
|
|
{
|
|
Link[j].Kc = y;
|
|
status = OPEN;
|
|
if (y == 0.0) status = CLOSED;
|
|
}
|
|
else if (status == OPEN) Link[j].Kc = 1.0;
|
|
Link[j].Stat = status;
|
|
}
|
|
else if (Link[j].Type >= PRV)
|
|
{
|
|
Link[j].Kc = y;
|
|
Link[j].Stat = status;
|
|
if (status != ACTIVE) Link[j].Kc = MISSING;
|
|
}
|
|
} /* end of changestatus */
|
|
|
|
/********************** END OF INPUT3.C ************************/
|
|
|