Files
EPANET/src/output.c
2013-07-22 16:56:05 -04:00

706 lines
24 KiB
C
Executable File

/*
*********************************************************************
OUTPUT.C -- Binary File Transfer Routines for EPANET Program
VERSION: 2.00
DATE: 5/8/00
8/15/07 (2.00.11)
AUTHOR: L. Rossman
US EPA - NRMRL
********************************************************************
*/
#include <stdio.h>
#include <string.h>
#ifndef __APPLE__
#include <malloc.h>
#else
#include <stdlib.h>
#endif
#include <math.h>
#include "text.h"
#include "types.h"
#include "funcs.h"
#define EXTERN extern
#include "hash.h"
#include "vars.h"
/* Macro to write x[1] to x[n] to file OutFile: */
#define FSAVE(n) (fwrite(x+1,sizeof(REAL4),(n),OutFile))
int savenetdata()
/*
**---------------------------------------------------------------
** Input: none
** Output: returns error code
** Purpose: saves input data in original units to binary
** output file using fixed-sized (4-byte) records
**---------------------------------------------------------------
*/
{
int i,nmax;
INT4 *ibuf;
REAL4 *x;
int errcode = 0;
/* Allocate buffer arrays */
nmax = MAX(Nnodes,Nlinks) + 1;
nmax = MAX(nmax,15);
ibuf = (INT4 *) calloc(nmax, sizeof(INT4));
x = (REAL4 *) calloc(nmax, sizeof(REAL4));
ERRCODE(MEMCHECK(ibuf));
ERRCODE(MEMCHECK(x));
if (!errcode)
{
/* Write integer variables to OutFile */
ibuf[0] = MAGICNUMBER;
/*** CODEVERSION replaces VERSION ***/ //(2.00.11 - LR)
ibuf[1] = CODEVERSION; //(2.00.11 - LR)
ibuf[2] = Nnodes;
ibuf[3] = Ntanks;
ibuf[4] = Nlinks;
ibuf[5] = Npumps;
ibuf[6] = Nvalves;
ibuf[7] = Qualflag;
ibuf[8] = TraceNode;
ibuf[9] = Flowflag;
ibuf[10] = Pressflag;
ibuf[11] = Tstatflag;
ibuf[12] = Rstart;
ibuf[13] = Rstep;
ibuf[14] = Dur;
fwrite(ibuf,sizeof(INT4),15,OutFile);
/* Write string variables to OutFile */
fwrite(Title[0],sizeof(char),MAXMSG+1,OutFile);
fwrite(Title[1],sizeof(char),MAXMSG+1,OutFile);
fwrite(Title[2],sizeof(char),MAXMSG+1,OutFile);
fwrite(InpFname,sizeof(char),MAXFNAME+1,OutFile);
fwrite(Rpt2Fname,sizeof(char),MAXFNAME+1,OutFile);
fwrite(ChemName,sizeof(char),MAXID+1,OutFile);
fwrite(Field[QUALITY].Units,sizeof(char),MAXID+1,OutFile);
/* Write node ID information to OutFile */
for (i=1; i<=Nnodes; i++)
fwrite(Node[i].ID, MAXID+1, 1, OutFile);
/* Write link information to OutFile */
/* (Note: first transfer values to buffer array,*/
/* then fwrite buffer array at offset of 1 ) */
for (i=1; i<=Nlinks; i++)
fwrite(Link[i].ID, MAXID+1, 1, OutFile);
for (i=1; i<=Nlinks; i++) ibuf[i] = Link[i].N1;
fwrite(ibuf+1,sizeof(INT4),Nlinks,OutFile);
for (i=1; i<=Nlinks; i++) ibuf[i] = Link[i].N2;
fwrite(ibuf+1,sizeof(INT4),Nlinks,OutFile);
for (i=1; i<=Nlinks; i++) ibuf[i] = Link[i].Type;
fwrite(ibuf+1,sizeof(INT4),Nlinks,OutFile);
/* Write tank information to OutFile.*/
for (i=1; i<=Ntanks; i++) ibuf[i] = Tank[i].Node;
fwrite(ibuf+1,sizeof(INT4),Ntanks,OutFile);
for (i=1; i<=Ntanks; i++) x[i] = (REAL4)Tank[i].A;
FSAVE(Ntanks);
/* Save node elevations to OutFile.*/
for (i=1; i<=Nnodes; i++) x[i] = (REAL4)(Node[i].El*Ucf[ELEV]);
FSAVE(Nnodes);
/* Save link lengths & diameters to OutFile.*/
for (i=1; i<=Nlinks; i++) x[i] = (REAL4)(Link[i].Len*Ucf[ELEV]);
FSAVE(Nlinks);
for (i=1; i<=Nlinks; i++)
{
if (Link[i].Type != PUMP)
x[i] = (REAL4)(Link[i].Diam*Ucf[DIAM]);
else
x[i] = 0.0f;
}
if (FSAVE(Nlinks) < (unsigned)Nlinks) errcode = 308;
}
/* Free memory used for buffer arrays */
free(ibuf);
free(x);
return(errcode);
}
int savehyd(long *htime)
/*
**--------------------------------------------------------------
** Input: *htime = current time
** Output: returns error code
** Purpose: saves current hydraulic solution to file HydFile
** in binary format
**--------------------------------------------------------------
*/
{
int i;
INT4 t;
int errcode = 0;
REAL4 *x = (REAL4 *) calloc(MAX(Nnodes,Nlinks) + 1, sizeof(REAL4));
if ( x == NULL ) return 101;
/* Save current time (htime) */
t = *htime;
fwrite(&t,sizeof(INT4),1,HydFile);
/* Save current nodal demands (D) */
for (i=1; i<=Nnodes; i++) x[i] = (REAL4)D[i];
fwrite(x+1,sizeof(REAL4),Nnodes,HydFile);
/* Copy heads (H) to buffer of floats (x) and save buffer */
for (i=1; i<=Nnodes; i++) x[i] = (REAL4)H[i];
fwrite(x+1,sizeof(REAL4),Nnodes,HydFile);
/* Force flow in closed links to be zero then save flows */
for (i=1; i<=Nlinks; i++)
{
if (S[i] <= CLOSED) x[i] = 0.0f;
else x[i] = (REAL4)Q[i];
}
fwrite(x+1,sizeof(REAL4),Nlinks,HydFile);
/* Copy link status to buffer of floats (x) & write buffer */
for (i=1; i<=Nlinks; i++) x[i] = (REAL4)S[i];
fwrite(x+1,sizeof(REAL4),Nlinks,HydFile);
/* Save link settings & check for successful write-to-disk */
/* (We assume that if any of the previous fwrites failed, */
/* then this one will also fail.) */
for (i=1; i<=Nlinks; i++) x[i] = (REAL4)K[i];
if (fwrite(x+1,sizeof(REAL4),Nlinks,HydFile) < (unsigned)Nlinks)
errcode = 308;
free(x);
fflush(HydFile); /* added TNT */
return(errcode);
} /* End of savehyd */
int savehydstep(long *hydstep)
/*
**--------------------------------------------------------------
** Input: *hydstep = next time step
** Output: returns error code
** Purpose: saves next hydraulic timestep to file HydFile
** in binary format
**--------------------------------------------------------------
*/
{
INT4 t;
int errcode = 0;
t = *hydstep;
if (fwrite(&t,sizeof(INT4),1,HydFile) < 1) errcode = 308;
if (t == 0) fputc(EOFMARK, HydFile);
fflush(HydFile); /* added TNT */
return(errcode);
}
int saveenergy()
/*
**--------------------------------------------------------------
** Input: none
** Output: returns error code
** Purpose: saves energy usage by each pump to OutFile
** in binary format
**--------------------------------------------------------------
*/
{
int i,j;
INT4 index;
REAL4 x[6]; /* work array */
double hdur, /* total duration in hours */
t; /* pumping duration */
hdur = Dur / 3600.0;
for (i=1; i<=Npumps; i++)
{
if (hdur == 0.0)
{
for (j=0; j<5; j++) x[j] = (REAL4)Pump[i].Energy[j];
x[5] = (REAL4)(Pump[i].Energy[5]*24.0);
}
else
{
t = Pump[i].Energy[0];
x[0] = (REAL4)(t/hdur);
x[1] = 0.0f;
x[2] = 0.0f;
x[3] = 0.0f;
x[4] = 0.0f;
if (t > 0.0)
{
x[1] = (REAL4)(Pump[i].Energy[1]/t);
x[2] = (REAL4)(Pump[i].Energy[2]/t);
x[3] = (REAL4)(Pump[i].Energy[3]/t);
}
x[4] = (REAL4)Pump[i].Energy[4];
x[5] = (REAL4)(Pump[i].Energy[5]*24.0/hdur);
}
x[0] *= 100.0f;
x[1] *= 100.0f;
/* Compute Kw-hr per MilGal (or per cubic meter) */
if (Unitsflag == SI) x[2] *= (REAL4)(1000.0/LPSperCFS/3600.0);
else x[2] *= (REAL4)(1.0e6/GPMperCFS/60.0);
for (j=0; j<6; j++) Pump[i].Energy[j] = x[j];
index = Pump[i].Link;
if (fwrite(&index,sizeof(INT4),1,OutFile) < 1) return(308);
if (fwrite(x, sizeof(REAL4), 6, OutFile) < 6) return(308);
}
Emax = Emax*Dcost;
x[0] = (REAL4)Emax;
if (fwrite(&x[0], sizeof(REAL4), 1, OutFile) < 1) return(308);
return(0);
}
int readhyd(long *hydtime)
/*
**--------------------------------------------------------------
** Input: none
** Output: *hydtime = time of hydraulic solution
** Returns: 1 if successful, 0 if not
** Purpose: reads hydraulic solution from file HydFile
**
** NOTE: A hydraulic solution consists of the current time
** (hydtime), nodal demands (D) and heads (H), link
** flows (Q), link status (S), and link settings (K).
**--------------------------------------------------------------
*/
{
int i;
INT4 t;
int result = 1;
REAL4 *x = (REAL4 *) calloc(MAX(Nnodes,Nlinks) + 1, sizeof(REAL4));
if ( x == NULL ) return 0;
if (fread(&t,sizeof(INT4),1,HydFile) < 1) result = 0;
*hydtime = t;
if (fread(x+1,sizeof(REAL4),Nnodes,HydFile) < (unsigned)Nnodes) result = 0;
else for (i=1; i<=Nnodes; i++) D[i] = x[i];
if (fread(x+1,sizeof(REAL4),Nnodes,HydFile) < (unsigned)Nnodes) result = 0;
else for (i=1; i<=Nnodes; i++) H[i] = x[i];
if (fread(x+1,sizeof(REAL4),Nlinks,HydFile) < (unsigned)Nlinks) result = 0;
else for (i=1; i<=Nlinks; i++) Q[i] = x[i];
if (fread(x+1,sizeof(REAL4),Nlinks,HydFile) < (unsigned)Nlinks) result = 0;
else for (i=1; i<=Nlinks; i++) S[i] = (char) x[i];
if (fread(x+1,sizeof(REAL4),Nlinks,HydFile) < (unsigned)Nlinks) result = 0;
else for (i=1; i<=Nlinks; i++) K[i] = x[i];
free(x);
return result;
} /* End of readhyd */
int readhydstep(long *hydstep)
/*
**--------------------------------------------------------------
** Input: none
** Output: *hydstep = next hydraulic time step (sec)
** Returns: 1 if successful, 0 if not
** Purpose: reads hydraulic time step from file HydFile
**--------------------------------------------------------------
*/
{
INT4 t;
if (fread(&t,sizeof(INT4),1,HydFile) < 1) return(0);
*hydstep = t;
return(1);
} /* End of readhydstep */
int saveoutput()
/*
**--------------------------------------------------------------
** Input: none
** Output: returns error code
** Purpose: writes simulation results to output file
**--------------------------------------------------------------
*/
{
int j;
int errcode = 0;
REAL4 *x = (REAL4 *) calloc(MAX(Nnodes,Nlinks) + 1, sizeof(REAL4));
if ( x == NULL ) return 101;
/* Write out node results, then link results */
for (j=DEMAND; j<=QUALITY; j++) ERRCODE(nodeoutput(j,x,Ucf[j]));
for (j=FLOW; j<=FRICTION; j++) ERRCODE(linkoutput(j,x,Ucf[j]));
free(x);
return(errcode);
} /* End of saveoutput */
int nodeoutput(int j, REAL4 *x, double ucf)
/*
**--------------------------------------------------------------
** Input: j = type of node variable
** *x = buffer for node values
** ucf = units conversion factor
** Output: returns error code
** Purpose: writes results for node variable j to output file
**-----------------------------------------------------------------
*/
{
int i;
/* Load computed results (in proper units) into buffer x */
switch(j)
{
case DEMAND: for (i=1; i<=Nnodes; i++)
x[i] = (REAL4)(D[i]*ucf);
break;
case HEAD: for (i=1; i<=Nnodes; i++)
x[i] = (REAL4)(H[i]*ucf);
break;
case PRESSURE: for (i=1; i<=Nnodes; i++)
x[i] = (REAL4)((H[i] - Node[i].El)*ucf);
break;
case QUALITY: for (i=1; i<=Nnodes; i++)
x[i] = (REAL4)(C[i]*ucf);
}
/* Write x[1] to x[Nnodes] to output file */
if (fwrite(x+1,sizeof(REAL4),Nnodes,TmpOutFile) < (unsigned)Nnodes)
return(308);
return(0);
} /* End of nodeoutput */
int linkoutput(int j, REAL4 *x, double ucf)
/*
**----------------------------------------------------------------
** Input: j = type of link variable
** *x = buffer for link values
** ucf = units conversion factor
** Output: returns error code
** Purpose: writes results for link variable j to output file
**----------------------------------------------------------------
*/
{
int i;
double a,h,q,f;
/* Load computed results (in proper units) into buffer x */
switch(j)
{
case FLOW: for (i=1; i<=Nlinks; i++)
x[i] = (REAL4)(Q[i]*ucf);
break;
case VELOCITY: for (i=1; i<=Nlinks; i++)
{
if (Link[i].Type == PUMP) x[i] = 0.0f;
else
{
q = ABS(Q[i]);
a = PI*SQR(Link[i].Diam)/4.0;
x[i] = (REAL4)(q/a*ucf);
}
}
break;
case HEADLOSS: for (i=1; i<=Nlinks; i++)
{
if (S[i] <= CLOSED) x[i] = 0.0f;
else
{
h = H[Link[i].N1] - H[Link[i].N2];
if (Link[i].Type != PUMP) h = ABS(h);
if (Link[i].Type <= PIPE)
x[i] = (REAL4)(1000.0*h/Link[i].Len);
else x[i] = (REAL4)(h*ucf);
}
}
break;
case LINKQUAL: for (i=1; i<=Nlinks; i++)
x[i] = (REAL4)(avgqual(i)*ucf);
break;
case STATUS: for (i=1; i<=Nlinks; i++)
x[i] = (REAL4)S[i];
break;
case SETTING: for (i=1; i<=Nlinks; i++)
{
if (K[i] != MISSING)
switch (Link[i].Type)
{
case CV:
case PIPE: x[i] = (REAL4)K[i];
break;
case PUMP: x[i] = (REAL4)K[i];
break;
case PRV:
case PSV:
case PBV: x[i] = (REAL4)(K[i]*Ucf[PRESSURE]);
break;
case FCV: x[i] = (REAL4)(K[i]*Ucf[FLOW]);
break;
case TCV: x[i] = (REAL4)K[i];
break;
default: x[i] = 0.0f;
}
else x[i] = 0.0f;
}
break;
case REACTRATE: /* Overall reaction rate in mass/L/day */
if (Qualflag == NONE) memset(x,0,(Nlinks+1 )*sizeof(REAL4));
else for (i=1; i<=Nlinks; i++) x[i] = (REAL4)(R[i]*ucf);
break;
case FRICTION: /* f = 2ghd/(Lu^2) where f = friction factor */
/* u = velocity, g = grav. accel., h = head */
/*loss, d = diam., & L = pipe length */
for (i=1; i<=Nlinks; i++)
{
if (Link[i].Type <= PIPE && ABS(Q[i]) > TINY)
{
h = ABS(H[Link[i].N1] - H[Link[i].N2]);
f = 39.725*h*pow(Link[i].Diam,5)/Link[i].Len/SQR(Q[i]);
x[i] = (REAL4)f;
}
else x[i] = 0.0f;
}
break;
}
/* Write x[1] to x[Nlinks] to output file */
if (fwrite(x+1,sizeof(REAL4),Nlinks,TmpOutFile) < (unsigned)Nlinks)
return(308);
return(0);
} /* End of linkoutput */
int savefinaloutput()
/*
**--------------------------------------------------------------
** Input: none
** Output: returns error code
** Purpose: saves time series statistics, reaction rates &
** epilog to output file.
**--------------------------------------------------------------
*/
{
int errcode = 0;
REAL4 *x;
/* Save time series statistic if computed */
if (Tstatflag != SERIES && TmpOutFile != NULL)
{
x = (REAL4 *) calloc(MAX(Nnodes,Nlinks) + 1, sizeof(REAL4));
if ( x == NULL ) return 101;
ERRCODE(savetimestat(x,NODEHDR));
ERRCODE(savetimestat(x,LINKHDR));
if (!errcode) Nperiods = 1;
fclose(TmpOutFile);
free(x);
}
/* Save avg. reaction rates & file epilog */
if (OutFile != NULL)
{
ERRCODE(savenetreacts(Wbulk,Wwall,Wtank,Wsource));
ERRCODE(saveepilog());
}
return(errcode);
}
int savetimestat(REAL4 *x, char objtype)
/*
**--------------------------------------------------------------
** Input: *x = buffer for node values
** objtype = NODEHDR (for nodes) or LINKHDR (for links)
** Output: returns error code
** Purpose: computes time series statistic for nodes or links
** and saves to normal output file.
**
** NOTE: This routine is dependent on how the output reporting
** variables were assigned to FieldType in TYPES.H.
**--------------------------------------------------------------
*/
{
int n, n1, n2;
int i, j, p, errcode = 0;
long startbyte, skipbytes;
float *stat1, *stat2, xx;
/*
Compute number of bytes in temp output file to skip over (skipbytes)
when moving from one time period to the next for a particular variable.
*/
if (objtype == NODEHDR)
{
/*
For nodes, we start at 0 and skip over node output for all
node variables minus 1 plus link output for all link variables.
*/
startbyte = 0;
skipbytes = (Nnodes*(QUALITY-DEMAND) +
Nlinks*(FRICTION-FLOW+1))*sizeof(REAL4);
n = Nnodes;
n1 = DEMAND;
n2 = QUALITY;
}
else
{
/*
For links, we start at the end of all node variables and skip
over node output for all node variables plus link output for
all link variables minus 1.
*/
startbyte = Nnodes*(QUALITY-DEMAND+1)*sizeof(REAL4);
skipbytes = (Nnodes*(QUALITY-DEMAND+1) +
Nlinks*(FRICTION-FLOW))*sizeof(REAL4);
n = Nlinks;
n1 = FLOW;
n2 = FRICTION;
}
stat1 = (float *) calloc(n+1, sizeof(float));
stat2 = (float *) calloc(n+1, sizeof(float));
ERRCODE(MEMCHECK(stat1));
ERRCODE(MEMCHECK(stat2));
/* Process each output reporting variable */
if (!errcode)
{
for (j=n1; j<=n2; j++)
{
/* Initialize stat arrays */
if (Tstatflag == AVG) memset(stat1, 0, (n+1)*sizeof(float));
else for (i=1; i<=n; i++)
{
stat1[i] = -MISSING; /* +1E10 */
stat2[i] = MISSING; /* -1E10 */
}
/* Position temp output file at start of output */
fseek(TmpOutFile, startbyte + (j-n1)*n*sizeof(REAL4), SEEK_SET);
/* Process each time period */
for (p=1; p<=Nperiods; p++)
{
/* Get output results for time period & update stats */
fread(x+1, sizeof(REAL4), n, TmpOutFile);
for (i=1; i<=n; i++)
{
xx = x[i];
if (objtype == LINKHDR)
{
if (j == FLOW) xx = ABS(xx);
if (j == STATUS)
{
if (xx >= OPEN) xx = 1.0;
else xx = 0.0;
}
}
if (Tstatflag == AVG) stat1[i] += xx;
else
{
stat1[i] = MIN(stat1[i], xx);
stat2[i] = MAX(stat2[i], xx);
}
}
/* Advance file to next period */
if (p < Nperiods) fseek(TmpOutFile, skipbytes, SEEK_CUR);
}
/* Compute resultant stat & save to regular output file */
switch (Tstatflag)
{
case AVG: for (i=1; i<=n; i++) x[i] = stat1[i]/(float)Nperiods;
break;
case MIN: for (i=1; i<=n; i++) x[i] = stat1[i];
break;
case MAX: for (i=1; i<=n; i++) x[i] = stat2[i];
break;
case RANGE: for (i=1; i<=n; i++) x[i] = stat2[i] - stat1[i];
break;
}
if (objtype == LINKHDR && j == STATUS)
{
for (i=1; i<=n; i++)
{
if (x[i] < 0.5f) x[i] = CLOSED;
else x[i] = OPEN;
}
}
if (fwrite(x+1, sizeof(REAL4), n, OutFile) < (unsigned) n) errcode = 308;
/* Update internal output variables where applicable */
if (objtype == NODEHDR) switch (j)
{
case DEMAND: for (i=1; i<=n; i++) D[i] = x[i]/Ucf[DEMAND];
break;
case HEAD: for (i=1; i<=n; i++) H[i] = x[i]/Ucf[HEAD];
break;
case QUALITY: for (i=1; i<=n; i++) C[i] = x[i]/Ucf[QUALITY];
break;
}
else if (j == FLOW) for (i=1; i<=n; i++) Q[i] = x[i]/Ucf[FLOW];
}
}
/* Free allocated memory */
free(stat1);
free(stat2);
return(errcode);
}
int savenetreacts(double wbulk, double wwall, double wtank, double wsource)
/*
**-----------------------------------------------------
** Writes average network-wide reaction rates (in
** mass/hr) to binary output file.
**-----------------------------------------------------
*/
{
int errcode = 0;
double t;
REAL4 w[4];
if (Dur > 0) t = (double)Dur/3600.;
else t = 1.;
w[0] = (REAL4)(wbulk/t);
w[1] = (REAL4)(wwall/t);
w[2] = (REAL4)(wtank/t);
w[3] = (REAL4)(wsource/t);
if (fwrite(w,sizeof(REAL4),4,OutFile) < 4) errcode = 308;
return(errcode);
}
int saveepilog()
/*
**-------------------------------------------------
** Writes Nperiods, Warnflag, & Magic Number to
** end of binary output file.
**-------------------------------------------------
*/
{
int errcode = 0;
INT4 i;
i = Nperiods;
if (fwrite(&i,sizeof(INT4),1,OutFile) < 1) errcode = 308;
i = Warnflag;
if (fwrite(&i,sizeof(INT4),1,OutFile) < 1) errcode = 308;
i = MAGICNUMBER;
if (fwrite(&i,sizeof(INT4),1,OutFile) < 1) errcode = 308;
return(errcode);
}
/********************** END OF OUTPUT.C **********************/