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.
1366 lines
37 KiB
C
1366 lines
37 KiB
C
/*
|
|
*********************************************************************
|
|
|
|
REPORT.C -- Reporting Routines for EPANET Program
|
|
|
|
VERSION: 2.00
|
|
DATE: 5/30/00
|
|
6/24/02
|
|
8/15/07 (2.00.11)
|
|
2/14/08 (2.00.12)
|
|
AUTHOR: L. Rossman
|
|
US EPA - NRMRL
|
|
|
|
This module contains various procedures (all beginning with
|
|
'write') that are called from other modules to write formatted
|
|
output to a report file.
|
|
|
|
It also contains function disconnected(), called from writehydwarn()
|
|
and writehyderr(), that checks if a hydraulic solution causes a
|
|
network to become disconnected.
|
|
|
|
The function writeline(pr, S) is used throughout to write a
|
|
formatted string S to the report file.
|
|
|
|
********************************************************************
|
|
*/
|
|
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#ifndef __APPLE__
|
|
#include <malloc.h>
|
|
#else
|
|
#include <stdlib.h>
|
|
#endif
|
|
#include "epanet2.h"
|
|
#include "funcs.h"
|
|
#include "hash.h"
|
|
#include "text.h"
|
|
#include "types.h"
|
|
#include <math.h>
|
|
#include <time.h>
|
|
#define EXTERN extern
|
|
#include "vars.h"
|
|
|
|
#undef WINDOWS
|
|
#ifdef _WIN32
|
|
#define WINDOWS
|
|
#define snprintf _snprintf
|
|
#endif
|
|
|
|
#define MAXCOUNT 10 /* Max. # of disconnected nodes listed */
|
|
|
|
/* Defined in enumstxt.h in EPANET.C */
|
|
extern char *NodeTxt[];
|
|
extern char *LinkTxt[];
|
|
extern char *StatTxt[];
|
|
extern char *TstatTxt[];
|
|
extern char *RptFormTxt[];
|
|
|
|
typedef REAL4 *Pfloat;
|
|
void writenodetable(EN_Project *pr, Pfloat *);
|
|
void writelinktable(EN_Project *pr, Pfloat *);
|
|
|
|
int writereport(EN_Project *pr)
|
|
/*
|
|
**------------------------------------------------------
|
|
** Input: none
|
|
** Output: returns error code
|
|
** Purpose: writes formatted output report to file
|
|
**
|
|
** Calls strcomp() from the EPANET.C module.
|
|
**------------------------------------------------------
|
|
*/
|
|
{
|
|
report_options_t *rep = &pr->report;
|
|
parser_data_t *par = &pr->parser;
|
|
|
|
char tflag;
|
|
FILE *tfile;
|
|
int errcode = 0;
|
|
|
|
/* If no secondary report file specified then */
|
|
/* write formatted output to primary report file. */
|
|
rep->Fprinterr = FALSE;
|
|
if (rep->Rptflag && strlen(rep->Rpt2Fname) == 0 && rep->RptFile != NULL) {
|
|
writecon(FMT17);
|
|
writecon(rep->Rpt1Fname);
|
|
if (rep->Energyflag)
|
|
writeenergy(pr);
|
|
errcode = writeresults(pr);
|
|
}
|
|
|
|
/* A secondary report file was specified */
|
|
else if (strlen(rep->Rpt2Fname) > 0) {
|
|
|
|
/* If secondary report file has same name as either input */
|
|
/* or primary report file then use primary report file. */
|
|
if (strcomp(rep->Rpt2Fname, par->InpFname) || strcomp(rep->Rpt2Fname, rep->Rpt1Fname)) {
|
|
writecon(FMT17);
|
|
writecon(rep->Rpt1Fname);
|
|
if (rep->Energyflag)
|
|
writeenergy(pr);
|
|
errcode = writeresults(pr);
|
|
}
|
|
|
|
/* Otherwise write report to secondary report file. */
|
|
else {
|
|
|
|
/* Try to open file */
|
|
tfile = rep->RptFile;
|
|
tflag = rep->Rptflag;
|
|
if ((rep->RptFile = fopen(rep->Rpt2Fname, "wt")) == NULL) {
|
|
rep->RptFile = tfile;
|
|
rep->Rptflag = tflag;
|
|
errcode = 303;
|
|
}
|
|
|
|
/* Write full formatted report to file */
|
|
else {
|
|
rep->Rptflag = 1;
|
|
writecon(FMT17);
|
|
writecon(rep->Rpt2Fname);
|
|
writelogo(pr);
|
|
if (rep->Summaryflag)
|
|
writesummary(pr);
|
|
if (rep->Energyflag)
|
|
writeenergy(pr);
|
|
errcode = writeresults(pr);
|
|
fclose(rep->RptFile);
|
|
rep->RptFile = tfile;
|
|
rep->Rptflag = tflag;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Special error handler for write-to-file error */
|
|
if (rep->Fprinterr)
|
|
errmsg(pr,309);
|
|
return (errcode);
|
|
} /* End of writereport */
|
|
|
|
void writelogo(EN_Project *pr)
|
|
/*
|
|
**--------------------------------------------------------------
|
|
** Input: none
|
|
** Output: none
|
|
** Purpose: writes program logo to report file.
|
|
**--------------------------------------------------------------
|
|
*/
|
|
{
|
|
report_options_t *rep = &pr->report;
|
|
|
|
int version;
|
|
int major;
|
|
int minor;
|
|
char s[80];
|
|
time_t timer; /* time_t structure & functions time() & */
|
|
/* ctime() are defined in time.h */
|
|
|
|
version = CODEVERSION;
|
|
major = version / 10000;
|
|
minor = (version % 10000) / 100;
|
|
|
|
time(&timer);
|
|
strcpy(rep->DateStamp, ctime(&timer));
|
|
rep->PageNum = 1;
|
|
rep->LineNum = 2;
|
|
fprintf(rep->RptFile, FMT18);
|
|
fprintf(rep->RptFile, "%s", rep->DateStamp);
|
|
writeline(pr, LOGO1);
|
|
writeline(pr, LOGO2);
|
|
writeline(pr, LOGO3);
|
|
writeline(pr, LOGO4);
|
|
sprintf(s, LOGO5, major, minor);
|
|
writeline(pr, s);
|
|
writeline(pr, LOGO6);
|
|
writeline(pr, "");
|
|
} /* End of writelogo */
|
|
|
|
void writesummary(EN_Project *pr)
|
|
/*
|
|
**--------------------------------------------------------------
|
|
** Input: none
|
|
** Output: none
|
|
** Purpose: writes summary system information to report file
|
|
**--------------------------------------------------------------
|
|
*/
|
|
{
|
|
EN_Network *net = &pr->network;
|
|
hydraulics_t *hyd = &pr->hydraulics;
|
|
report_options_t *rep = &pr->report;
|
|
quality_t *qu = &pr->quality;
|
|
parser_data_t *par = &pr->parser;
|
|
time_options_t *time = &pr->time_options;
|
|
time_options_t *ti = &pr->time_options;
|
|
|
|
char s[MAXFNAME + 1];
|
|
int i;
|
|
int nres = 0;
|
|
|
|
for (i = 0; i < 3; i++) {
|
|
if (strlen(pr->Title[i]) > 0) {
|
|
sprintf(s, "%-.70s", pr->Title[i]);
|
|
writeline(pr, s);
|
|
}
|
|
}
|
|
writeline(pr, " ");
|
|
sprintf(s, FMT19, par->InpFname);
|
|
writeline(pr, s);
|
|
sprintf(s, FMT20, net->Njuncs);
|
|
writeline(pr, s);
|
|
for (i = 1; i <= net->Ntanks; i++)
|
|
if (net->Tank[i].A == 0.0)
|
|
nres++;
|
|
sprintf(s, FMT21a, nres);
|
|
writeline(pr, s);
|
|
sprintf(s, FMT21b, net->Ntanks - nres);
|
|
writeline(pr, s);
|
|
sprintf(s, FMT22, net->Npipes);
|
|
writeline(pr, s);
|
|
sprintf(s, FMT23, net->Npumps);
|
|
writeline(pr, s);
|
|
sprintf(s, FMT24, net->Nvalves);
|
|
writeline(pr, s);
|
|
sprintf(s, FMT25, RptFormTxt[hyd->Formflag]);
|
|
writeline(pr, s);
|
|
sprintf(s, FMT26, time->Hstep * pr->Ucf[TIME], rep->Field[TIME].Units);
|
|
writeline(pr, s);
|
|
sprintf(s, FMT27, hyd->Hacc);
|
|
writeline(pr, s);
|
|
|
|
if (hyd->HeadErrorLimit > 0.0) {
|
|
sprintf(s, FMT27d, hyd->HeadErrorLimit*pr->Ucf[HEAD], rep->Field[HEAD].Units);
|
|
writeline(pr, s);
|
|
}
|
|
if (hyd->FlowChangeLimit > 0.0) {
|
|
sprintf(s, FMT27e, hyd->FlowChangeLimit*pr->Ucf[FLOW], rep->Field[FLOW].Units);
|
|
writeline(pr, s);
|
|
}
|
|
|
|
sprintf(s, FMT27a, hyd->CheckFreq);
|
|
writeline(pr, s);
|
|
sprintf(s, FMT27b, hyd->MaxCheck);
|
|
writeline(pr, s);
|
|
sprintf(s, FMT27c, hyd->DampLimit);
|
|
writeline(pr, s);
|
|
|
|
sprintf(s, FMT28, hyd->MaxIter);
|
|
writeline(pr, s);
|
|
if (qu->Qualflag == NONE || time->Dur == 0.0)
|
|
sprintf(s, FMT29);
|
|
else if (qu->Qualflag == CHEM)
|
|
sprintf(s, FMT30, qu->ChemName);
|
|
else if (qu->Qualflag == TRACE)
|
|
sprintf(s, FMT31, net->Node[qu->TraceNode].ID);
|
|
else if (qu->Qualflag == AGE)
|
|
sprintf(s, FMT32);
|
|
writeline(pr, s);
|
|
if (qu->Qualflag != NONE && ti->Dur > 0) {
|
|
sprintf(s, FMT33, (float)qu->Qstep / 60.0);
|
|
writeline(pr, s);
|
|
sprintf(s, FMT34, qu->Ctol * pr->Ucf[QUALITY], rep->Field[QUALITY].Units);
|
|
writeline(pr, s);
|
|
}
|
|
sprintf(s, FMT36, hyd->SpGrav);
|
|
writeline(pr, s);
|
|
sprintf(s, FMT37a, hyd->Viscos / VISCOS);
|
|
writeline(pr, s);
|
|
sprintf(s, FMT37b, qu->Diffus / DIFFUS);
|
|
writeline(pr, s);
|
|
sprintf(s, FMT38, hyd->Dmult);
|
|
writeline(pr, s);
|
|
sprintf(s, FMT39, time->Dur * pr->Ucf[TIME], rep->Field[TIME].Units);
|
|
writeline(pr, s);
|
|
if (rep->Rptflag) {
|
|
sprintf(s, FMT40);
|
|
writeline(pr, s);
|
|
if (rep->Nodeflag == 0)
|
|
writeline(pr, FMT41);
|
|
if (rep->Nodeflag == 1)
|
|
writeline(pr, FMT42);
|
|
if (rep->Nodeflag == 2)
|
|
writeline(pr, FMT43);
|
|
writelimits(pr, DEMAND, QUALITY);
|
|
if (rep->Linkflag == 0)
|
|
writeline(pr, FMT44);
|
|
if (rep->Linkflag == 1)
|
|
writeline(pr, FMT45);
|
|
if (rep->Linkflag == 2)
|
|
writeline(pr, FMT46);
|
|
writelimits(pr, DIAM, HEADLOSS);
|
|
}
|
|
writeline(pr, " ");
|
|
} /* End of writesummary */
|
|
|
|
void writehydstat(EN_Project *pr, int iter, double relerr)
|
|
/*
|
|
**--------------------------------------------------------------
|
|
** Input: iter = # iterations to find hydraulic solution
|
|
** relerr = convergence error in hydraulic solution
|
|
** Output: none
|
|
** Purpose: writes hydraulic status report for solution found
|
|
** at current time period to report file
|
|
**--------------------------------------------------------------
|
|
*/
|
|
{
|
|
EN_Network *net = &pr->network;
|
|
hydraulics_t *hyd = &pr->hydraulics;
|
|
report_options_t *rep = &pr->report;
|
|
time_options_t *time = &pr->time_options;
|
|
|
|
|
|
double *NodeDemand = hyd->NodeDemand;
|
|
Stank *Tank = net->Tank;
|
|
Slink *Link = net->Link;
|
|
|
|
int i, n;
|
|
StatType newstat;
|
|
char s1[MAXLINE + 1];
|
|
|
|
/*** Updated 6/24/02 ***/
|
|
char atime[13];
|
|
|
|
/* Display system status */
|
|
strcpy(atime, clocktime(rep->Atime, time->Htime));
|
|
if (iter > 0) {
|
|
if (relerr <= hyd->Hacc)
|
|
sprintf(s1, FMT58, atime, iter);
|
|
else
|
|
sprintf(s1, FMT59, atime, iter, relerr);
|
|
writeline(pr, s1);
|
|
}
|
|
|
|
/*
|
|
Display status changes for tanks.
|
|
D[n] is net inflow to tank at node n.
|
|
Old tank status is stored in OldStat[]
|
|
at indexes Nlinks+1 to Nlinks+Ntanks.
|
|
*/
|
|
for (i = 1; i <= net->Ntanks; i++) {
|
|
n = net->Tank[i].Node;
|
|
if (ABS(NodeDemand[n]) < 0.001)
|
|
newstat = CLOSED;
|
|
else if (NodeDemand[n] > 0.0)
|
|
newstat = FILLING;
|
|
else if (NodeDemand[n] < 0.0)
|
|
newstat = EMPTYING;
|
|
else
|
|
newstat = hyd->OldStat[net->Nlinks + i];
|
|
if (newstat != hyd->OldStat[net->Nlinks + i]) {
|
|
if (Tank[i].A > 0.0)
|
|
snprintf(s1, MAXLINE, FMT50, atime, net->Node[n].ID, StatTxt[newstat],
|
|
(hyd->NodeHead[n] - net->Node[n].El) * pr->Ucf[HEAD],
|
|
rep->Field[HEAD].Units);
|
|
else
|
|
snprintf(s1, MAXLINE, FMT51, atime, net->Node[n].ID, StatTxt[newstat]);
|
|
writeline(pr, s1);
|
|
hyd->OldStat[net->Nlinks + i] = newstat;
|
|
}
|
|
}
|
|
|
|
/* Display status changes for links */
|
|
for (i = 1; i <= net->Nlinks; i++) {
|
|
if (hyd->LinkStatus[i] != hyd->OldStat[i]) {
|
|
if (time->Htime == 0)
|
|
sprintf(s1, FMT52, atime, LinkTxt[(int)net->Link[i].Type], net->Link[i].ID,
|
|
StatTxt[(int)hyd->LinkStatus[i]]);
|
|
else
|
|
sprintf(s1, FMT53, atime, LinkTxt[Link[i].Type], net->Link[i].ID,
|
|
StatTxt[hyd->OldStat[i]], StatTxt[hyd->LinkStatus[i]]);
|
|
writeline(pr, s1);
|
|
hyd->OldStat[i] = hyd->LinkStatus[i];
|
|
}
|
|
}
|
|
writeline(pr, " ");
|
|
} /* End of writehydstat */
|
|
|
|
void writeenergy(EN_Project *pr)
|
|
/*
|
|
**-------------------------------------------------------------
|
|
** Input: none
|
|
** Output: none
|
|
** Purpose: writes energy usage report to report file
|
|
**-------------------------------------------------------------
|
|
*/
|
|
{
|
|
EN_Network *net = &pr->network;
|
|
hydraulics_t *hyd = &pr->hydraulics;
|
|
report_options_t *rep = &pr->report;
|
|
Spump *pump;
|
|
|
|
int j;
|
|
double csum;
|
|
char s[MAXLINE + 1];
|
|
if (net->Npumps == 0)
|
|
return;
|
|
writeline(pr, " ");
|
|
writeheader(pr,ENERHDR, 0);
|
|
csum = 0.0;
|
|
for (j = 1; j <= net->Npumps; j++) {
|
|
pump = &net->Pump[j];
|
|
csum += pump->Energy[TOTAL_COST];
|
|
if (rep->LineNum == (long)rep->PageSize)
|
|
writeheader(pr, ENERHDR, 1);
|
|
sprintf(s, "%-8s %6.2f %6.2f %9.2f %9.2f %9.2f %9.2f",
|
|
net->Link[pump->Link].ID, pump->Energy[PCNT_ONLINE], pump->Energy[PCNT_EFFIC],
|
|
pump->Energy[KWH_PER_FLOW], pump->Energy[TOTAL_KWH], pump->Energy[MAX_KW],
|
|
pump->Energy[TOTAL_COST]);
|
|
writeline(pr, s);
|
|
}
|
|
fillstr(s, '-', 63);
|
|
writeline(pr, s);
|
|
|
|
/*** Updated 6/24/02 ***/
|
|
sprintf(s, FMT74, "", hyd->Emax * hyd->Dcost);
|
|
writeline(pr, s);
|
|
sprintf(s, FMT75, "", csum + hyd->Emax * hyd->Dcost);
|
|
/*** End of update ***/
|
|
|
|
writeline(pr, s);
|
|
writeline(pr, " ");
|
|
} /* End of writeenergy */
|
|
|
|
int writeresults(EN_Project *pr)
|
|
/*
|
|
**--------------------------------------------------------------
|
|
** Input: none
|
|
** Output: returns error code
|
|
** Purpose: writes simulation results to report file
|
|
**--------------------------------------------------------------
|
|
*/
|
|
{
|
|
EN_Network *net = &pr->network;
|
|
out_file_t *out = &pr->out_files;
|
|
report_options_t *rep = &pr->report;
|
|
time_options_t *time = &pr->time_options;
|
|
FILE *outFile = out->OutFile;
|
|
|
|
|
|
Pfloat *x; /* Array of pointers to floats */
|
|
int j, m, n, np, nnv, nlv;
|
|
int errcode = 0;
|
|
|
|
/*
|
|
**-----------------------------------------------------------
|
|
** NOTE: The OutFile contains results for 4 node variables
|
|
** (demand, head, pressure, & quality) and 8 link
|
|
** variables (flow, velocity, headloss, quality,
|
|
** status, setting, reaction rate & friction factor)
|
|
** at each reporting time.
|
|
**-----------------------------------------------------------
|
|
*/
|
|
|
|
/* Return if no output file */
|
|
if (outFile == NULL)
|
|
return (106);
|
|
|
|
/* Return if no nodes or links selected for reporting */
|
|
/* or if no node or link report variables enabled. */
|
|
if (!rep->Nodeflag && !rep->Linkflag) {
|
|
return (errcode);
|
|
}
|
|
nnv = 0;
|
|
for (j = ELEV; j <= QUALITY; j++) {
|
|
nnv += rep->Field[j].Enabled;
|
|
}
|
|
nlv = 0;
|
|
for (j = LENGTH; j <= FRICTION; j++) {
|
|
nlv += rep->Field[j].Enabled;
|
|
}
|
|
if (nnv == 0 && nlv == 0) {
|
|
return (errcode);
|
|
}
|
|
|
|
/* Allocate memory for output variables. */
|
|
/* m = larger of # node variables & # link variables */
|
|
/* n = larger of # nodes & # links */
|
|
m = MAX((QUALITY - DEMAND + 1), (FRICTION - FLOW + 1));
|
|
n = MAX((net->Nnodes + 1), (net->Nlinks + 1));
|
|
x = (Pfloat *)calloc(m, sizeof(Pfloat));
|
|
ERRCODE(MEMCHECK(x));
|
|
if (errcode)
|
|
return (errcode);
|
|
for (j = 0; j < m; j++) {
|
|
x[j] = (REAL4 *)calloc(n, sizeof(REAL4));
|
|
ERRCODE(MEMCHECK(x[j]));
|
|
}
|
|
if (errcode)
|
|
return (errcode);
|
|
|
|
/* Re-position output file & initialize report time. */
|
|
fseek(outFile, out->OutOffset2, SEEK_SET);
|
|
time->Htime = time->Rstart;
|
|
|
|
/* For each reporting time: */
|
|
for (np = 1; np <= rep->Nperiods; np++) {
|
|
|
|
/* Read in node results & write node table. */
|
|
/* (Remember to offset x[j] by 1 because array is zero-based). */
|
|
for (j = DEMAND; j <= QUALITY; j++) {
|
|
fread((x[j - DEMAND]) + 1, sizeof(REAL4), net->Nnodes, outFile);
|
|
}
|
|
if (nnv > 0 && rep->Nodeflag > 0) {
|
|
writenodetable(pr,x);
|
|
}
|
|
|
|
/* Read in link results & write link table. */
|
|
for (j = FLOW; j <= FRICTION; j++) {
|
|
fread((x[j - FLOW]) + 1, sizeof(REAL4), net->Nlinks, outFile);
|
|
}
|
|
if (nlv > 0 && rep->Linkflag > 0) {
|
|
writelinktable(pr,x);
|
|
}
|
|
time->Htime += time->Rstep;
|
|
}
|
|
|
|
/* Free allocated memory */
|
|
for (j = 0; j < m; j++) {
|
|
free(x[j]);
|
|
}
|
|
free(x);
|
|
return (errcode);
|
|
} /* End of writereport */
|
|
|
|
void writenodetable(EN_Project *pr, Pfloat *x)
|
|
/*
|
|
**---------------------------------------------------------------
|
|
** Input: x = pointer to node results for current time
|
|
** Output: none
|
|
** Purpose: writes node results for current time to report file
|
|
**---------------------------------------------------------------
|
|
*/
|
|
{
|
|
EN_Network *net = &pr->network;
|
|
report_options_t *rep = &pr->report;
|
|
|
|
int i, j;
|
|
char s[MAXLINE + 1], s1[16];
|
|
double y[MAXVAR];
|
|
|
|
/* Write table header */
|
|
writeheader(pr, NODEHDR, 0);
|
|
|
|
/* For each node: */
|
|
for (i = 1; i <= net->Nnodes; i++) {
|
|
Snode *node = &net->Node[i];
|
|
/* Place results for each node variable in y */
|
|
y[ELEV] = node->El * pr->Ucf[ELEV];
|
|
for (j = DEMAND; j <= QUALITY; j++)
|
|
y[j] = *((x[j - DEMAND]) + i);
|
|
|
|
/* Check if node gets reported on */
|
|
if ((rep->Nodeflag == 1 || node->Rpt) && checklimits(rep, y, ELEV, QUALITY)) {
|
|
|
|
/* Check if new page needed */
|
|
if (rep->LineNum == (long)rep->PageSize)
|
|
writeheader(pr, NODEHDR, 1);
|
|
|
|
/* Add node ID and each reported field to string s */
|
|
sprintf(s, "%-15s", node->ID);
|
|
for (j = ELEV; j <= QUALITY; j++) {
|
|
if (rep->Field[j].Enabled == TRUE) {
|
|
|
|
/*** Updated 6/24/02 ***/
|
|
if (fabs(y[j]) > 1.e6)
|
|
sprintf(s1, "%10.2e", y[j]);
|
|
else
|
|
sprintf(s1, "%10.*f", rep->Field[j].Precision, y[j]);
|
|
/*** End of update ***/
|
|
|
|
strcat(s, s1);
|
|
}
|
|
}
|
|
|
|
/* Note if node is a reservoir/tank */
|
|
if (i > net->Njuncs) {
|
|
strcat(s, " ");
|
|
strcat(s, NodeTxt[getnodetype(net,i)]);
|
|
}
|
|
|
|
/* Write results for node */
|
|
writeline(pr, s);
|
|
}
|
|
}
|
|
writeline(pr, " ");
|
|
}
|
|
|
|
void writelinktable(EN_Project *pr, Pfloat *x)
|
|
/*
|
|
**---------------------------------------------------------------
|
|
** Input: x = pointer to link results for current time
|
|
** Output: none
|
|
** Purpose: writes link results for current time to report file
|
|
**---------------------------------------------------------------
|
|
*/
|
|
{
|
|
EN_Network *net = &pr->network;
|
|
report_options_t *rep = &pr->report;
|
|
Slink *Link = net->Link;
|
|
double *Ucf = pr->Ucf;
|
|
|
|
|
|
int i, j, k;
|
|
char s[MAXLINE + 1], s1[16];
|
|
double y[MAXVAR];
|
|
|
|
/* Write table header */
|
|
writeheader(pr,LINKHDR, 0);
|
|
|
|
/* For each link: */
|
|
for (i = 1; i <= net->Nlinks; i++) {
|
|
|
|
/* Place results for each link variable in y */
|
|
y[LENGTH] = Link[i].Len * Ucf[LENGTH];
|
|
y[DIAM] = Link[i].Diam * Ucf[DIAM];
|
|
for (j = FLOW; j <= FRICTION; j++)
|
|
y[j] = *((x[j - FLOW]) + i);
|
|
|
|
/* Check if link gets reported on */
|
|
if ((rep->Linkflag == 1 || Link[i].Rpt) && checklimits(rep, y, DIAM, FRICTION)) {
|
|
|
|
/* Check if new page needed */
|
|
if (rep->LineNum == (long)rep->PageSize)
|
|
writeheader(pr,LINKHDR, 1);
|
|
|
|
/* Add link ID and each reported field to string s */
|
|
sprintf(s, "%-15s", Link[i].ID);
|
|
for (j = LENGTH; j <= FRICTION; j++) {
|
|
if (rep->Field[j].Enabled == TRUE) {
|
|
if (j == STATUS) {
|
|
if (y[j] <= CLOSED)
|
|
k = CLOSED;
|
|
else if (y[j] == ACTIVE)
|
|
k = ACTIVE;
|
|
else
|
|
k = OPEN;
|
|
sprintf(s1, "%10s", StatTxt[k]);
|
|
}
|
|
|
|
/*** Updated 6/24/02 ***/
|
|
else {
|
|
if (fabs(y[j]) > 1.e6)
|
|
sprintf(s1, "%10.2e", y[j]);
|
|
else
|
|
sprintf(s1, "%10.*f", rep->Field[j].Precision, y[j]);
|
|
}
|
|
/*** End of update ***/
|
|
|
|
strcat(s, s1);
|
|
}
|
|
}
|
|
|
|
/* Note if link is a pump or valve */
|
|
if ((j = Link[i].Type) > EN_PIPE) {
|
|
strcat(s, " ");
|
|
strcat(s, LinkTxt[j]);
|
|
}
|
|
|
|
/* Write results for link */
|
|
writeline(pr, s);
|
|
}
|
|
}
|
|
writeline(pr, " ");
|
|
}
|
|
|
|
void writeheader(EN_Project *pr, int type, int contin)
|
|
/*
|
|
**--------------------------------------------------------------
|
|
** Input: type = table type
|
|
** contin = table continuation flag
|
|
** Output: none
|
|
** Purpose: writes column headings for output report tables
|
|
**--------------------------------------------------------------
|
|
*/
|
|
{
|
|
report_options_t *rep = &pr->report;
|
|
quality_t *qu = &pr->quality;
|
|
parser_data_t *par = &pr->parser;
|
|
time_options_t *time = &pr->time_options;
|
|
|
|
char s[MAXLINE + 1], s1[MAXLINE + 1], s2[MAXLINE + 1], s3[MAXLINE + 1];
|
|
int i, n;
|
|
|
|
/* Move to next page if < 11 lines remain on current page. */
|
|
if (rep->Rptflag && rep->LineNum + 11 > (long)rep->PageSize) {
|
|
while (rep->LineNum < (long)rep->PageSize)
|
|
writeline(pr, " ");
|
|
}
|
|
writeline(pr, " ");
|
|
|
|
/* Hydraulic Status Table */
|
|
if (type == STATHDR) {
|
|
sprintf(s, FMT49);
|
|
if (contin)
|
|
strcat(s, t_CONTINUED);
|
|
writeline(pr, s);
|
|
fillstr(s, '-', 70);
|
|
writeline(pr, s);
|
|
}
|
|
|
|
/* Energy Usage Table */
|
|
if (type == ENERHDR) {
|
|
if (par->Unitsflag == SI)
|
|
strcpy(s1, t_perM3);
|
|
else
|
|
strcpy(s1, t_perMGAL);
|
|
sprintf(s, FMT71);
|
|
if (contin)
|
|
strcat(s, t_CONTINUED);
|
|
writeline(pr, s);
|
|
fillstr(s, '-', 63);
|
|
writeline(pr, s);
|
|
sprintf(s, FMT72);
|
|
writeline(pr, s);
|
|
sprintf(s, FMT73, s1);
|
|
writeline(pr, s);
|
|
fillstr(s, '-', 63);
|
|
writeline(pr, s);
|
|
}
|
|
|
|
/* Node Results Table */
|
|
if (type == NODEHDR) {
|
|
if (rep->Tstatflag == RANGE)
|
|
sprintf(s, FMT76, t_DIFFER);
|
|
else if (rep->Tstatflag != SERIES)
|
|
sprintf(s, FMT76, TstatTxt[rep->Tstatflag]);
|
|
else if (time->Dur == 0)
|
|
sprintf(s, FMT77);
|
|
else
|
|
sprintf(s, FMT78, clocktime(rep->Atime, time->Htime));
|
|
if (contin)
|
|
strcat(s, t_CONTINUED);
|
|
writeline(pr, s);
|
|
n = 15;
|
|
sprintf(s2, "%15s", "");
|
|
strcpy(s, t_NODEID);
|
|
sprintf(s3, "%-15s", s);
|
|
for (i = ELEV; i < QUALITY; i++)
|
|
if (rep->Field[i].Enabled == TRUE) {
|
|
n += 10;
|
|
sprintf(s, "%10s", rep->Field[i].Name);
|
|
strcat(s2, s);
|
|
sprintf(s, "%10s", rep->Field[i].Units);
|
|
strcat(s3, s);
|
|
}
|
|
if (rep->Field[QUALITY].Enabled == TRUE) {
|
|
n += 10;
|
|
sprintf(s, "%10s", qu->ChemName);
|
|
strcat(s2, s);
|
|
sprintf(s, "%10s", qu->ChemUnits);
|
|
strcat(s3, s);
|
|
}
|
|
fillstr(s1, '-', n);
|
|
writeline(pr, s1);
|
|
writeline(pr, s2);
|
|
writeline(pr, s3);
|
|
writeline(pr, s1);
|
|
}
|
|
|
|
/* Link Results Table */
|
|
if (type == LINKHDR) {
|
|
if (rep->Tstatflag == RANGE)
|
|
sprintf(s, FMT79, t_DIFFER);
|
|
else if (rep->Tstatflag != SERIES)
|
|
sprintf(s, FMT79, TstatTxt[rep->Tstatflag]);
|
|
else if (time->Dur == 0)
|
|
sprintf(s, FMT80);
|
|
else
|
|
sprintf(s, FMT81, clocktime(rep->Atime, time->Htime));
|
|
if (contin)
|
|
strcat(s, t_CONTINUED);
|
|
writeline(pr, s);
|
|
n = 15;
|
|
sprintf(s2, "%15s", "");
|
|
strcpy(s, t_LINKID);
|
|
sprintf(s3, "%-15s", s);
|
|
for (i = LENGTH; i <= FRICTION; i++)
|
|
if (rep->Field[i].Enabled == TRUE) {
|
|
n += 10;
|
|
sprintf(s, "%10s", rep->Field[i].Name);
|
|
strcat(s2, s);
|
|
sprintf(s, "%10s", rep->Field[i].Units);
|
|
strcat(s3, s);
|
|
}
|
|
fillstr(s1, '-', n);
|
|
writeline(pr, s1);
|
|
writeline(pr, s2);
|
|
writeline(pr, s3);
|
|
writeline(pr, s1);
|
|
}
|
|
} /* End of writeheader */
|
|
|
|
void writeline(EN_Project *pr, char *s)
|
|
/*
|
|
**--------------------------------------------------------------
|
|
** Input: *s = text string
|
|
** Output: none
|
|
** Purpose: writes a line of output to report file
|
|
**--------------------------------------------------------------
|
|
*/
|
|
{
|
|
report_options_t *rpt = &pr->report;
|
|
|
|
if (rpt->RptFile == NULL) {
|
|
return;
|
|
}
|
|
if (rpt->Rptflag) {
|
|
if (rpt->LineNum == (long)rpt->PageSize) {
|
|
rpt->PageNum++;
|
|
if (fprintf(rpt->RptFile, FMT82, (int)rpt->PageNum, pr->Title[0]) == EOF) {
|
|
rpt->Fprinterr = TRUE;
|
|
}
|
|
rpt->LineNum = 3;
|
|
}
|
|
}
|
|
if (fprintf(rpt->RptFile, "\n %s", s) == EOF) {
|
|
rpt->Fprinterr = TRUE;
|
|
}
|
|
rpt->LineNum++;
|
|
} /* End of writeline */
|
|
|
|
void writerelerr(EN_Project *pr, int iter, double relerr)
|
|
/*
|
|
**-----------------------------------------------------------------
|
|
** Input: iter = current iteration of hydraulic solution
|
|
** relerr = current convergence error
|
|
** Output: none
|
|
** Purpose: writes out convergence status of hydraulic solution
|
|
**-----------------------------------------------------------------
|
|
*/
|
|
{
|
|
report_options_t *rep = &pr->report;
|
|
time_options_t *time = &pr->time_options;
|
|
|
|
if (iter == 0) {
|
|
sprintf(pr->Msg, FMT64, clocktime(rep->Atime, time->Htime));
|
|
writeline(pr, pr->Msg);
|
|
} else {
|
|
sprintf(pr->Msg, FMT65, iter, relerr);
|
|
writeline(pr, pr->Msg);
|
|
}
|
|
} /* End of writerelerr */
|
|
|
|
void writestatchange(EN_Project *pr, int k, char s1, char s2)
|
|
/*
|
|
**--------------------------------------------------------------
|
|
** Input: k = link index
|
|
** s1 = old link status
|
|
** s2 = new link status
|
|
** Output: none
|
|
** Purpose: writes change in link status to output report
|
|
**--------------------------------------------------------------
|
|
*/
|
|
{
|
|
EN_Network *net = &pr->network;
|
|
hydraulics_t *hyd = &pr->hydraulics;
|
|
Slink *Link = net->Link;
|
|
double *Ucf = pr->Ucf;
|
|
double *LinkSetting = hyd->LinkSetting;
|
|
|
|
int j1, j2;
|
|
double setting;
|
|
|
|
/* We have a pump/valve setting change instead of a status change */
|
|
if (s1 == s2) {
|
|
|
|
/*** Updated 10/25/00 ***/
|
|
setting = LinkSetting[k]; // Link[k].Kc;
|
|
|
|
switch (Link[k].Type) {
|
|
case EN_PRV:
|
|
case EN_PSV:
|
|
case EN_PBV:
|
|
setting *= Ucf[PRESSURE];
|
|
break;
|
|
case EN_FCV:
|
|
setting *= Ucf[FLOW];
|
|
default:
|
|
break;
|
|
}
|
|
sprintf(pr->Msg, FMT56, LinkTxt[Link[k].Type], Link[k].ID, setting);
|
|
writeline(pr, pr->Msg);
|
|
return;
|
|
}
|
|
|
|
/* We have a status change. Write the old & new status types. */
|
|
if (s1 == ACTIVE)
|
|
j1 = ACTIVE;
|
|
else if (s1 <= CLOSED)
|
|
j1 = CLOSED;
|
|
else
|
|
j1 = OPEN;
|
|
if (s2 == ACTIVE)
|
|
j2 = ACTIVE;
|
|
else if (s2 <= CLOSED)
|
|
j2 = CLOSED;
|
|
else
|
|
j2 = OPEN;
|
|
if (j1 != j2) {
|
|
sprintf(pr->Msg, FMT57, LinkTxt[Link[k].Type], Link[k].ID, StatTxt[j1],
|
|
StatTxt[j2]);
|
|
writeline(pr, pr->Msg);
|
|
}
|
|
} /* End of writestatchange */
|
|
|
|
void writecontrolaction(EN_Project *pr, int k, int i)
|
|
/*
|
|
----------------------------------------------------------------
|
|
** Input: k = link index
|
|
** i = control index
|
|
** Output: none
|
|
** Purpose: writes control action taken to status report
|
|
**--------------------------------------------------------------
|
|
*/
|
|
{
|
|
EN_Network *net = &pr->network;
|
|
report_options_t *rep = &pr->report;
|
|
time_options_t *time = &pr->time_options;
|
|
Snode *Node = net->Node;
|
|
Slink *Link = net->Link;
|
|
Scontrol *Control = net->Control;
|
|
|
|
int n;
|
|
switch (Control[i].Type) {
|
|
case LOWLEVEL:
|
|
case HILEVEL:
|
|
n = Control[i].Node;
|
|
sprintf(pr->Msg, FMT54, clocktime(rep->Atime, time->Htime), LinkTxt[Link[k].Type],
|
|
Link[k].ID, NodeTxt[getnodetype(net,n)], Node[n].ID);
|
|
break;
|
|
case TIMER:
|
|
case TIMEOFDAY:
|
|
sprintf(pr->Msg, FMT55, clocktime(rep->Atime, time->Htime), LinkTxt[Link[k].Type], Link[k].ID);
|
|
break;
|
|
default:
|
|
return;
|
|
}
|
|
writeline(pr, pr->Msg);
|
|
}
|
|
|
|
void writeruleaction(EN_Project *pr, int k, char *ruleID)
|
|
/*
|
|
**--------------------------------------------------------------
|
|
** Input: k = link index
|
|
** *ruleID = rule ID
|
|
** Output: none
|
|
** Purpose: writes rule action taken to status report
|
|
**--------------------------------------------------------------
|
|
*/
|
|
{
|
|
EN_Network *net = &pr->network;
|
|
report_options_t *rep = &pr->report;
|
|
time_options_t *time = &pr->time_options;
|
|
Slink *Link = net->Link;
|
|
|
|
sprintf(pr->Msg, FMT63, clocktime(rep->Atime, time->Htime), LinkTxt[Link[k].Type],
|
|
Link[k].ID, ruleID);
|
|
writeline(pr, pr->Msg);
|
|
}
|
|
|
|
int writehydwarn(EN_Project *pr, int iter, double relerr)
|
|
/*
|
|
**--------------------------------------------------------------
|
|
** Input: iter = # iterations to find hydraulic solution
|
|
** Output: warning flag code
|
|
** Purpose: writes hydraulic warning message to report file
|
|
**
|
|
** Note: Warning conditions checked in following order:
|
|
** 1. System balanced but unstable
|
|
** 2. Negative pressures
|
|
** 3. FCV cannot supply flow or PRV/PSV cannot maintain pressure
|
|
** 4. Pump out of range
|
|
** 5. Network disconnected
|
|
** 6. System unbalanced
|
|
**--------------------------------------------------------------
|
|
*/
|
|
{
|
|
EN_Network *net = &pr->network;
|
|
hydraulics_t *hyd = &pr->hydraulics;
|
|
report_options_t *rep = &pr->report;
|
|
time_options_t *time = &pr->time_options;
|
|
|
|
Snode *Node = net->Node;
|
|
Slink *Link = net->Link;
|
|
Spump *Pump = net->Pump;
|
|
Svalve *Valve = net->Valve;
|
|
|
|
const int Njuncs = net->Njuncs;
|
|
double *NodeDemand = hyd->NodeDemand;
|
|
double *LinkFlows = hyd->LinkFlows;
|
|
double *LinkSetting = hyd->LinkSetting;
|
|
|
|
int i, j;
|
|
char flag = 0;
|
|
char s;
|
|
|
|
/* Check if system unstable */
|
|
if (iter > hyd->MaxIter && relerr <= hyd->Hacc) {
|
|
sprintf(pr->Msg, WARN02, clocktime(rep->Atime, time->Htime));
|
|
if (rep->Messageflag)
|
|
writeline(pr, pr->Msg);
|
|
flag = 2;
|
|
}
|
|
|
|
/* Check for negative pressures */
|
|
for (i = 1; i <= Njuncs; i++) {
|
|
Snode *node = &Node[i];
|
|
if (hyd->NodeHead[i] < node->El && NodeDemand[i] > 0.0) {
|
|
sprintf(pr->Msg, WARN06, clocktime(rep->Atime, time->Htime));
|
|
if (rep->Messageflag) {
|
|
writeline(pr, pr->Msg);
|
|
}
|
|
flag = 6;
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* Check for abnormal valve condition */
|
|
for (i = 1; i <= net->Nvalves; i++) {
|
|
j = Valve[i].Link;
|
|
if (hyd->LinkStatus[j] >= XFCV) {
|
|
sprintf(pr->Msg, WARN05, LinkTxt[Link[j].Type], Link[j].ID,
|
|
StatTxt[hyd->LinkStatus[j]], clocktime(rep->Atime, time->Htime));
|
|
if (rep->Messageflag)
|
|
writeline(pr, pr->Msg);
|
|
flag = 5;
|
|
}
|
|
}
|
|
|
|
/* Check for abnormal pump condition */
|
|
for (i = 1; i <= net->Npumps; i++) {
|
|
j = Pump[i].Link;
|
|
s = hyd->LinkStatus[j];
|
|
if (hyd->LinkStatus[j] >= OPEN)
|
|
{
|
|
if (LinkFlows[j] > LinkSetting[j] * Pump[i].Qmax)
|
|
s = XFLOW;
|
|
if (LinkFlows[j] < 0.0)
|
|
s = XHEAD;
|
|
}
|
|
if (s == XHEAD || s == XFLOW)
|
|
{
|
|
sprintf(pr->Msg, WARN04, Link[j].ID, StatTxt[s],
|
|
clocktime(rep->Atime, time->Htime));
|
|
if (rep->Messageflag)
|
|
writeline(pr, pr->Msg);
|
|
flag = 4;
|
|
}
|
|
}
|
|
|
|
/* Check if system is unbalanced */
|
|
if (iter > hyd->MaxIter && relerr > hyd->Hacc) {
|
|
sprintf(pr->Msg, WARN01, clocktime(rep->Atime, time->Htime));
|
|
if (hyd->ExtraIter == -1)
|
|
strcat(pr->Msg, t_HALTED);
|
|
if (rep->Messageflag)
|
|
writeline(pr, pr->Msg);
|
|
flag = 1;
|
|
}
|
|
|
|
/* Check for disconnected network */
|
|
/* & update global warning flag */
|
|
if (flag > 0) {
|
|
disconnected(pr);
|
|
pr->Warnflag = flag;
|
|
}
|
|
return (flag);
|
|
} /* End of writehydwarn */
|
|
|
|
void writehyderr(EN_Project *pr, int errnode)
|
|
/*
|
|
**-----------------------------------------------------------
|
|
** Input: none
|
|
** Output: none
|
|
** Purpose: outputs status & checks connectivity when
|
|
** network hydraulic equations cannot be solved.
|
|
**-----------------------------------------------------------
|
|
*/
|
|
{
|
|
EN_Network *net = &pr->network;
|
|
report_options_t *rep = &pr->report;
|
|
time_options_t *time = &pr->time_options;
|
|
Snode *Node = net->Node;
|
|
|
|
sprintf(pr->Msg, FMT62, clocktime(rep->Atime, time->Htime), Node[errnode].ID);
|
|
if (rep->Messageflag)
|
|
writeline(pr, pr->Msg);
|
|
writehydstat(pr, 0, 0);
|
|
disconnected(pr);
|
|
} /* End of writehyderr */
|
|
|
|
int disconnected(EN_Project *pr)
|
|
/*
|
|
**-------------------------------------------------------------------
|
|
** Input: None
|
|
** Output: Returns number of disconnected nodes
|
|
** Purpose: Tests current hydraulic solution to see if any closed
|
|
** links have caused the network to become disconnected.
|
|
**-------------------------------------------------------------------
|
|
*/
|
|
{
|
|
EN_Network *net = &pr->network;
|
|
hydraulics_t *hyd = &pr->hydraulics;
|
|
report_options_t *rep = &pr->report;
|
|
time_options_t *time = &pr->time_options;
|
|
|
|
int i, j;
|
|
int count, mcount;
|
|
int errcode = 0;
|
|
int *nodelist;
|
|
char *marked;
|
|
|
|
/* Allocate memory for node list & marked list */
|
|
nodelist = (int *)calloc(net->Nnodes + 1, sizeof(int));
|
|
marked = (char *)calloc(net->Nnodes + 1, sizeof(char));
|
|
ERRCODE(MEMCHECK(nodelist));
|
|
ERRCODE(MEMCHECK(marked));
|
|
if (errcode)
|
|
return (0);
|
|
|
|
/* Place tanks on node list and marked list */
|
|
for (i = 1; i <= net->Ntanks; i++) {
|
|
j = net->Njuncs + i;
|
|
nodelist[i] = j;
|
|
marked[j] = 1;
|
|
}
|
|
|
|
/* Place junctions with negative demands on the lists */
|
|
mcount = net->Ntanks;
|
|
for (i = 1; i <= net->Njuncs; i++) {
|
|
if (hyd->NodeDemand[i] < 0.0) {
|
|
mcount++;
|
|
nodelist[mcount] = i;
|
|
marked[i] = 1;
|
|
}
|
|
}
|
|
|
|
/* Mark all nodes that can be connected to tanks */
|
|
/* and count number of nodes remaining unmarked. */
|
|
marknodes(pr, mcount, nodelist, marked);
|
|
j = 0;
|
|
count = 0;
|
|
for (i = 1; i <= net->Njuncs; i++) {
|
|
Snode *node = &net->Node[i];
|
|
if (!marked[i] && hyd->NodeDemand[i] != 0.0) {
|
|
count++;
|
|
if (count <= MAXCOUNT && rep->Messageflag) {
|
|
sprintf(pr->Msg, WARN03a, node->ID, clocktime(rep->Atime, time->Htime));
|
|
writeline(pr, pr->Msg);
|
|
}
|
|
j = i; /* Last unmarked node */
|
|
}
|
|
}
|
|
|
|
/* Report number of unmarked nodes and find closed link */
|
|
/* on path from node j back to a tank. */
|
|
if (count > 0 && rep->Messageflag) {
|
|
if (count > MAXCOUNT) {
|
|
sprintf(pr->Msg, WARN03b, count - MAXCOUNT, clocktime(rep->Atime, time->Htime));
|
|
writeline(pr, pr->Msg);
|
|
}
|
|
getclosedlink(pr, j, marked);
|
|
}
|
|
|
|
/* Free allocated memory */
|
|
free(nodelist);
|
|
free(marked);
|
|
return (count);
|
|
} /* End of disconnected() */
|
|
|
|
void marknodes(EN_Project *pr, int m, int *nodelist, char *marked)
|
|
/*
|
|
**----------------------------------------------------------------
|
|
** Input: m = number of source nodes
|
|
** nodelist[] = list of nodes to be traced from
|
|
** marked[] = TRUE if node connected to source
|
|
** Output: None.
|
|
** Purpose: Marks all junction nodes connected to tanks.
|
|
**----------------------------------------------------------------
|
|
*/
|
|
{
|
|
EN_Network *net = &pr->network;
|
|
hydraulics_t *hyd = &pr->hydraulics;
|
|
|
|
int i, j, k, n;
|
|
Padjlist alink;
|
|
|
|
/* Scan each successive entry of node list */
|
|
n = 1;
|
|
while (n <= m) {
|
|
|
|
/* Scan all nodes connected to current node */
|
|
i = nodelist[n];
|
|
for (alink = net->Adjlist[i]; alink != NULL; alink = alink->next) {
|
|
|
|
/* Get indexes of connecting link and node */
|
|
k = alink->link;
|
|
j = alink->node;
|
|
if (marked[j]) {
|
|
continue;
|
|
}
|
|
|
|
/* Check if valve connection is in correct direction */
|
|
switch (net->Link[k].Type) {
|
|
case EN_CVPIPE:
|
|
case EN_PRV:
|
|
case EN_PSV:
|
|
if (j == net->Link[k].N1) {
|
|
continue;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
|
|
/* Mark connection node if link not closed */
|
|
if (hyd->LinkStatus[k] > CLOSED) {
|
|
marked[j] = 1;
|
|
m++;
|
|
nodelist[m] = j;
|
|
}
|
|
}
|
|
n++;
|
|
}
|
|
} /* End of marknodes() */
|
|
|
|
void getclosedlink(EN_Project *pr, int i, char *marked)
|
|
/*
|
|
**----------------------------------------------------------------
|
|
** Input: i = junction index
|
|
** marked[] = marks nodes already examined
|
|
** Output: None.
|
|
** Purpose: Determines if a closed link connects to junction i.
|
|
**----------------------------------------------------------------
|
|
*/
|
|
{
|
|
EN_Network *net = &pr->network;
|
|
|
|
int j, k;
|
|
Padjlist alink;
|
|
marked[i] = 2;
|
|
for (alink = net->Adjlist[i]; alink != NULL; alink = alink->next) {
|
|
k = alink->link;
|
|
j = alink->node;
|
|
if (marked[j] == 2)
|
|
continue;
|
|
if (marked[j] == 1) {
|
|
sprintf(pr->Msg, WARN03c, net->Link[k].ID);
|
|
writeline(pr, pr->Msg);
|
|
return;
|
|
} else
|
|
getclosedlink(pr, j, marked);
|
|
}
|
|
}
|
|
|
|
void writelimits(EN_Project *pr, int j1, int j2)
|
|
/*
|
|
**--------------------------------------------------------------
|
|
** Input: j1 = index of first output variable
|
|
** j2 = index of last output variable
|
|
** Output: none
|
|
** Purpose: writes reporting criteria to output report
|
|
**--------------------------------------------------------------
|
|
*/
|
|
{
|
|
report_options_t *rep = &pr->report;
|
|
|
|
int j;
|
|
for (j = j1; j <= j2; j++) {
|
|
if (rep->Field[j].RptLim[LOW] < BIG) {
|
|
sprintf(pr->Msg, FMT47, rep->Field[j].Name, rep->Field[j].RptLim[LOW],
|
|
rep->Field[j].Units);
|
|
writeline(pr, pr->Msg);
|
|
}
|
|
if (rep->Field[j].RptLim[HI] > -BIG) {
|
|
sprintf(pr->Msg, FMT48, rep->Field[j].Name, rep->Field[j].RptLim[HI],
|
|
rep->Field[j].Units);
|
|
writeline(pr, pr->Msg);
|
|
}
|
|
}
|
|
} /* End of writelimits */
|
|
|
|
int checklimits(report_options_t *rep, double *y, int j1, int j2)
|
|
/*
|
|
**--------------------------------------------------------------
|
|
** Input: *y = array of output results
|
|
** j1 = index of first output variable
|
|
** j2 = index of last output variable
|
|
** Output: returns 1 if criteria met, 0 otherwise
|
|
** Purpose: checks if output reporting criteria is met
|
|
**--------------------------------------------------------------
|
|
*/
|
|
{
|
|
int j;
|
|
for (j = j1; j <= j2; j++) {
|
|
if (y[j] > rep->Field[j].RptLim[LOW] || y[j] < rep->Field[j].RptLim[HI])
|
|
return (0);
|
|
}
|
|
return (1);
|
|
} /* End of checklim */
|
|
|
|
void writetime(EN_Project *pr, char *fmt)
|
|
/*
|
|
**----------------------------------------------------------------
|
|
** Input: fmt = format string
|
|
** Output: none
|
|
** Purpose: writes starting/ending time of a run to report file
|
|
**----------------------------------------------------------------
|
|
*/
|
|
{
|
|
time_t timer;
|
|
time(&timer);
|
|
sprintf(pr->Msg, fmt, ctime(&timer));
|
|
writeline(pr, pr->Msg);
|
|
}
|
|
|
|
char *clocktime(char *atime, long seconds)
|
|
/*
|
|
**--------------------------------------------------------------
|
|
** Input: seconds = time in seconds
|
|
** Output: atime = time in hrs:min
|
|
** (returns pointer to atime)
|
|
** Purpose: converts time in seconds to hours:minutes format
|
|
**--------------------------------------------------------------
|
|
*/
|
|
{
|
|
/*** Updated 6/24/02 ***/
|
|
long h, m, s;
|
|
h = seconds / 3600;
|
|
m = seconds % 3600 / 60;
|
|
s = seconds - 3600 * h - 60 * m;
|
|
sprintf(atime, "%01d:%02d:%02d", (int)h, (int)m, (int)s);
|
|
return (atime);
|
|
} /* End of clocktime */
|
|
|
|
char *fillstr(char *s, char ch, int n)
|
|
/*
|
|
**---------------------------------------------------------
|
|
** Fills n bytes of s to character ch.
|
|
** NOTE: does not check for overwriting s.
|
|
**---------------------------------------------------------
|
|
*/
|
|
{
|
|
int i;
|
|
for (i = 0; i <= n; i++)
|
|
s[i] = ch;
|
|
s[n + 1] = '\0';
|
|
return (s);
|
|
}
|
|
|
|
int getnodetype(EN_Network *net, int i)
|
|
/*
|
|
**---------------------------------------------------------
|
|
** Determines type of node with index i
|
|
** (junction = 0, reservoir = 1, tank = 2).
|
|
**---------------------------------------------------------
|
|
*/
|
|
{
|
|
if (i <= net->Njuncs)
|
|
return (0);
|
|
if (net->Tank[i - net->Njuncs].A == 0.0)
|
|
return (1);
|
|
return (2);
|
|
}
|
|
|
|
/********************* END OF REPORT.C ********************/
|