968 lines
26 KiB
C
968 lines
26 KiB
C
/*
|
|
**********************************************************************
|
|
|
|
RULES.C -- Rule processor module for EPANET
|
|
|
|
VERSION: 2.00
|
|
DATE: 5/8/00
|
|
9/7/00
|
|
10/25/00
|
|
3/1/01
|
|
8/15/07 (2.00.11)
|
|
AUTHOR: L. Rossman
|
|
US EPA - NRMRL
|
|
|
|
The entry points for this module are:
|
|
initrules() -- called from ENopen() in EPANET.C
|
|
addrule() -- called from netsize() in INPUT2.C
|
|
allocrules() -- called from allocdata() in EPANET.C
|
|
ruledata() -- called from newline() in INPUT2.C
|
|
freerules() -- called from freedata() in EPANET.C
|
|
checkrules() -- called from ruletimestep() in HYDRAUL.C
|
|
|
|
**********************************************************************
|
|
*/
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <malloc.h>
|
|
#include "hash.h"
|
|
#include "text.h"
|
|
#include "types.h"
|
|
#include "funcs.h"
|
|
#define EXTERN extern
|
|
#include "vars.h"
|
|
|
|
struct Premise /* Rule Premise Clause */
|
|
{
|
|
int logop; /* Logical operator */
|
|
int object; /* Node or link */
|
|
int index; /* Object's index */
|
|
int variable; /* Pressure, flow, etc. */
|
|
int relop; /* Relational operator */
|
|
int status; /* Variable's status */
|
|
double value; /* Variable's value */
|
|
struct Premise *next;
|
|
};
|
|
|
|
struct Action /* Rule Action Clause */
|
|
{
|
|
int link; /* Link index */
|
|
int status; /* Link's status */
|
|
double setting; /* Link's setting */
|
|
struct Action *next;
|
|
};
|
|
|
|
struct aRule /* Control Rule Structure */
|
|
{
|
|
char label[MAXID+1]; /* Rule character label */
|
|
double priority; /* Priority level */
|
|
struct Premise *Pchain; /* Linked list of premises */
|
|
struct Action *Tchain; /* Linked list of actions if true */
|
|
struct Action *Fchain; /* Linked list of actions if false */
|
|
struct aRule *next;
|
|
};
|
|
|
|
struct ActItem /* Action list item */
|
|
{
|
|
int ruleindex; /* Index of rule action belongs to */
|
|
struct Action *action; /* An action structure */
|
|
struct ActItem *next;
|
|
};
|
|
|
|
struct aRule *Rule; /* Array of rules */
|
|
struct ActItem *ActList; /* Linked list of action items */
|
|
int RuleState; /* State of rule interpreter */
|
|
long Time1; /* Start of rule evaluation time interval (sec) */
|
|
struct Premise *Plast; /* Previous premise clause */
|
|
|
|
enum Rulewords {r_RULE,r_IF,r_AND,r_OR,r_THEN,r_ELSE,r_PRIORITY,r_ERROR};
|
|
char *Ruleword[] = {w_RULE,w_IF,w_AND,w_OR,w_THEN,w_ELSE,w_PRIORITY,NULL};
|
|
|
|
enum Varwords {r_DEMAND, r_HEAD, r_GRADE, r_LEVEL, r_PRESSURE,
|
|
r_FLOW, r_STATUS, r_SETTING, r_POWER, r_TIME,
|
|
r_CLOCKTIME, r_FILLTIME, r_DRAINTIME};
|
|
char *Varword[] = {w_DEMAND, w_HEAD, w_GRADE, w_LEVEL, w_PRESSURE,
|
|
w_FLOW, w_STATUS, w_SETTING, w_POWER,w_TIME,
|
|
w_CLOCKTIME,w_FILLTIME,w_DRAINTIME, NULL};
|
|
|
|
enum Objects {r_JUNC,r_RESERV,r_TANK,r_PIPE,r_PUMP,r_VALVE,
|
|
r_NODE,r_LINK,r_SYSTEM};
|
|
char *Object[] = {w_JUNC,w_RESERV,w_TANK,w_PIPE,w_PUMP,w_VALVE,
|
|
w_NODE,w_LINK,w_SYSTEM,NULL};
|
|
|
|
/* NOTE: place "<=" & ">=" before "<" & ">" so that findmatch() works correctly. */
|
|
enum Operators { EQ, NE, LE, GE, LT, GT, IS, NOT, BELOW, ABOVE};
|
|
char *Operator[] = {"=","<>","<=",">=","<",">",w_IS,w_NOT,w_BELOW,w_ABOVE,NULL};
|
|
|
|
enum Values {IS_NUMBER,IS_OPEN,IS_CLOSED,IS_ACTIVE};
|
|
char *Value[] = {"XXXX", w_OPEN, w_CLOSED, w_ACTIVE,NULL};
|
|
|
|
/* External variables declared in INPUT2.C */
|
|
extern char *Tok[MAXTOKS];
|
|
extern int Ntokens;
|
|
|
|
/*
|
|
** Local function prototypes are defined here and not in FUNCS.H
|
|
** because some of them utilize the Premise and Action structures
|
|
** defined locally in this module.
|
|
*/
|
|
void newrule(void);
|
|
int newpremise(int);
|
|
int newaction(void);
|
|
int newpriority(void);
|
|
int evalpremises(int);
|
|
void updateactlist(int, struct Action *);
|
|
int checkaction(int, struct Action *);
|
|
int checkpremise(struct Premise *);
|
|
int checktime(struct Premise *);
|
|
int checkstatus(struct Premise *);
|
|
int checkvalue(struct Premise *);
|
|
int takeactions(void);
|
|
void clearactlist(void);
|
|
void clearrules(void);
|
|
void ruleerrmsg(int);
|
|
|
|
|
|
void initrules()
|
|
/*
|
|
**--------------------------------------------------------------
|
|
** Initializes rule base.
|
|
** Called by ENopen() in EPANET.C module
|
|
**--------------------------------------------------------------
|
|
*/
|
|
{
|
|
RuleState = r_PRIORITY;
|
|
Rule = NULL;
|
|
}
|
|
|
|
|
|
void addrule(char *tok)
|
|
/*
|
|
**--------------------------------------------------------------
|
|
** Updates rule count if RULE keyword found in line of input.
|
|
** Called by netsize() in INPUT2.C module.
|
|
**--------------------------------------------------------------
|
|
*/
|
|
{
|
|
if (match(tok,w_RULE)) MaxRules++;
|
|
}
|
|
|
|
|
|
int allocrules()
|
|
/*
|
|
**--------------------------------------------------------------
|
|
** Allocates memory for rule-based controls.
|
|
** Called by allocdata() in EPANET.C module.
|
|
**--------------------------------------------------------------
|
|
*/
|
|
{
|
|
Rule = (struct aRule *) calloc(MaxRules+1,sizeof(struct aRule));
|
|
if (Rule == NULL) return(101);
|
|
else return(0);
|
|
}
|
|
|
|
|
|
void freerules()
|
|
/*
|
|
**--------------------------------------------------------------
|
|
** Frees memory used for rule-based controls.
|
|
** Called by freedata() in EPANET.C module.
|
|
**--------------------------------------------------------------
|
|
*/
|
|
{
|
|
clearrules();
|
|
free(Rule);
|
|
}
|
|
|
|
|
|
int checkrules(long dt)
|
|
/*
|
|
**-----------------------------------------------------
|
|
** Checks which rules should fire at current time.
|
|
** Called by ruletimestep() in HYDRAUL.C.
|
|
**-----------------------------------------------------
|
|
*/
|
|
{
|
|
int i,
|
|
r; /* Number of actions actually taken */
|
|
|
|
/* Start of rule evaluation time interval */
|
|
Time1 = Htime - dt + 1;
|
|
|
|
/* Iterate through each rule */
|
|
ActList = NULL;
|
|
r = 0;
|
|
for (i=1; i<=Nrules; i++)
|
|
{
|
|
/* If premises true, add THEN clauses to action list. */
|
|
if (evalpremises(i) == TRUE) updateactlist(i,Rule[i].Tchain);
|
|
|
|
/* If premises false, add ELSE actions to list. */
|
|
else
|
|
{
|
|
if (Rule[i].Fchain != NULL) updateactlist(i,Rule[i].Fchain);
|
|
}
|
|
}
|
|
|
|
/* Execute actions then clear list. */
|
|
if (ActList != NULL) r = takeactions();
|
|
clearactlist();
|
|
return(r);
|
|
}
|
|
|
|
|
|
int ruledata()
|
|
/*
|
|
**--------------------------------------------------------------
|
|
** Parses a line from [RULES] section of input.
|
|
** Called by newline() in INPUT2.C module.
|
|
** Tok[] is global array of tokens parsed from input line.
|
|
**--------------------------------------------------------------
|
|
*/
|
|
{
|
|
int key, /* Keyword code */
|
|
err;
|
|
|
|
/* Exit if current rule has an error */
|
|
if (RuleState == r_ERROR) return(0);
|
|
|
|
/* Find the key word that begins the rule statement */
|
|
err = 0;
|
|
key = findmatch(Tok[0],Ruleword);
|
|
switch (key)
|
|
{
|
|
case -1: err = 201; /* Unrecognized keyword */
|
|
break;
|
|
case r_RULE: Nrules++;
|
|
newrule();
|
|
RuleState = r_RULE;
|
|
break;
|
|
case r_IF: if (RuleState != r_RULE)
|
|
{
|
|
err = 221; /* Mis-placed IF clause */
|
|
break;
|
|
}
|
|
RuleState = r_IF;
|
|
err = newpremise(r_AND);
|
|
break;
|
|
case r_AND: if (RuleState == r_IF) err = newpremise(r_AND);
|
|
else if (RuleState == r_THEN || RuleState == r_ELSE)
|
|
err = newaction();
|
|
else err = 221;
|
|
break;
|
|
case r_OR: if (RuleState == r_IF) err = newpremise(r_OR);
|
|
else err = 221;
|
|
break;
|
|
case r_THEN: if (RuleState != r_IF)
|
|
{
|
|
err = 221; /* Mis-placed THEN clause */
|
|
break;
|
|
}
|
|
RuleState = r_THEN;
|
|
err = newaction();
|
|
break;
|
|
case r_ELSE: if (RuleState != r_THEN)
|
|
{
|
|
err = 221; /* Mis-placed ELSE clause */
|
|
break;
|
|
}
|
|
RuleState = r_ELSE;
|
|
err = newaction();
|
|
break;
|
|
case r_PRIORITY: if (RuleState != r_THEN && RuleState != r_ELSE)
|
|
{
|
|
err = 221;
|
|
break;
|
|
}
|
|
RuleState = r_PRIORITY;
|
|
err = newpriority();
|
|
break;
|
|
default: err = 201;
|
|
}
|
|
|
|
/* Set RuleState to r_ERROR if errors found */
|
|
if (err)
|
|
{
|
|
RuleState = r_ERROR;
|
|
ruleerrmsg(err);
|
|
err = 200;
|
|
}
|
|
return(err);
|
|
}
|
|
|
|
|
|
void clearactlist()
|
|
/*
|
|
**----------------------------------------------------------
|
|
** Clears memory used for action list
|
|
**----------------------------------------------------------
|
|
*/
|
|
{
|
|
struct ActItem *a;
|
|
struct ActItem *anext;
|
|
a = ActList;
|
|
while (a != NULL)
|
|
{
|
|
anext = a->next;
|
|
free(a);
|
|
a = anext;
|
|
}
|
|
}
|
|
|
|
|
|
void clearrules()
|
|
/*
|
|
**-----------------------------------------------------------
|
|
** Clears memory used for premises & actions for all rules
|
|
**-----------------------------------------------------------
|
|
*/
|
|
{
|
|
struct Premise *p;
|
|
struct Premise *pnext;
|
|
struct Action *a;
|
|
struct Action *anext;
|
|
int i;
|
|
for (i=1; i<=Nrules; i++)
|
|
{
|
|
p = Rule[i].Pchain;
|
|
while (p != NULL)
|
|
{
|
|
pnext = p->next;
|
|
free(p);
|
|
p = pnext;
|
|
}
|
|
a = Rule[i].Tchain;
|
|
while (a != NULL)
|
|
{
|
|
anext = a->next;
|
|
free(a);
|
|
a = anext;
|
|
}
|
|
a = Rule[i].Fchain;
|
|
while (a != NULL)
|
|
{
|
|
anext = a->next;
|
|
free(a);
|
|
a = anext;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void newrule()
|
|
/*
|
|
**----------------------------------------------------------
|
|
** Adds new rule to rule base
|
|
**----------------------------------------------------------
|
|
*/
|
|
{
|
|
strncpy(Rule[Nrules].label, Tok[1], MAXID);
|
|
Rule[Nrules].Pchain = NULL;
|
|
Rule[Nrules].Tchain = NULL;
|
|
Rule[Nrules].Fchain = NULL;
|
|
Rule[Nrules].priority = 0.0;
|
|
Plast = NULL;
|
|
}
|
|
|
|
|
|
int newpremise(int logop)
|
|
/*
|
|
**--------------------------------------------------------------------
|
|
** Adds new premise to current rule.
|
|
** Formats are:
|
|
** IF/AND/OR <object> <id> <variable> <operator> <value>
|
|
** IF/AND/OR SYSTEM <variable> <operator> <value> (units)
|
|
**
|
|
** Calls findmatch() and hour() in INPUT2.C.
|
|
** Calls findnode() and findlink() in EPANET.C.
|
|
**---------------------------------------------------------------------
|
|
*/
|
|
{
|
|
int i,j,k,m,r,s,v;
|
|
double x;
|
|
struct Premise *p;
|
|
|
|
/* Check for correct number of tokens */
|
|
if (Ntokens != 5 && Ntokens != 6) return(201);
|
|
|
|
/* Find network object & id if present */
|
|
i = findmatch(Tok[1],Object);
|
|
if (i == r_SYSTEM)
|
|
{
|
|
j = 0;
|
|
v = findmatch(Tok[2],Varword);
|
|
if (v != r_DEMAND && v != r_TIME && v != r_CLOCKTIME) return(201);
|
|
}
|
|
else
|
|
{
|
|
v = findmatch(Tok[3],Varword);
|
|
if (v < 0) return(201);
|
|
switch (i)
|
|
{
|
|
case r_NODE:
|
|
case r_JUNC:
|
|
case r_RESERV:
|
|
case r_TANK: k = r_NODE; break;
|
|
case r_LINK:
|
|
case r_PIPE:
|
|
case r_PUMP:
|
|
case r_VALVE: k = r_LINK; break;
|
|
default: return(201);
|
|
}
|
|
i = k;
|
|
if (i == r_NODE)
|
|
{
|
|
j = findnode(Tok[2]);
|
|
if (j == 0) return(203);
|
|
switch (v)
|
|
{
|
|
case r_DEMAND:
|
|
case r_HEAD:
|
|
case r_GRADE:
|
|
case r_LEVEL:
|
|
case r_PRESSURE: break;
|
|
|
|
/*** Updated 9/7/00 ***/
|
|
case r_FILLTIME:
|
|
case r_DRAINTIME: if (j <= Njuncs) return(201); break;
|
|
|
|
default: return(201);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
j = findlink(Tok[2]);
|
|
if (j == 0) return(204);
|
|
switch (v)
|
|
{
|
|
case r_FLOW:
|
|
case r_STATUS:
|
|
case r_SETTING: break;
|
|
default: return(201);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Parse relational operator (r) and check for synonyms */
|
|
if (i == r_SYSTEM) m = 3;
|
|
else m = 4;
|
|
k = findmatch(Tok[m],Operator);
|
|
if (k < 0) return(201);
|
|
switch(k)
|
|
{
|
|
case IS: r = EQ; break;
|
|
case NOT: r = NE; break;
|
|
case BELOW: r = LT; break;
|
|
case ABOVE: r = GT; break;
|
|
default: r = k;
|
|
}
|
|
|
|
/* Parse for status (s) or numerical value (x) */
|
|
s = 0;
|
|
x = MISSING;
|
|
if (v == r_TIME || v == r_CLOCKTIME)
|
|
{
|
|
if (Ntokens == 6)
|
|
x = hour(Tok[4],Tok[5])*3600.;
|
|
else
|
|
x = hour(Tok[4],"")*3600.;
|
|
if (x < 0.0) return(202);
|
|
}
|
|
else if ((k = findmatch(Tok[Ntokens-1],Value)) > IS_NUMBER) s = k;
|
|
else
|
|
{
|
|
if (!getfloat(Tok[Ntokens-1],&x)) return(202);
|
|
if (v == r_FILLTIME || v == r_DRAINTIME) x = x*3600.0; //(2.00.11 - LR)
|
|
}
|
|
|
|
|
|
|
|
/* Create new premise structure */
|
|
p = (struct Premise *) malloc(sizeof(struct Premise));
|
|
if (p == NULL) return(101);
|
|
p->object = i;
|
|
p->index = j;
|
|
p->variable = v;
|
|
p->relop = r;
|
|
p->logop = logop;
|
|
p->status = s;
|
|
p->value = x;
|
|
|
|
/* Add premise to current rule's premise list */
|
|
p->next = NULL;
|
|
if (Plast == NULL) Rule[Nrules].Pchain = p;
|
|
else Plast->next = p;
|
|
Plast = p;
|
|
return(0);
|
|
}
|
|
|
|
|
|
int newaction()
|
|
/*
|
|
**----------------------------------------------------------
|
|
** Adds new action to current rule.
|
|
** Format is:
|
|
** THEN/ELSE/AND LINK <id> <variable> IS <value>
|
|
**
|
|
** Calls findlink() from EPANET.C.
|
|
** Calls getfloat() and findmatch() from INPUT2.C.
|
|
**----------------------------------------------------------
|
|
*/
|
|
{
|
|
int j,k,s;
|
|
double x;
|
|
struct Action *a;
|
|
|
|
/* Check for correct number of tokens */
|
|
if (Ntokens != 6) return(201);
|
|
|
|
/* Check that link exists */
|
|
j = findlink(Tok[2]);
|
|
if (j == 0) return(204);
|
|
|
|
/*** Updated 9/7/00 ***/
|
|
/* Cannot control a CV */
|
|
if (Link[j].Type == CV) return(207);
|
|
|
|
/* Find value for status or setting */
|
|
s = -1;
|
|
x = MISSING;
|
|
if ((k = findmatch(Tok[5],Value)) > IS_NUMBER) s = k;
|
|
else
|
|
{
|
|
if (!getfloat(Tok[5],&x)) return(202);
|
|
if (x < 0.0) return(202);
|
|
}
|
|
|
|
/*** Updated 9/7/00 ***/
|
|
/* Cannot change setting for a GPV ***/
|
|
if (x != MISSING && Link[j].Type == GPV) return(202);
|
|
|
|
/*** Updated 3/1/01 ***/
|
|
/* Set status for pipe in case setting was specified */
|
|
if (x != MISSING && Link[j].Type == PIPE)
|
|
{
|
|
if (x == 0.0) s = IS_CLOSED;
|
|
else s = IS_OPEN;
|
|
x = MISSING;
|
|
}
|
|
|
|
/* Create a new action structure */
|
|
a = (struct Action *) malloc(sizeof(struct Action));
|
|
if (a == NULL) return(101);
|
|
a->link = j;
|
|
a->status = s;
|
|
a->setting = x;
|
|
|
|
/* Add action to current rule's action list */
|
|
if (RuleState == r_THEN)
|
|
{
|
|
a->next = Rule[Nrules].Tchain;
|
|
Rule[Nrules].Tchain = a;
|
|
}
|
|
else
|
|
{
|
|
a->next = Rule[Nrules].Fchain;
|
|
Rule[Nrules].Fchain = a;
|
|
}
|
|
return(0);
|
|
}
|
|
|
|
|
|
int newpriority()
|
|
/*
|
|
**---------------------------------------------------
|
|
** Adds priority rating to current rule
|
|
**---------------------------------------------------
|
|
*/
|
|
{
|
|
double x;
|
|
if (!getfloat(Tok[1],&x)) return(202);
|
|
Rule[Nrules].priority = x;
|
|
return(0);
|
|
}
|
|
|
|
|
|
int evalpremises(int i)
|
|
/*
|
|
**----------------------------------------------------------
|
|
** Checks if premises to rule i are true
|
|
**----------------------------------------------------------
|
|
*/
|
|
{
|
|
int result;
|
|
struct Premise *p;
|
|
|
|
result = TRUE;
|
|
p = Rule[i].Pchain;
|
|
while (p != NULL)
|
|
{
|
|
if (p->logop == r_OR)
|
|
{
|
|
if (result == FALSE)
|
|
{
|
|
result = checkpremise(p);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (result == FALSE) return(FALSE);
|
|
result = checkpremise(p);
|
|
}
|
|
p = p->next;
|
|
}
|
|
return(result);
|
|
}
|
|
|
|
|
|
int checkpremise(struct Premise *p)
|
|
/*
|
|
**----------------------------------------------------------
|
|
** Checks if a particular premise is true
|
|
**----------------------------------------------------------
|
|
*/
|
|
{
|
|
if (p->variable == r_TIME || p->variable == r_CLOCKTIME)
|
|
return(checktime(p));
|
|
else if (p->status > IS_NUMBER)
|
|
return(checkstatus(p));
|
|
else
|
|
return(checkvalue(p));
|
|
}
|
|
|
|
|
|
int checktime(struct Premise *p)
|
|
/*
|
|
**------------------------------------------------------------
|
|
** Checks if condition on system time holds
|
|
**------------------------------------------------------------
|
|
*/
|
|
{
|
|
char flag;
|
|
long t1,t2,x;
|
|
|
|
/* Get start and end of rule evaluation time interval */
|
|
if (p->variable == r_TIME)
|
|
{
|
|
t1 = Time1;
|
|
t2 = Htime;
|
|
}
|
|
else if (p->variable == r_CLOCKTIME)
|
|
{
|
|
t1 = (Time1 + Tstart) % SECperDAY;
|
|
t2 = (Htime + Tstart) % SECperDAY;
|
|
}
|
|
else return(0);
|
|
|
|
/* Test premise's time */
|
|
x = (long)(p->value);
|
|
switch (p->relop)
|
|
{
|
|
/* For inequality, test against current time */
|
|
case LT: if (t2 >= x) return(0); break;
|
|
case LE: if (t2 > x) return(0); break;
|
|
case GT: if (t2 <= x) return(0); break;
|
|
case GE: if (t2 < x) return(0); break;
|
|
|
|
/* For equality, test if within interval */
|
|
case EQ:
|
|
case NE:
|
|
flag = FALSE;
|
|
if (t2 < t1) /* E.g., 11:00 am to 1:00 am */
|
|
{
|
|
if (x >= t1 || x <= t2) flag = TRUE;
|
|
}
|
|
else
|
|
{
|
|
if (x >= t1 && x <= t2) flag = TRUE;
|
|
}
|
|
if (p->relop == EQ && flag == FALSE) return(0);
|
|
if (p->relop == NE && flag == TRUE) return(0);
|
|
break;
|
|
}
|
|
|
|
/* If we get to here then premise was satisfied */
|
|
return(1);
|
|
}
|
|
|
|
|
|
int checkstatus(struct Premise *p)
|
|
/*
|
|
**------------------------------------------------------------
|
|
** Checks if condition on link status holds
|
|
**------------------------------------------------------------
|
|
*/
|
|
{
|
|
char i;
|
|
int j;
|
|
switch (p->status)
|
|
{
|
|
case IS_OPEN:
|
|
case IS_CLOSED:
|
|
case IS_ACTIVE:
|
|
i = S[p->index];
|
|
if (i <= CLOSED) j = IS_CLOSED;
|
|
else if (i == ACTIVE) j = IS_ACTIVE;
|
|
else j = IS_OPEN;
|
|
if (j == p->status &&
|
|
p->relop == EQ) return(1);
|
|
if (j != p->status &&
|
|
p->relop == NE) return(1);
|
|
}
|
|
return(0);
|
|
}
|
|
|
|
|
|
int checkvalue(struct Premise *p)
|
|
/*
|
|
**----------------------------------------------------------
|
|
** Checks if numerical condition on a variable is true.
|
|
** Uses tolerance of 0.001 when testing conditions.
|
|
**----------------------------------------------------------
|
|
*/
|
|
{
|
|
int i,j,v;
|
|
double x,
|
|
tol = 1.e-3;
|
|
|
|
i = p->index;
|
|
v = p->variable;
|
|
switch (v)
|
|
{
|
|
|
|
/*** Updated 10/25/00 ***/
|
|
case r_DEMAND: if (p->object == r_SYSTEM) x = Dsystem*Ucf[DEMAND];
|
|
else x = D[i]*Ucf[DEMAND];
|
|
break;
|
|
|
|
case r_HEAD:
|
|
case r_GRADE: x = H[i]*Ucf[HEAD];
|
|
break;
|
|
case r_PRESSURE: x = (H[i]-Node[i].El)*Ucf[PRESSURE];
|
|
break;
|
|
case r_LEVEL: x = (H[i]-Node[i].El)*Ucf[HEAD];
|
|
break;
|
|
case r_FLOW: x = ABS(Q[i])*Ucf[FLOW];
|
|
break;
|
|
case r_SETTING: if (K[i] == MISSING) return(0);
|
|
x = K[i];
|
|
switch (Link[i].Type)
|
|
{
|
|
case PRV:
|
|
case PSV:
|
|
case PBV: x = x*Ucf[PRESSURE]; break;
|
|
case FCV: x = x*Ucf[FLOW]; break;
|
|
}
|
|
break;
|
|
case r_FILLTIME: if (i <= Njuncs) return(0);
|
|
j = i-Njuncs;
|
|
if (Tank[j].A == 0.0) return(0);
|
|
if (D[i] <= TINY) return(0);
|
|
x = (Tank[j].Vmax - Tank[j].V)/D[i];
|
|
break;
|
|
case r_DRAINTIME: if (i <= Njuncs) return(0);
|
|
j = i-Njuncs;
|
|
if (Tank[j].A == 0.0) return(0);
|
|
if (D[i] >= -TINY) return(0);
|
|
x = (Tank[j].Vmin - Tank[j].V)/D[i];
|
|
break;
|
|
default: return(0);
|
|
}
|
|
switch (p->relop)
|
|
{
|
|
case EQ: if (ABS(x - p->value) > tol) return(0);
|
|
break;
|
|
case NE: if (ABS(x - p->value) < tol) return(0);
|
|
break;
|
|
case LT: if (x > p->value + tol) return(0); break;
|
|
case LE: if (x > p->value - tol) return(0); break;
|
|
case GT: if (x < p->value - tol) return(0); break;
|
|
case GE: if (x < p->value + tol) return(0); break;
|
|
}
|
|
return(1);
|
|
}
|
|
|
|
|
|
void updateactlist(int i, struct Action *actions)
|
|
/*
|
|
**---------------------------------------------------
|
|
** Adds rule's actions to action list
|
|
**---------------------------------------------------
|
|
*/
|
|
{
|
|
struct ActItem *item;
|
|
struct Action *a;
|
|
|
|
/* Iterate through each action of Rule i */
|
|
a = actions;
|
|
while (a != NULL)
|
|
{
|
|
/* Add action to list if not already on it */
|
|
if (!checkaction(i,a))
|
|
{
|
|
item = (struct ActItem *) malloc(sizeof(struct ActItem));
|
|
if (item != NULL)
|
|
{
|
|
item->action = a;
|
|
item->ruleindex = i;
|
|
item->next = ActList;
|
|
ActList = item;
|
|
}
|
|
}
|
|
a = a->next;
|
|
}
|
|
}
|
|
|
|
|
|
int checkaction(int i, struct Action *a)
|
|
/*
|
|
**-----------------------------------------------------------
|
|
** Checks if an action is already on the Action List
|
|
**-----------------------------------------------------------
|
|
*/
|
|
{
|
|
int i1,k,k1;
|
|
struct ActItem *item;
|
|
struct Action *a1;
|
|
|
|
/* Search action list for link named in action */
|
|
k = a->link; /* Action applies to link k */
|
|
item = ActList;
|
|
while (item != NULL)
|
|
{
|
|
a1 = item->action;
|
|
i1 = item->ruleindex;
|
|
k1 = a1->link;
|
|
|
|
/* If link on list then replace action if rule has higher priority. */
|
|
if (k1 == k)
|
|
{
|
|
if (Rule[i].priority > Rule[i1].priority)
|
|
{
|
|
item->action = a;
|
|
item->ruleindex = i;
|
|
}
|
|
return(1);
|
|
}
|
|
item = item->next;
|
|
}
|
|
return(0);
|
|
}
|
|
|
|
|
|
int takeactions()
|
|
/*
|
|
**-----------------------------------------------------------
|
|
** Implements actions on action list
|
|
**-----------------------------------------------------------
|
|
*/
|
|
{
|
|
struct Action *a;
|
|
struct ActItem *item;
|
|
char flag;
|
|
int k, s, n;
|
|
double tol = 1.e-3,
|
|
v, x;
|
|
|
|
n = 0;
|
|
item = ActList;
|
|
while (item != NULL)
|
|
{
|
|
flag = FALSE;
|
|
a = item->action;
|
|
k = a->link;
|
|
s = S[k];
|
|
v = K[k];
|
|
x = a->setting;
|
|
|
|
/* Switch link from closed to open */
|
|
if (a->status == IS_OPEN && s <= CLOSED)
|
|
{
|
|
setlinkstatus(k, 1, &S[k], &K[k]);
|
|
flag = TRUE;
|
|
}
|
|
|
|
/* Switch link from not closed to closed */
|
|
else if (a->status == IS_CLOSED && s > CLOSED)
|
|
{
|
|
setlinkstatus(k, 0, &S[k], &K[k]);
|
|
flag = TRUE;
|
|
}
|
|
|
|
/* Change link's setting */
|
|
else if (x != MISSING)
|
|
{
|
|
switch(Link[k].Type)
|
|
{
|
|
case PRV:
|
|
case PSV:
|
|
case PBV: x = x/Ucf[PRESSURE]; break;
|
|
case FCV: x = x/Ucf[FLOW]; break;
|
|
}
|
|
if (ABS(x-v) > tol)
|
|
{
|
|
setlinksetting(k, x, &S[k], &K[k]);
|
|
flag = TRUE;
|
|
}
|
|
}
|
|
|
|
/* Report rule action */
|
|
if (flag == TRUE)
|
|
{
|
|
n++;
|
|
if (Statflag) writeruleaction(k,Rule[item->ruleindex].label);
|
|
}
|
|
|
|
/* Move to next action on list */
|
|
item = item->next;
|
|
}
|
|
return(n);
|
|
}
|
|
|
|
|
|
void ruleerrmsg(int err)
|
|
/*
|
|
**-----------------------------------------------------------
|
|
** Reports error message
|
|
**-----------------------------------------------------------
|
|
*/
|
|
{
|
|
int i;
|
|
char label[81];
|
|
char fmt[256];
|
|
switch (err)
|
|
{
|
|
case 201: strcpy(fmt,R_ERR201); break;
|
|
case 202: strcpy(fmt,R_ERR202); break;
|
|
case 203: strcpy(fmt,R_ERR203); break;
|
|
case 204: strcpy(fmt,R_ERR204); break;
|
|
|
|
/*** Updated on 9/7/00 ***/
|
|
case 207: strcpy(fmt,R_ERR207); break;
|
|
|
|
case 221: strcpy(fmt,R_ERR221); break;
|
|
default: return;
|
|
}
|
|
if (Nrules > 0)
|
|
{
|
|
strcpy(label,t_RULE);
|
|
strcat(label," ");
|
|
strcat(label,Rule[Nrules].label);
|
|
}
|
|
else strcpy(label,t_RULES_SECT);
|
|
sprintf(Msg,fmt);
|
|
strcat(Msg,label);
|
|
strcat(Msg,":");
|
|
writeline(Msg);
|
|
strcpy(fmt,Tok[0]);
|
|
for (i=1; i<Ntokens; i++)
|
|
{
|
|
strcat(fmt," ");
|
|
strcat(fmt,Tok[i]);
|
|
}
|
|
writeline(fmt);
|
|
}
|
|
|
|
/***************** END OF RULES.C ******************/
|
|
|