Files
EPANET/src/epanet.c

5145 lines
128 KiB
C

/*
*******************************************************************************
EPANET.C -- Hydraulic & Water Quality Simulator for Water Distribution Networks
VERSION: 2.1
AUTHORS: L. Rossman - US EPA - NRMRL
OpenWaterAnalytics members: see git stats for contributors
EPANET performs extended period hydraulic and water quality analysis of
looped, pressurized piping networks. The program consists of the
following code modules:
EPANET.C -- main module providing supervisory control
INPUT1.C -- controls processing of input data
INPUT2.C -- reads data from input file
INPUT3.C -- parses individual lines of input data
INPFILE.C -- saves modified input data to a text file
RULES.C -- implements rule-based control of piping system
HYDRAUL.C -- computes extended period hydraulic behavior
QUALITY.C -- tracks transport & fate of water quality
OUTPUT.C -- handles transfer of data to and from binary files
REPORT.C -- handles reporting of results to text file
SMATRIX.C -- sparse matrix linear equation solver routines
MEMPOOL.C -- memory allocation routines
HASH.C -- hash table routines
The program can be compiled as either a stand-alone console application
or as a dynamic link library (DLL) of function calls depending on whether
the macro identifier 'DLL' is defined or not.
See EPANET2.H for function prototypes of exported DLL functions
See FUNCS.H for prototypes of all other functions
See TYPES.H for declaration of global constants and data structures
See VARS.H for declaration of global variables
See TEXT.H for declaration of all string constants
See ENUMSTXT.H for assignment of string constants to enumerated types
The following naming conventions are used in all modules of this program:
1. Names of exportable functions in the DLL begin with the "EN" prefix.
2. All other function names are lowercase.
3. Global variable names begin with an uppercase letter.
4. Local variable names are all lowercase.
5. Declared constants and enumerated values defined in TYPES.H are
all uppercase.
6. String constants defined in TEXT.H begin with a lower case character
followed by an underscore and then all uppercase characters (e.g.
t_HEADLOSS)
--------------------------------------------------------------------------
This is the main module of the EPANET program. It uses a series of
functions, all beginning with the letters EN, to control program behavior.
See the main() and ENepanet() functions below for the simplest example of
these.
This module calls the following functions that reside in other modules:
RULES.C
initrules()
allocrules()
closerules()
INPUT1.C
getdata()
initreport()
INPUT2.C
netsize()
setreport()
HYDRAUL.C
openhyd()
inithyd()
runhyd()
nexthyd()
closehyd()
tankvolume()
getenergy()
setlinkstatus()
setlinksetting()
HYDCOEFFS
resistcoeff()
QUALITY.C
openqual()
initqual()
runqual()
nextqual()
stepqual()
closequal()
REPORT.C
writeline(pr, )
writelogo()
writereport()
HASH.C
ENHashTablecreate()
ENHashTableFind()
ENHashTableFree()
The macro ERRCODE(x) is defined in TYPES.H. It says if the current
value of the error code variable (errcode) is not fatal (< 100) then
execute function x and set the error code equal to its return value.
*******************************************************************************
*/
/*** Need to define WINDOWS to use the getTmpName function ***/
// --- define WINDOWS
#undef WINDOWS
#ifdef _WIN32
#define WINDOWS
#endif
#ifdef __WIN32__
#define WINDOWS
#endif
#ifdef WINDOWS
#include <windows.h>
#endif
/************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#ifndef __APPLE__
#include <malloc.h>
#endif
#include <float.h>
#include <math.h>
#include "enumstxt.h"
#include "epanet2.h"
#include "funcs.h"
#include "text.h"
#include "types.h"
#define EXTERN
////////////////////////////////////////////#include "epanet2.h"
#include "vars.h"
/****************************************************************
LEGACY (v <= 2.1) API: uses global project variable
*****************************************************************/
/*------------------------------------------------------------------------
** Input: f1 = pointer to name of input file
** f2 = pointer to name of report file
** f3 = pointer to name of binary output file
** pviewprog = see note below
** Output: none
** Returns: error code
** Purpose: runs a complete EPANET simulation
**
** The pviewprog() argument is a pointer to a callback function
** that takes a character string (char *) as its only parameter.
** The function would reside in and be used by the calling
** program to display the progress messages that EPANET generates
** as it carries out its computations. If this feature is not
** needed then the argument should be NULL.
**-------------------------------------------------------------------------
*/
int DLLEXPORT ENepanet(char *f1, char *f2, char *f3, void (*pviewprog)(char *))
{
int errcode = 0;
EN_Project *_p;
ERRCODE(EN_alloc(&_defaultModel));
ERRCODE(EN_open(_defaultModel, f1, f2, f3));
_p = (EN_Project*)_defaultModel;
_p->viewprog = pviewprog;
if (_p->out_files.Hydflag != USE) {
ERRCODE(EN_solveH(_defaultModel));
}
ERRCODE(EN_solveQ(_defaultModel));
ERRCODE(EN_report(_defaultModel));
EN_close(_defaultModel);
EN_free(&_defaultModel);
return (errcode);
}
int DLLEXPORT ENopen(char *f1, char *f2, char *f3) {
int errcode = 0;
ERRCODE(EN_alloc(&_defaultModel));
EN_open(_defaultModel, f1, f2, f3);
return (errcode);
}
int DLLEXPORT ENsaveinpfile(char *filename) {
return EN_saveinpfile(_defaultModel, filename);
}
int DLLEXPORT ENclose() { return EN_close(_defaultModel); }
int DLLEXPORT ENsolveH() { return EN_solveH(_defaultModel); }
int DLLEXPORT ENsaveH() { return EN_saveH(_defaultModel); }
int DLLEXPORT ENopenH() { return EN_openH(_defaultModel); }
int DLLEXPORT ENinitH(int flag) { return EN_initH(_defaultModel, flag); }
int DLLEXPORT ENrunH(long *t) { return EN_runH(_defaultModel, t); }
int DLLEXPORT ENnextH(long *tstep) { return EN_nextH(_defaultModel, tstep); }
int DLLEXPORT ENcloseH() { return EN_closeH(_defaultModel); }
int DLLEXPORT ENsavehydfile(char *filename) {
return EN_savehydfile(_defaultModel, filename);
}
int DLLEXPORT ENusehydfile(char *filename) {
return EN_usehydfile(_defaultModel, filename);
}
int DLLEXPORT ENsolveQ() { return EN_solveQ(_defaultModel); }
int DLLEXPORT ENopenQ() { return EN_openQ(_defaultModel); }
int DLLEXPORT ENinitQ(int saveflag) {
return EN_initQ(_defaultModel, saveflag);
}
int DLLEXPORT ENrunQ(long *t) { return EN_runQ(_defaultModel, t); }
int DLLEXPORT ENnextQ(long *tstep) { return EN_nextQ(_defaultModel, tstep); }
int DLLEXPORT ENstepQ(long *tleft) { return EN_stepQ(_defaultModel, tleft); }
int DLLEXPORT ENcloseQ() { return EN_closeQ(_defaultModel); }
int DLLEXPORT ENwriteline(char *line) {
return EN_writeline(_defaultModel, line);
}
int DLLEXPORT ENreport() { return EN_report(_defaultModel); }
int DLLEXPORT ENresetreport() { return EN_resetreport(_defaultModel); }
int DLLEXPORT ENsetreport(char *s) { return EN_setreport(_defaultModel, s); }
int DLLEXPORT ENgetversion(int *v) { return EN_getversion(v); }
int DLLEXPORT ENgetcontrol(int cindex, int *ctype, int *lindex,
EN_API_FLOAT_TYPE *setting, int *nindex,
EN_API_FLOAT_TYPE *level) {
return EN_getcontrol(_defaultModel, cindex, ctype, lindex, setting, nindex,
level);
}
int DLLEXPORT ENgetcount(int code, int *count) {
return EN_getcount(_defaultModel, (EN_CountType)code, count);
}
int DLLEXPORT ENgetoption(int code, EN_API_FLOAT_TYPE *value) {
return EN_getoption(_defaultModel, (EN_Option)code, value);
}
int DLLEXPORT ENgettimeparam(int code, long *value) {
return EN_gettimeparam(_defaultModel, code, value);
}
int DLLEXPORT ENgetflowunits(int *code) {
return EN_getflowunits(_defaultModel, code);
}
int DLLEXPORT ENsetflowunits(int code) {
return EN_setflowunits(_defaultModel, code);
}
int DLLEXPORT ENgetpatternindex(char *id, int *index) {
return EN_getpatternindex(_defaultModel, id, index);
}
int DLLEXPORT ENgetpatternid(int index, char *id) {
return EN_getpatternid(_defaultModel, index, id);
}
int DLLEXPORT ENgetpatternlen(int index, int *len) {
return EN_getpatternlen(_defaultModel, index, len);
}
int DLLEXPORT ENgetpatternvalue(int index, int period,
EN_API_FLOAT_TYPE *value) {
return EN_getpatternvalue(_defaultModel, index, period, value);
}
int DLLEXPORT ENgetcurveindex(char *id, int *index) {
return EN_getcurveindex(_defaultModel, id, index);
}
int DLLEXPORT ENgetcurveid(int index, char *id) {
return EN_getcurveid(_defaultModel, index, id);
}
int DLLEXPORT ENgetcurvelen(int index, int *len) {
return EN_getcurvelen(_defaultModel, index, len);
}
int DLLEXPORT ENgetcurvevalue(int index, int pnt, EN_API_FLOAT_TYPE *x,
EN_API_FLOAT_TYPE *y) {
return EN_getcurvevalue(_defaultModel, index, pnt, x, y);
}
int DLLEXPORT ENgetqualtype(int *qualcode, int *tracenode) {
return EN_getqualtype(_defaultModel, qualcode, tracenode);
}
int DLLEXPORT ENgetqualinfo(int *qualcode, char *chemname, char *chemunits,
int *tracenode) {
return EN_getqualinfo(_defaultModel, qualcode, chemname, chemunits,
tracenode);
}
int DLLEXPORT ENgeterror(int errcode, char *errmsg, int n) {
return EN_geterror(errcode, errmsg, n);
}
int DLLEXPORT ENgetstatistic(int code, EN_API_FLOAT_TYPE *value) {
return EN_getstatistic(_defaultModel, code, value);
}
int DLLEXPORT ENgetnodeindex(char *id, int *index) {
return EN_getnodeindex(_defaultModel, id, index);
}
int DLLEXPORT ENgetnodeid(int index, char *id) {
return EN_getnodeid(_defaultModel, index, id);
}
int DLLEXPORT ENgetnodetype(int index, int *code) {
return EN_getnodetype(_defaultModel, index, code);
}
int DLLEXPORT ENgetcoord(int index, EN_API_FLOAT_TYPE *x,
EN_API_FLOAT_TYPE *y) {
return EN_getcoord(_defaultModel, index, x, y);
}
int DLLEXPORT ENsetcoord(int index, EN_API_FLOAT_TYPE x, EN_API_FLOAT_TYPE y) {
return EN_setcoord(_defaultModel, index, x, y);
}
int DLLEXPORT ENgetnodevalue(int index, int code, EN_API_FLOAT_TYPE *value) {
return EN_getnodevalue(_defaultModel, index, code, value);
}
int DLLEXPORT ENgetlinkindex(char *id, int *index) {
return EN_getlinkindex(_defaultModel, id, index);
}
int DLLEXPORT ENgetlinkid(int index, char *id) {
return EN_getlinkid(_defaultModel, index, id);
}
int DLLEXPORT ENgetlinktype(int index, EN_LinkType *code) {
return EN_getlinktype(_defaultModel, index, code);
}
int DLLEXPORT ENgetlinknodes(int index, int *node1, int *node2) {
return EN_getlinknodes(_defaultModel, index, node1, node2);
}
int DLLEXPORT ENgetlinkvalue(int index, int code, EN_API_FLOAT_TYPE *value) {
return EN_getlinkvalue(_defaultModel, index, (EN_LinkProperty)code, value);
}
int DLLEXPORT ENgetcurve(int curveIndex, char *id, int *nValues,
EN_API_FLOAT_TYPE **xValues,
EN_API_FLOAT_TYPE **yValues) {
return EN_getcurve(_defaultModel, curveIndex, id, nValues, xValues, yValues);
}
int DLLEXPORT ENsetcontrol(int cindex, int ctype, int lindex,
EN_API_FLOAT_TYPE setting, int nindex,
EN_API_FLOAT_TYPE level) {
return EN_setcontrol(_defaultModel, cindex, ctype, lindex, setting, nindex,
level);
}
int DLLEXPORT ENsetnodevalue(int index, int code, EN_API_FLOAT_TYPE v) {
return EN_setnodevalue(_defaultModel, index, code, v);
}
int DLLEXPORT ENsetlinkvalue(int index, int code, EN_API_FLOAT_TYPE v) {
return EN_setlinkvalue(_defaultModel, index, code, v);
}
int DLLEXPORT ENaddpattern(char *id) {
return EN_addpattern(_defaultModel, id);
}
int DLLEXPORT ENsetpattern(int index, EN_API_FLOAT_TYPE *f, int n) {
return EN_setpattern(_defaultModel, index, f, n);
}
int DLLEXPORT ENsetpatternvalue(int index, int period,
EN_API_FLOAT_TYPE value) {
return EN_setpatternvalue(_defaultModel, index, period, value);
}
int DLLEXPORT ENaddcurve(char *id) { return EN_addcurve(_defaultModel, id); }
int DLLEXPORT ENsetcurve(int index, EN_API_FLOAT_TYPE *x, EN_API_FLOAT_TYPE *y,
int n) {
return EN_setcurve(_defaultModel, index, x, y, n);
}
int DLLEXPORT ENsetcurvevalue(int index, int pnt, EN_API_FLOAT_TYPE x,
EN_API_FLOAT_TYPE y) {
return EN_setcurvevalue(_defaultModel, index, pnt, x, y);
}
int DLLEXPORT ENsettimeparam(int code, long value) {
return EN_settimeparam(_defaultModel, code, value);
}
int DLLEXPORT ENsetoption(int code, EN_API_FLOAT_TYPE v) {
return EN_setoption(_defaultModel, code, v);
}
int DLLEXPORT ENsetstatusreport(int code) {
return EN_setstatusreport(_defaultModel, code);
}
int DLLEXPORT ENsetqualtype(int qualcode, char *chemname, char *chemunits,
char *tracenode) {
return EN_setqualtype(_defaultModel, qualcode, chemname, chemunits,
tracenode);
}
int DLLEXPORT ENgetheadcurveindex(int index, int *curveindex) {
return EN_getheadcurveindex(_defaultModel, index, curveindex);
}
int DLLEXPORT ENsetheadcurveindex(int index, int curveindex) {
return EN_setheadcurveindex(_defaultModel, index, curveindex);
}
int DLLEXPORT ENgetpumptype(int index, int *type) {
return EN_getpumptype(_defaultModel, index, type);
}
int DLLEXPORT ENgetnumdemands(int nodeIndex, int *numDemands) {
return EN_getnumdemands(_defaultModel, nodeIndex, numDemands);
}
int DLLEXPORT ENgetbasedemand(int nodeIndex, int demandIdx,
EN_API_FLOAT_TYPE *baseDemand) {
return EN_getbasedemand(_defaultModel, nodeIndex, demandIdx, baseDemand);
}
int DLLEXPORT ENsetbasedemand(int nodeIndex, int demandIdx,
EN_API_FLOAT_TYPE baseDemand) {
return EN_setbasedemand(_defaultModel, nodeIndex, demandIdx, baseDemand);
}
int DLLEXPORT ENgetdemandpattern(int nodeIndex, int demandIdx, int *pattIdx) {
return EN_getdemandpattern(_defaultModel, nodeIndex, demandIdx, pattIdx);
}
int DLLEXPORT ENgetaveragepatternvalue(int index, EN_API_FLOAT_TYPE *value) {
return EN_getaveragepatternvalue(_defaultModel, index, value);
}
int DLLEXPORT ENgetrule(int index, int *nPremises, int *nTrueActions,
int *nFalseActions, EN_API_FLOAT_TYPE *priority) {
return EN_getrule(_defaultModel, index, nPremises, nTrueActions, nFalseActions, priority);
}
int DLLEXPORT ENsetrulepriority(int index, EN_API_FLOAT_TYPE priority){
return EN_setrulepriority(_defaultModel, index, priority);
}
int DLLEXPORT ENgetpremise(int indexRule, int indexPremise, int *logop, int *object, int *indexObj, int *variable, int *relop, int *status, EN_API_FLOAT_TYPE *value){
return EN_getpremise(_defaultModel, indexRule, indexPremise, logop, object, indexObj, variable, relop, status, value);
}
int DLLEXPORT ENsetpremise(int indexRule, int indexPremise, int logop, int object, int indexObj, int variable, int relop, int status, EN_API_FLOAT_TYPE value){
return EN_setpremise(_defaultModel, indexRule, indexPremise, logop, object, indexObj, variable, relop, status, value);
}
int DLLEXPORT ENsetpremiseindex(int indexRule, int indexPremise, int indexObj){
return EN_setpremiseindex(_defaultModel, indexRule, indexPremise, indexObj);
}
int DLLEXPORT ENsetpremisestatus(int indexRule, int indexPremise, int status){
return EN_setpremisestatus(_defaultModel, indexRule, indexPremise, status);
}
int DLLEXPORT ENsetpremisevalue(int indexRule, int indexPremise, EN_API_FLOAT_TYPE value){
return EN_setpremisevalue(_defaultModel, indexRule, indexPremise, value);
}
int DLLEXPORT ENgettrueaction(int indexRule, int indexAction, int *indexLink,
int *status, EN_API_FLOAT_TYPE *setting){
return EN_gettrueaction(_defaultModel, indexRule, indexAction, indexLink, status, setting);
}
int DLLEXPORT ENsettrueaction(int indexRule, int indexAction, int indexLink,
int status, EN_API_FLOAT_TYPE setting){
return EN_settrueaction(_defaultModel, indexRule, indexAction, indexLink, status, setting);
}
int DLLEXPORT ENgetfalseaction(int indexRule, int indexAction, int *indexLink,
int *status, EN_API_FLOAT_TYPE *setting){
return EN_getfalseaction(_defaultModel, indexRule, indexAction, indexLink, status, setting);
}
int DLLEXPORT ENsetfalseaction(int indexRule, int indexAction, int indexLink,
int status, EN_API_FLOAT_TYPE setting){
return EN_setfalseaction(_defaultModel, indexRule, indexAction, indexLink, status, setting);
}
int DLLEXPORT ENgetruleID(int indexRule, char* id){
return EN_getruleID(_defaultModel, indexRule, id);
}
int DLLEXPORT ENsetlinktype(char *id, EN_LinkType toType) {
return EN_setlinktype(_defaultModel, id, toType);
}
int DLLEXPORT ENaddnode(char *id, EN_NodeType nodeType) {
return EN_addnode(_defaultModel, id, nodeType);
}
int DLLEXPORT ENaddlink(char *id, EN_LinkType linkType, char *fromNode,
char *toNode) {
return EN_addlink(_defaultModel, id, linkType, fromNode, toNode);
}
int DLLEXPORT ENdeletelink(int index) {
return EN_deletelink(_defaultModel, index);
}
int DLLEXPORT ENdeletenode(int index) {
return EN_deletenode(_defaultModel, index);
}
/*
----------------------------------------------------------------
Functions for opening & closing the EPANET system
----------------------------------------------------------------
*/
/// allocate a project pointer
int DLLEXPORT EN_alloc(EN_ProjectHandle *ph)
{
EN_Project *project = calloc(1, sizeof(EN_Project));
*ph = project;
return 0;
}
int DLLEXPORT EN_free(EN_ProjectHandle *ph)
{
EN_Project *p = (EN_Project*)(*ph);
free(p);
*ph = NULL;
return 0;
}
int DLLEXPORT EN_init(EN_ProjectHandle *ph, char *f2, char *f3,
EN_FlowUnits UnitsType, EN_FormType HeadlossFormula)
/*----------------------------------------------------------------
** Input:
** f2 = pointer to name of report file
** f3 = pointer to name of binary output file
UnitsType = flow units flag
HeadlossFormula = headloss formula flag
** Output: none
** Returns: error code
** Purpose: opens EPANET
**----------------------------------------------------------------
*/
{
int errcode = 0;
/*** Updated 9/7/00 ***/
/* Reset math coprocessor */
#ifdef DLL
_fpreset();
#endif
EN_Project *pr = (EN_Project*)*ph;
/* Set system flags */
pr->Openflag = TRUE;
pr->hydraulics.OpenHflag = FALSE;
pr->quality.OpenQflag = FALSE;
pr->save_options.SaveHflag = FALSE;
pr->save_options.SaveQflag = FALSE;
pr->Warnflag = FALSE;
pr->parser.Coordflag = TRUE;
/*** Updated 9/7/00 ***/
pr->report.Messageflag = TRUE;
pr->report.Rptflag = 1;
/* Initialize global pointers to NULL. */
initpointers(pr);
ERRCODE(netsize(pr));
ERRCODE(allocdata(pr));
setdefaults(pr);
pr->parser.Flowflag = UnitsType;
pr->hydraulics.Formflag = HeadlossFormula;
adjustdata(pr);
initreport(&pr->report);
initunits(pr);
inittanks(pr);
convertunits(pr);
pr->parser.MaxPats = 0;
// initialize default pattern
getpatterns(pr);
return (errcode);
}
int DLLEXPORT EN_open(EN_ProjectHandle ph, const char *f1, const char *f2, const char *f3)
/*----------------------------------------------------------------
** Input: f1 = pointer to name of input file
** f2 = pointer to name of report file
** f3 = pointer to name of binary output file
** Output: none
** Returns: error code
** Purpose: opens EPANET input file & reads in network data
**----------------------------------------------------------------
*/
{
int errcode = 0;
/*** Updated 9/7/00 ***/
/* Reset math coprocessor */
#ifdef DLL
_fpreset();
#endif
EN_Project *p = (EN_Project*)ph;
/* Set system flags */
p->Openflag = FALSE;
p->hydraulics.OpenHflag = FALSE;
p->quality.OpenQflag = FALSE;
p->save_options.SaveHflag = FALSE;
p->save_options.SaveQflag = FALSE;
p->Warnflag = FALSE;
p->parser.Coordflag = TRUE;
/*** Updated 9/7/00 ***/
p->report.Messageflag = TRUE;
p->report.Rptflag = 1;
/* Initialize global pointers to NULL. */
initpointers(p);
/* Open input & report files */
ERRCODE(openfiles(p, f1, f2, f3));
if (errcode > 0) {
errmsg(p, errcode);
return (errcode);
}
writelogo(p);
/* Find network size & allocate memory for data */
writecon(FMT02);
writewin(p->viewprog, FMT100);
ERRCODE(netsize(p));
ERRCODE(allocdata(p));
/* Retrieve input data */
ERRCODE(getdata(p));
/* Free temporary linked lists used for Patterns & Curves */
freeTmplist(p->parser.Patlist);
freeTmplist(p->parser.Curvelist);
/* If using previously saved hydraulics then open its file */
if (p->out_files.Hydflag == USE) {
ERRCODE(openhydfile(p));
}
/* Write input summary to report file */
if (!errcode) {
if (p->report.Summaryflag) {
writesummary(p);
}
writetime(p, FMT104);
p->Openflag = TRUE;
} else
errmsg(p, errcode);
return (errcode);
}
int DLLEXPORT EN_saveinpfile(EN_ProjectHandle ph, char *filename)
/*----------------------------------------------------------------
** Input: filename = name of INP file
** Output: none
** Returns: error code
** Purpose: saves current data base to file
**----------------------------------------------------------------
*/
{
EN_Project *p = (EN_Project*)ph;
if (!p->Openflag)
return (102);
return (saveinpfile(p, filename));
}
int DLLEXPORT EN_close(EN_ProjectHandle ph)
/*----------------------------------------------------------------
** Input: none
** Output: none
** Returns: error code
** Purpose: frees all memory & files used by EPANET
**----------------------------------------------------------------
*/
{
out_file_t *out;
EN_Project *p = (EN_Project*)ph;
if (p->Openflag) {
writetime(p, FMT105);
}
freedata(p);
out = &p->out_files;
if (out->TmpOutFile != out->OutFile) {
if (out->TmpOutFile != NULL) {
fclose(out->TmpOutFile);
}
remove(out->TmpFname);
}
out->TmpOutFile = NULL;
if (p->parser.InFile != NULL) {
fclose(p->parser.InFile);
p->parser.InFile = NULL;
}
if (p->report.RptFile != NULL && p->report.RptFile != stdout) {
fclose(p->report.RptFile);
p->report.RptFile = NULL;
}
if (out->HydFile != NULL) {
fclose(out->HydFile);
out->HydFile = NULL;
}
if (out->OutFile != NULL) {
fclose(out->OutFile);
out->OutFile = NULL;
}
if (out->Hydflag == SCRATCH)
remove(out->HydFname);
if (out->Outflag == SCRATCH)
remove(out->OutFname);
p->Openflag = FALSE;
p->hydraulics.OpenHflag = FALSE;
p->save_options.SaveHflag = FALSE;
p->quality.OpenQflag = FALSE;
p->save_options.SaveQflag = FALSE;
return (0);
}
/*
----------------------------------------------------------------
Functions for running a hydraulic analysis
----------------------------------------------------------------
*/
int DLLEXPORT EN_solveH(EN_ProjectHandle ph)
/*----------------------------------------------------------------
** Input: none
** Output: none
** Returns: error code
** Purpose: solves for network hydraulics in all time periods
**----------------------------------------------------------------
*/
{
int errcode;
long t, tstep;
EN_Project *p = (EN_Project*)ph;
/* Open hydraulics solver */
errcode = EN_openH(ph);
if (!errcode) {
/* Initialize hydraulics */
errcode = EN_initH(ph, EN_SAVE);
writecon(FMT14);
/* Analyze each hydraulic period */
if (!errcode)
do {
/* Display progress message */
/*** Updated 6/24/02 ***/
sprintf(p->Msg, "%-10s",
clocktime(p->report.Atime, p->time_options.Htime));
writecon(p->Msg);
sprintf(p->Msg, FMT101, p->report.Atime);
writewin(p->viewprog, p->Msg);
/* Solve for hydraulics & advance to next time period */
tstep = 0;
ERRCODE(EN_runH(ph, &t));
ERRCODE(EN_nextH(ph, &tstep));
/*** Updated 6/24/02 ***/
writecon("\b\b\b\b\b\b\b\b\b\b");
} while (tstep > 0);
}
/* Close hydraulics solver */
/*** Updated 6/24/02 ***/
writecon("\b\b\b\b\b\b\b\b ");
EN_closeH(ph);
errcode = MAX(errcode, p->Warnflag);
return (errcode);
}
int DLLEXPORT EN_saveH(EN_ProjectHandle ph)
/*----------------------------------------------------------------
** Input: none
** Output: none
** Returns: error code
** Purpose: saves hydraulic results to binary file.
**
** Must be called before ENreport() if no WQ simulation made.
** Should not be called if ENsolveQ() will be used.
**----------------------------------------------------------------
*/
{
char tmpflag;
int errcode;
EN_Project *p = (EN_Project*)ph;
/* Check if hydraulic results exist */
if (!p->save_options.SaveHflag)
return (104);
/* Temporarily turn off WQ analysis */
tmpflag = p->quality.Qualflag;
p->quality.Qualflag = NONE;
/* Call WQ solver to simply transfer results */
/* from Hydraulics file to Output file at */
/* fixed length reporting time intervals. */
errcode = EN_solveQ(p);
/* Restore WQ analysis option */
p->quality.Qualflag = tmpflag;
if (errcode) {
errmsg(p, errcode);
}
return (errcode);
}
int DLLEXPORT EN_openH(EN_ProjectHandle ph)
/*----------------------------------------------------------------
** Input: none
** Output: none
** Returns: error code
** Purpose: sets up data structures for hydraulic analysis
**----------------------------------------------------------------
*/
{
int errcode = 0;
EN_Project *p = (EN_Project*)ph;
/* Check that input data exists */
p->hydraulics.OpenHflag = FALSE;
p->save_options.SaveHflag = FALSE;
if (!p->Openflag) {
return (102);
}
/* Check that previously saved hydraulics file not in use */
if (p->out_files.Hydflag == USE) {
return (107);
}
/* Open hydraulics solver */
ERRCODE(openhyd(p));
if (!errcode)
p->hydraulics.OpenHflag = TRUE;
else
errmsg(p, errcode);
return (errcode);
}
/*** Updated 3/1/01 ***/
int DLLEXPORT EN_initH(EN_ProjectHandle ph, int flag)
/*----------------------------------------------------------------
** Input: flag = 2-digit flag where 1st (left) digit indicates
** if link flows should be re-initialized (1) or
** not (0) and 2nd digit indicates if hydraulic
** results should be saved to file (1) or not (0)
** Output: none
** Returns: error code
** Purpose: initializes hydraulic analysis
**----------------------------------------------------------------
*/
{
int errcode = 0;
int sflag, fflag;
EN_Project *p = (EN_Project*)ph;
/* Reset status flags */
p->save_options.SaveHflag = FALSE;
p->Warnflag = FALSE;
/* Get values of save-to-file flag and reinitialize-flows flag */
fflag = flag / EN_INITFLOW;
sflag = flag - fflag * EN_INITFLOW;
/* Check that hydraulics solver was opened */
if (!p->hydraulics.OpenHflag)
return (103);
/* Open hydraulics file */
p->save_options.Saveflag = FALSE;
if (sflag > 0) {
errcode = openhydfile(p);
if (!errcode)
p->save_options.Saveflag = TRUE;
else {
errmsg(p, errcode);
return errcode;
}
}
/* Initialize hydraulics */
inithyd(p, fflag);
if (p->report.Statflag > 0)
writeheader(p, STATHDR, 0);
return (errcode);
}
int DLLEXPORT EN_runH(EN_ProjectHandle ph, long *t) {
int errcode;
EN_Project *p = (EN_Project*)ph;
*t = 0;
if (!p->hydraulics.OpenHflag)
return (103);
errcode = runhyd(p, t);
if (errcode)
errmsg(p, errcode);
return (errcode);
}
int DLLEXPORT EN_nextH(EN_ProjectHandle ph, long *tstep) {
int errcode;
EN_Project *p = (EN_Project*)ph;
*tstep = 0;
if (!p->hydraulics.OpenHflag)
return (103);
errcode = nexthyd(p, tstep);
if (errcode)
errmsg(p, errcode);
else if (p->save_options.Saveflag && *tstep == 0)
p->save_options.SaveHflag = TRUE;
return (errcode);
}
int DLLEXPORT EN_closeH(EN_ProjectHandle ph)
{
EN_Project *p = (EN_Project*)ph;
if (!p->Openflag) {
return (102);
}
if (p->hydraulics.OpenHflag) {
closehyd(p);
}
p->hydraulics.OpenHflag = FALSE;
return (0);
}
int DLLEXPORT EN_savehydfile(EN_ProjectHandle ph, char *filename) {
FILE *f;
int c;
FILE *HydFile;
EN_Project *p = (EN_Project*)ph;
/* Check that hydraulics results exist */
if (p->out_files.HydFile == NULL || !p->save_options.SaveHflag)
return (104);
/* Open file */
if ((f = fopen(filename, "w+b")) == NULL)
return (305);
/* Copy from HydFile to f */
HydFile = p->out_files.HydFile;
fseek(HydFile, 0, SEEK_SET);
while ((c = fgetc(HydFile)) != EOF) {
fputc(c, f);
}
fclose(f);
return (0);
}
int DLLEXPORT EN_usehydfile(EN_ProjectHandle ph, char *filename) {
int errcode;
EN_Project *p = (EN_Project*)ph;
/* Check that input data exists & hydraulics system closed */
if (!p->Openflag)
return (102);
if (p->hydraulics.OpenHflag)
return (108);
/* Try to open hydraulics file */
strncpy(p->out_files.HydFname, filename, MAXFNAME);
p->out_files.Hydflag = USE;
p->save_options.SaveHflag = TRUE;
errcode = openhydfile(p);
/* If error, then reset flags */
if (errcode) {
strcpy(p->out_files.HydFname, "");
p->out_files.Hydflag = SCRATCH;
p->save_options.SaveHflag = FALSE;
}
return (errcode);
}
/*
----------------------------------------------------------------
Functions for running a WQ analysis
----------------------------------------------------------------
*/
int DLLEXPORT EN_solveQ(EN_ProjectHandle ph) {
int errcode;
long t, tstep;
EN_Project *p = (EN_Project*)ph;
/* Open WQ solver */
errcode = EN_openQ(ph);
if (!errcode) {
/* Initialize WQ */
errcode = EN_initQ(ph, EN_SAVE);
if (p->quality.Qualflag)
writecon(FMT15);
else {
writecon(FMT16);
writewin(p->viewprog, FMT103);
}
/* Analyze each hydraulic period */
if (!errcode)
do {
/* Display progress message */
/*** Updated 6/24/02 ***/
sprintf(p->Msg, "%-10s",
clocktime(p->report.Atime, p->time_options.Htime));
writecon(p->Msg);
if (p->quality.Qualflag) {
sprintf(p->Msg, FMT102, p->report.Atime);
writewin(p->viewprog, p->Msg);
}
/* Retrieve current network solution & update WQ to next time period */
tstep = 0;
ERRCODE(EN_runQ(ph, &t));
ERRCODE(EN_nextQ(ph, &tstep));
/*** Updated 6/24/02 ***/
writecon("\b\b\b\b\b\b\b\b\b\b");
} while (tstep > 0);
}
/* Close WQ solver */
/*** Updated 6/24/02 ***/
writecon("\b\b\b\b\b\b\b\b ");
EN_closeQ(ph);
return (errcode);
}
int DLLEXPORT EN_openQ(EN_ProjectHandle ph) {
int errcode = 0;
EN_Project *p = (EN_Project*)ph;
/* Check that hydraulics results exist */
p->quality.OpenQflag = FALSE;
p->save_options.SaveQflag = FALSE;
if (!p->Openflag)
return (102);
// !LT! todo - check for p->save_options.SaveHflag / set sequential/step mode
// if (!p->save_options.SaveHflag) return(104);
/* Open WQ solver */
ERRCODE(openqual(p));
if (!errcode)
p->quality.OpenQflag = TRUE;
else
errmsg(p, errcode);
return (errcode);
}
int DLLEXPORT EN_initQ(EN_ProjectHandle ph, int saveflag) {
int errcode = 0;
EN_Project *p = (EN_Project*)ph;
if (!p->quality.OpenQflag)
return (105);
initqual(p);
p->save_options.SaveQflag = FALSE;
p->save_options.Saveflag = FALSE;
if (saveflag) {
errcode = openoutfile(p);
if (!errcode)
p->save_options.Saveflag = TRUE;
}
return (errcode);
}
int DLLEXPORT EN_runQ(EN_ProjectHandle ph, long *t) {
int errcode;
EN_Project *p = (EN_Project*)ph;
*t = 0;
if (!p->quality.OpenQflag)
return (105);
errcode = runqual(p, t);
if (errcode)
errmsg(p, errcode);
return (errcode);
}
int DLLEXPORT EN_nextQ(EN_ProjectHandle ph, long *tstep) {
int errcode;
EN_Project *p = (EN_Project*)ph;
*tstep = 0;
if (!p->quality.OpenQflag)
return (105);
errcode = nextqual(p, tstep);
if (!errcode && p->save_options.Saveflag && *tstep == 0) {
p->save_options.SaveQflag = TRUE;
}
if (errcode)
errmsg(p, errcode);
return (errcode);
}
int DLLEXPORT EN_stepQ(EN_ProjectHandle ph, long *tleft) {
int errcode;
EN_Project *p = (EN_Project*)ph;
*tleft = 0;
if (!p->quality.OpenQflag)
return (105);
errcode = stepqual(p, tleft);
if (!errcode && p->save_options.Saveflag && *tleft == 0) {
p->save_options.SaveQflag = TRUE;
}
if (errcode)
errmsg(p, errcode);
return (errcode);
}
int DLLEXPORT EN_closeQ(EN_ProjectHandle ph) {
EN_Project *p = (EN_Project*)ph;
if (!p->Openflag)
return (102);
closequal(p);
p->quality.OpenQflag = FALSE;
return (0);
}
/*
----------------------------------------------------------------
Functions for generating an output report
----------------------------------------------------------------
*/
int DLLEXPORT EN_writeline(EN_ProjectHandle ph, char *line) {
EN_Project *p = (EN_Project*)ph;
if (!p->Openflag)
return (102);
writeline(p, line);
return (0);
}
int DLLEXPORT EN_report(EN_ProjectHandle ph) {
int errcode;
EN_Project *p = (EN_Project*)ph;
/* Check if results saved to binary output file */
if (!p->save_options.SaveQflag)
return (106);
errcode = writereport(p);
if (errcode)
errmsg(p, errcode);
return (errcode);
}
int DLLEXPORT EN_resetreport(EN_ProjectHandle ph) {
int i;
EN_Project *p = (EN_Project*)ph;
if (!p->Openflag)
return (102);
initreport(&p->report);
for (i = 1; i <= p->network.Nnodes; i++)
p->network.Node[i].Rpt = 0;
for (i = 1; i <= p->network.Nlinks; i++)
p->network.Link[i].Rpt = 0;
return (0);
}
int DLLEXPORT EN_setreport(EN_ProjectHandle ph, char *s) {
char s1[MAXLINE + 1];
EN_Project *p = (EN_Project*)ph;
if (!p->Openflag)
return (102);
if (strlen(s) > MAXLINE)
return (250);
strcpy(s1, s);
if (setreport(p, s1) > 0)
return (250);
else
return (0);
}
/*
----------------------------------------------------------------
Functions for retrieving network information
----------------------------------------------------------------
*/
/*** Updated 10/25/00 ***/
int DLLEXPORT EN_getversion(int *v)
/*----------------------------------------------------------------
** Input: none
** Output: *v = version number of the source code
** Returns: error code (should always be 0)
** Purpose: retrieves a number assigned to the most recent
** update of the source code. This number, set by the
** constant CODEVERSION found in TYPES.H, is to be
** interpreted with implied decimals, i.e., "20100" == "2(.)01(.)00"
**----------------------------------------------------------------
*/
{
*v = CODEVERSION;
return (0);
}
int DLLEXPORT EN_getcontrol(EN_ProjectHandle ph, int cindex, int *ctype, int *lindex,
EN_API_FLOAT_TYPE *setting, int *nindex,
EN_API_FLOAT_TYPE *level) {
double s, lvl;
EN_Project *pr = (EN_Project*)ph;
EN_Network *net = &pr->network;
Scontrol *Control = net->Control;
Snode *Node = net->Node;
Slink *Link = net->Link;
const int Njuncs = net->Njuncs;
double *Ucf = pr->Ucf;
s = 0.0;
lvl = 0.0;
*ctype = 0;
*lindex = 0;
*nindex = 0;
if (!pr->Openflag)
return (102);
if (cindex < 1 || cindex > net->Ncontrols)
return (241);
*ctype = Control[cindex].Type;
*lindex = Control[cindex].Link;
s = Control[cindex].Setting;
if (Control[cindex].Setting != MISSING) {
switch (Link[*lindex].Type) {
case EN_PRV:
case EN_PSV:
case EN_PBV:
s *= Ucf[PRESSURE];
break;
case EN_FCV:
s *= Ucf[FLOW];
default:
break;
}
} else if (Control[cindex].Status == OPEN) {
s = 1.0;
}
/*** Updated 3/1/01 ***/
else
s = 0.0;
*nindex = Control[cindex].Node;
if (*nindex > Njuncs)
lvl = (Control[cindex].Grade - Node[*nindex].El) * Ucf[ELEV];
else if (*nindex > 0)
lvl = (Control[cindex].Grade - Node[*nindex].El) * Ucf[PRESSURE];
else
lvl = (EN_API_FLOAT_TYPE)Control[cindex].Time;
*setting = (EN_API_FLOAT_TYPE)s;
*level = (EN_API_FLOAT_TYPE)lvl;
return (0);
}
int DLLEXPORT EN_getcount(EN_ProjectHandle ph, EN_CountType code, int *count) {
EN_Project *pr = (EN_Project*)ph;
EN_Network *net = &pr->network;
*count = 0;
if (!pr->Openflag)
return (102);
switch (code) {
case EN_NODECOUNT:
*count = net->Nnodes;
break;
case EN_TANKCOUNT:
*count = net->Ntanks;
break;
case EN_LINKCOUNT:
*count = net->Nlinks;
break;
case EN_PATCOUNT:
*count = net->Npats;
break;
case EN_CURVECOUNT:
*count = net->Ncurves;
break;
case EN_CONTROLCOUNT:
*count = net->Ncontrols;
break;
case EN_RULECOUNT:
*count = net->Nrules;
break;
default:
return (251);
}
return (0);
}
int DLLEXPORT EN_getoption(EN_ProjectHandle ph, EN_Option code,
EN_API_FLOAT_TYPE *value) {
EN_Project *pr = (EN_Project*)ph;
hydraulics_t *hyd = &pr->hydraulics;
quality_t *qu = &pr->quality;
double *Ucf = pr->Ucf;
double v = 0.0;
*value = 0.0;
if (!pr->Openflag)
return (102);
switch (code) {
case EN_TRIALS:
v = (double)hyd->MaxIter;
break;
case EN_ACCURACY:
v = hyd->Hacc;
break;
case EN_TOLERANCE:
v = qu->Ctol * Ucf[QUALITY];
break;
case EN_EMITEXPON:
if (hyd->Qexp > 0.0)
v = 1.0 / hyd->Qexp;
break;
case EN_DEMANDMULT:
v = hyd->Dmult;
break;
case EN_HEADERROR:
v = hyd->HeadErrorLimit * Ucf[HEAD];
break;
case EN_FLOWCHANGE:
v = hyd->FlowChangeLimit * Ucf[FLOW];
break;
default:
return (251);
}
*value = (EN_API_FLOAT_TYPE)v;
return (0);
}
int DLLEXPORT EN_gettimeparam(EN_ProjectHandle ph, int code, long *value) {
int i;
EN_Project *pr = (EN_Project*)ph;
report_options_t *rep = &pr->report;
quality_t *qu = &pr->quality;
time_options_t *time = &pr->time_options;
*value = 0;
if (!pr->Openflag)
return (102);
if (code < EN_DURATION || code > EN_NEXTEVENTIDX)
return (251);
switch (code) {
case EN_DURATION:
*value = time->Dur;
break;
case EN_HYDSTEP:
*value = time->Hstep;
break;
case EN_QUALSTEP:
*value = qu->Qstep;
break;
case EN_PATTERNSTEP:
*value = time->Pstep;
break;
case EN_PATTERNSTART:
*value = time->Pstart;
break;
case EN_REPORTSTEP:
*value = time->Rstep;
break;
case EN_REPORTSTART:
*value = time->Rstart;
break;
case EN_STATISTIC:
*value = rep->Tstatflag;
break;
case EN_RULESTEP:
*value = time->Rulestep;
break;
case EN_PERIODS:
*value = rep->Nperiods;
break;
case EN_STARTTIME:
*value = time->Tstart;
break; /* Added TNT 10/2/2009 */
case EN_HTIME:
*value = time->Htime;
break;
case EN_NEXTEVENT:
*value = time->Hstep; // find the lesser of the hydraulic time step length, or the
// time to next fill/empty
tanktimestep(pr,value);
break;
case EN_NEXTEVENTIDX:
*value = time->Hstep;
i = tanktimestep(pr, value);
*value = i;
break;
}
return (0);
}
int DLLEXPORT EN_getflowunits(EN_ProjectHandle ph, int *code) {
EN_Project *p = (EN_Project*)ph;
*code = -1;
if (!p->Openflag)
return (102);
*code = p->parser.Flowflag;
return (0);
}
int DLLEXPORT EN_setflowunits(EN_ProjectHandle ph, int code) {
int i, j;
double qfactor, vfactor, hfactor, efactor, xfactor, yfactor;
EN_Project *p = (EN_Project*)ph;
double *Ucf = p->Ucf;
EN_Network *net = &p->network;
if (!p->Openflag) {
return(102);
}
/* Determine unit system based on flow units */
qfactor = Ucf[FLOW];
vfactor = Ucf[VOLUME];
hfactor = Ucf[HEAD];
efactor = Ucf[ELEV];
p->parser.Flowflag = code;
switch (code)
{
case LPS: /* Liters/sec */
case LPM: /* Liters/min */
case MLD: /* megaliters/day */
case CMH: /* cubic meters/hr */
case CMD: /* cubic meters/day */
p->parser.Unitsflag = SI;
break;
default:
p->parser.Unitsflag = US;
break;
}
/* Revise pressure units depending on flow units */
if (p->parser.Unitsflag != SI) {
p->parser.Pressflag = PSI;
}
else if (p->parser.Pressflag == PSI) {
p->parser.Pressflag = METERS;
}
initunits(p);
//update curves
for (i=1; i <= net->Ncurves; i++)
{
switch (net->Curve[i].Type)
{
case V_CURVE:
xfactor = efactor/Ucf[ELEV];
yfactor = vfactor/Ucf[VOLUME];
break;
case H_CURVE:
case P_CURVE:
xfactor = qfactor/Ucf[FLOW];
yfactor = hfactor/Ucf[HEAD];
break;
case E_CURVE:
xfactor = qfactor/Ucf[FLOW];
yfactor = 1;
break;
default:
xfactor = 1;
yfactor = 1;
}
for (j=0; j < net->Curve[i].Npts; j++)
{
net->Curve[i].X[j] = net->Curve[i].X[j]/xfactor;
net->Curve[i].Y[j] = net->Curve[i].Y[j]/yfactor;
}
}
return(0);
}
int DLLEXPORT EN_getpatternindex(EN_ProjectHandle ph, char *id, int *index) {
int i;
EN_Project *p = (EN_Project*)ph;
*index = 0;
if (!p->Openflag)
return (102);
for (i = 1; i <= p->network.Npats; i++) {
if (strcmp(id, p->network.Pattern[i].ID) == 0) {
*index = i;
return (0);
}
}
*index = 0;
return (205);
}
int DLLEXPORT EN_getpatternid(EN_ProjectHandle ph, int index, char *id) {
EN_Project *p = (EN_Project*)ph;
strcpy(id, "");
if (!p->Openflag)
return (102);
if (index < 1 || index > p->network.Npats)
return (205);
strcpy(id, p->network.Pattern[index].ID);
return (0);
}
int DLLEXPORT EN_getpatternlen(EN_ProjectHandle ph, int index, int *len) {
EN_Project *p = (EN_Project*)ph;
if (!p->Openflag)
return (102);
if (index < 1 || index > p->network.Npats)
return (205);
*len = p->network.Pattern[index].Length;
return (0);
}
int DLLEXPORT EN_getpatternvalue(EN_ProjectHandle ph, int index, int period,
EN_API_FLOAT_TYPE *value) {
EN_Project *p = (EN_Project*)ph;
*value = 0.0;
if (!p->Openflag)
return (102);
if (index < 1 || index > p->network.Npats)
return (205);
if (period < 1 || period > p->network.Pattern[index].Length)
return (251);
*value = (EN_API_FLOAT_TYPE)p->network.Pattern[index].F[period - 1];
return (0);
}
int DLLEXPORT EN_getcurveindex(EN_ProjectHandle ph, char *id, int *index) {
int i;
EN_Project *p = (EN_Project*)ph;
*index = 0;
if (!p->Openflag)
return (102);
for (i = 1; i <= p->network.Ncurves; i++) {
if (strcmp(id, p->network.Curve[i].ID) == 0) {
*index = i;
return (0);
}
}
*index = 0;
return (206);
}
int DLLEXPORT EN_getcurveid(EN_ProjectHandle ph, int index, char *id) {
EN_Project *p = (EN_Project*)ph;
strcpy(id, "");
if (!p->Openflag)
return (102);
if (index < 1 || index > p->network.Ncurves)
return (206);
strcpy(id, p->network.Curve[index].ID);
return (0);
}
int DLLEXPORT EN_getcurvelen(EN_ProjectHandle ph, int index, int *len) {
EN_Project *p = (EN_Project*)ph;
if (!p->Openflag)
return (102);
if (index < 1 || index > p->network.Ncurves)
return (206);
*len = p->network.Curve[index].Npts;
return (0);
}
int DLLEXPORT EN_getcurvevalue(EN_ProjectHandle ph, int index, int pnt,
EN_API_FLOAT_TYPE *x, EN_API_FLOAT_TYPE *y) {
EN_Project *p = (EN_Project*)ph;
*x = 0.0;
*y = 0.0;
if (!p->Openflag)
return (102);
if (index < 1 || index > p->network.Ncurves)
return (206);
if (pnt < 1 || pnt > p->network.Curve[index].Npts)
return (251);
*x = (EN_API_FLOAT_TYPE)p->network.Curve[index].X[pnt - 1];
*y = (EN_API_FLOAT_TYPE)p->network.Curve[index].Y[pnt - 1];
return (0);
}
int DLLEXPORT EN_getqualtype(EN_ProjectHandle ph, int *qualcode, int *tracenode) {
EN_Project *p = (EN_Project*)ph;
*tracenode = 0;
if (!p->Openflag)
return (102);
*qualcode = p->quality.Qualflag;
if (p->quality.Qualflag == TRACE)
*tracenode = p->quality.TraceNode;
return (0);
}
int DLLEXPORT EN_getqualinfo(EN_Project *p, int *qualcode, char *chemname,
char *chemunits, int *tracenode) {
EN_getqualtype(p, qualcode, tracenode);
if (p->quality.Qualflag == TRACE) {
strncpy(chemname, "", MAXID);
strncpy(chemunits, "dimensionless", MAXID);
} else {
strncpy(chemname, p->quality.ChemName, MAXID);
strncpy(chemunits, p->quality.ChemUnits, MAXID);
}
return 0;
}
int DLLEXPORT EN_geterror(int errcode, char *errmsg, int n) {
char newMsg[MAXMSG+1];
switch (errcode) {
case 1:
strncpy(errmsg, WARN1, n);
break;
case 2:
strncpy(errmsg, WARN2, n);
break;
case 3:
strncpy(errmsg, WARN3, n);
break;
case 4:
strncpy(errmsg, WARN4, n);
break;
case 5:
strncpy(errmsg, WARN5, n);
break;
case 6:
strncpy(errmsg, WARN6, n);
break;
default:
geterrmsg(errcode, newMsg);
strncpy(errmsg, newMsg, n);
}
if (strlen(errmsg) == 0)
return (251);
else
return (0);
}
int DLLEXPORT EN_getstatistic(EN_ProjectHandle ph, int code, EN_API_FLOAT_TYPE *value) {
EN_Project *p = (EN_Project*)ph;
switch (code) {
case EN_ITERATIONS:
*value = (EN_API_FLOAT_TYPE)p->hydraulics.iterations;
break;
case EN_RELATIVEERROR:
*value = (EN_API_FLOAT_TYPE)p->hydraulics.relativeError;
break;
default:
break;
}
return 0;
}
/*
----------------------------------------------------------------
Functions for retrieving node data
----------------------------------------------------------------
*/
int DLLEXPORT EN_getnodeindex(EN_ProjectHandle ph, char *id, int *index) {
EN_Project *p = (EN_Project*)ph;
*index = 0;
if (!p->Openflag)
return (102);
*index = findnode(&p->network,id);
if (*index == 0)
return (203);
else
return (0);
}
int DLLEXPORT EN_getnodeid(EN_ProjectHandle ph, int index, char *id) {
EN_Project *p = (EN_Project*)ph;
strcpy(id, "");
if (!p->Openflag)
return (102);
if (index < 1 || index > p->network.Nnodes)
return (203);
strcpy(id, p->network.Node[index].ID);
return (0);
}
int DLLEXPORT EN_getnodetype(EN_ProjectHandle ph, int index, int *code) {
EN_Project *p = (EN_Project*)ph;
*code = -1;
if (!p->Openflag)
return (102);
if (index < 1 || index > p->network.Nnodes)
return (203);
if (index <= p->network.Njuncs)
*code = EN_JUNCTION;
else {
if (p->network.Tank[index - p->network.Njuncs].A == 0.0)
*code = EN_RESERVOIR;
else
*code = EN_TANK;
}
return (0);
}
int DLLEXPORT EN_getcoord(EN_ProjectHandle ph, int index, EN_API_FLOAT_TYPE *x,
EN_API_FLOAT_TYPE *y) {
EN_Project *p = (EN_Project*)ph;
if (!p->Openflag)
return (102);
if (index < 1 || index > p->network.Nnodes)
return (203);
if (!p->parser.Coordflag)
return (255);
// check if node have coords
if (p->network.Coord[index].HaveCoords == FALSE)
return (254);
*x = (EN_API_FLOAT_TYPE)p->network.Coord[index].X;
*y = (EN_API_FLOAT_TYPE)p->network.Coord[index].Y;
return 0;
}
int DLLEXPORT EN_setcoord(EN_ProjectHandle ph, int index, EN_API_FLOAT_TYPE x,
EN_API_FLOAT_TYPE y) {
EN_Project *p = (EN_Project*)ph;
if (!p->Openflag)
return (102);
if (index < 1 || index > p->network.Nnodes)
return (203);
if (!p->parser.Coordflag)
return (255);
p->network.Coord[index].X = x;
p->network.Coord[index].Y = y;
p->network.Coord[index].HaveCoords = TRUE;
return 0;
}
int DLLEXPORT EN_getnodevalue(EN_ProjectHandle ph, int index, int code,
EN_API_FLOAT_TYPE *value) {
double v = 0.0;
Pdemand demand;
Psource source;
EN_Project *p = (EN_Project*)ph;
EN_Network *net = &p->network;
hydraulics_t *hyd = &p->hydraulics;
quality_t *qu = &p->quality;
Snode *Node = net->Node;
Stank *Tank = net->Tank;
const int Nnodes = net->Nnodes;
const int Njuncs = net->Njuncs;
double *Ucf = p->Ucf;
double *NodeDemand = hyd->NodeDemand;
double *NodeQual = qu->NodeQual;
/* Check for valid arguments */
*value = 0.0;
if (!p->Openflag)
return (102);
if (index <= 0 || index > Nnodes)
return (203);
/* Retrieve called-for parameter */
switch (code) {
case EN_ELEVATION:
v = Node[index].El * Ucf[ELEV];
break;
case EN_BASEDEMAND:
v = 0.0;
/* NOTE: primary demand category is last on demand list */
if (index <= Njuncs)
for (demand = Node[index].D; demand != NULL; demand = demand->next)
v = (demand->Base);
v *= Ucf[FLOW];
break;
case EN_PATTERN:
v = 0.0;
/* NOTE: primary demand category is last on demand list */
if (index <= Njuncs) {
for (demand = Node[index].D; demand != NULL; demand = demand->next)
v = (double)(demand->Pat);
} else
v = (double)(Tank[index - Njuncs].Pat);
break;
case EN_EMITTER:
v = 0.0;
if (Node[index].Ke > 0.0)
v = Ucf[FLOW] / pow((Ucf[PRESSURE] * Node[index].Ke), (1.0 / hyd->Qexp));
break;
case EN_INITQUAL:
v = Node[index].C0 * Ucf[QUALITY];
break;
/*** Additional parameters added for retrieval ***/
case EN_SOURCEQUAL:
case EN_SOURCETYPE:
case EN_SOURCEMASS:
case EN_SOURCEPAT:
source = Node[index].S;
if (source == NULL)
return (240);
if (code == EN_SOURCEQUAL)
v = source->C0;
else if (code == EN_SOURCEMASS)
v = source->Smass * 60.0;
else if (code == EN_SOURCEPAT)
v = source->Pat;
else
v = source->Type;
break;
case EN_TANKLEVEL:
if (index <= Njuncs)
return (251);
v = (Tank[index - Njuncs].H0 - Node[index].El) * Ucf[ELEV];
break;
/*** New parameter added for retrieval ***/
case EN_INITVOLUME:
v = 0.0;
if (index > Njuncs)
v = Tank[index - Njuncs].V0 * Ucf[VOLUME];
break;
/*** New parameter added for retrieval ***/
case EN_MIXMODEL:
v = MIX1;
if (index > Njuncs)
v = Tank[index - Njuncs].MixModel;
break;
/*** New parameter added for retrieval ***/
case EN_MIXZONEVOL:
v = 0.0;
if (index > Njuncs)
v = Tank[index - Njuncs].V1max * Ucf[VOLUME];
break;
case EN_DEMAND:
v = NodeDemand[index] * Ucf[FLOW];
break;
case EN_HEAD:
v = hyd->NodeHead[index] * Ucf[HEAD];
break;
case EN_PRESSURE:
v = (hyd->NodeHead[index] - Node[index].El) * Ucf[PRESSURE];
break;
case EN_QUALITY:
v = NodeQual[index] * Ucf[QUALITY];
break;
/*** New parameters added for retrieval begins here ***/
/*** (Thanks to Nicolas Basile of Ecole Polytechnique ***/
/*** de Montreal for suggesting some of these.) ***/
case EN_TANKDIAM:
v = 0.0;
if (index > Njuncs) {
v = sqrt(4.0 / PI * Tank[index - Njuncs].A) * Ucf[ELEV];
}
break;
case EN_MINVOLUME:
v = 0.0;
if (index > Njuncs)
v = Tank[index - Njuncs].Vmin * Ucf[VOLUME];
break;
case EN_MAXVOLUME: // !sph
v = 0.0;
if (index > Njuncs)
v = Tank[index - Njuncs].Vmax * Ucf[VOLUME];
break;
case EN_VOLCURVE:
v = 0.0;
if (index > Njuncs)
v = Tank[index - Njuncs].Vcurve;
break;
case EN_MINLEVEL:
v = 0.0;
if (index > Njuncs) {
v = (Tank[index - Njuncs].Hmin - Node[index].El) * Ucf[ELEV];
}
break;
case EN_MAXLEVEL:
v = 0.0;
if (index > Njuncs) {
v = (Tank[index - Njuncs].Hmax - Node[index].El) * Ucf[ELEV];
}
break;
case EN_MIXFRACTION:
v = 1.0;
if (index > Njuncs && Tank[index - Njuncs].Vmax > 0.0) {
v = Tank[index - Njuncs].V1max / Tank[index - Njuncs].Vmax;
}
break;
case EN_TANK_KBULK:
v = 0.0;
if (index > Njuncs)
v = Tank[index - Njuncs].Kb * SECperDAY;
break;
/*** New parameter additions ends here. ***/
case EN_TANKVOLUME:
if (index <= Njuncs)
return (251);
v = tankvolume(p, index - Njuncs, hyd->NodeHead[index]) * Ucf[VOLUME];
break;
default:
return (251);
}
*value = (EN_API_FLOAT_TYPE)v;
return (0);
}
/*
----------------------------------------------------------------
Functions for retrieving link data
----------------------------------------------------------------
*/
int DLLEXPORT EN_getlinkindex(EN_ProjectHandle ph, char *id, int *index) {
EN_Project *p = (EN_Project*)ph;
*index = 0;
if (!p->Openflag)
return (102);
*index = findlink(&p->network,id);
if (*index == 0)
return (204);
else
return (0);
}
int DLLEXPORT EN_getlinkid(EN_ProjectHandle ph, int index, char *id) {
EN_Project *p = (EN_Project*)ph;
strcpy(id, "");
if (!p->Openflag)
return (102);
if (index < 1 || index > p->network.Nlinks)
return (204);
strcpy(id, p->network.Link[index].ID);
return (0);
}
int DLLEXPORT EN_getlinktype(EN_ProjectHandle ph, int index, EN_LinkType *code) {
EN_Project *p = (EN_Project*)ph;
*code = -1;
if (!p->Openflag)
return (102);
if (index < 1 || index > p->network.Nlinks)
return (204);
*code = p->network.Link[index].Type;
return (0);
}
int DLLEXPORT EN_getlinknodes(EN_ProjectHandle ph, int index, int *node1,
int *node2) {
EN_Project *p = (EN_Project*)ph;
*node1 = 0;
*node2 = 0;
if (!p->Openflag)
return (102);
if (index < 1 || index > p->network.Nlinks)
return (204);
*node1 = p->network.Link[index].N1;
*node2 = p->network.Link[index].N2;
return (0);
}
int DLLEXPORT EN_getlinkvalue(EN_ProjectHandle ph, int index, EN_LinkProperty code,
EN_API_FLOAT_TYPE *value) {
double a, h, q, v = 0.0;
int returnValue = 0;
EN_Project *p = (EN_Project*)ph;
EN_Network *net = &p->network;
hydraulics_t *hyd = &p->hydraulics;
Slink *Link = net->Link;
Spump *Pump = net->Pump;
const int Nlinks = net->Nlinks;
double *Ucf = p->Ucf;
double *LinkFlows = hyd->LinkFlows;
double *LinkSetting = hyd->LinkSetting;
/* Check for valid arguments */
*value = 0.0;
if (!p->Openflag)
return (102);
if (index <= 0 || index > Nlinks)
return (204);
/* Retrieve called-for parameter */
switch (code) {
case EN_DIAMETER:
if (Link[index].Type == EN_PUMP)
v = 0.0;
else
v = Link[index].Diam * Ucf[DIAM];
break;
case EN_LENGTH:
v = Link[index].Len * Ucf[ELEV];
break;
case EN_ROUGHNESS:
if (Link[index].Type <= EN_PIPE) {
if (hyd->Formflag == DW)
v = Link[index].Kc * (1000.0 * Ucf[ELEV]);
else
v = Link[index].Kc;
} else
v = 0.0;
break;
case EN_MINORLOSS:
if (Link[index].Type != EN_PUMP) {
v = Link[index].Km;
v *= (SQR(Link[index].Diam) * SQR(Link[index].Diam) / 0.02517);
} else
v = 0.0;
break;
case EN_INITSTATUS:
if (Link[index].Stat <= CLOSED)
v = 0.0;
else
v = 1.0;
break;
case EN_INITSETTING:
if (Link[index].Type == EN_PIPE || Link[index].Type == EN_CVPIPE)
return (ENgetlinkvalue(index, EN_ROUGHNESS, value));
v = Link[index].Kc;
switch (Link[index].Type) {
case EN_PRV:
case EN_PSV:
case EN_PBV:
v *= Ucf[PRESSURE];
break;
case EN_FCV:
v *= Ucf[FLOW];
default:
break;
}
break;
case EN_KBULK:
v = Link[index].Kb * SECperDAY;
break;
case EN_KWALL:
v = Link[index].Kw * SECperDAY;
break;
case EN_FLOW:
/*** Updated 10/25/00 ***/
if (hyd->LinkStatus[index] <= CLOSED)
v = 0.0;
else
v = LinkFlows[index] * Ucf[FLOW];
break;
case EN_VELOCITY:
if (Link[index].Type == EN_PUMP) {
v = 0.0;
}
/*** Updated 11/19/01 ***/
else if (hyd->LinkStatus[index] <= CLOSED)
v = 0.0;
else {
q = ABS(LinkFlows[index]);
a = PI * SQR(Link[index].Diam) / 4.0;
v = q / a * Ucf[VELOCITY];
}
break;
case EN_HEADLOSS:
/*** Updated 11/19/01 ***/
if (hyd->LinkStatus[index] <= CLOSED)
v = 0.0;
else {
h = hyd->NodeHead[Link[index].N1] - hyd->NodeHead[Link[index].N2];
if (Link[index].Type != EN_PUMP)
h = ABS(h);
v = h * Ucf[HEADLOSS];
}
break;
case EN_STATUS:
if (hyd->LinkStatus[index] <= CLOSED)
v = 0.0;
else
v = 1.0;
break;
case EN_SETTING:
if (Link[index].Type == EN_PIPE || Link[index].Type == EN_CVPIPE) {
return (ENgetlinkvalue(index, EN_ROUGHNESS, value));
}
if (LinkSetting[index] == MISSING) {
v = 0.0;
} else {
v = LinkSetting[index];
}
switch (Link[index].Type) {
case EN_PRV:
case EN_PSV:
case EN_PBV:
v *= Ucf[PRESSURE];
break;
case EN_FCV:
v *= Ucf[FLOW];
default:
break;
}
break;
case EN_ENERGY:
getenergy(p, index, &v, &a);
break;
case EN_LINKQUAL:
v = avgqual(p, index) * Ucf[LINKQUAL];
break;
case EN_LINKPATTERN:
if (Link[index].Type == EN_PUMP)
v = (double)Pump[findpump(&p->network, index)].Upat;
break;
case EN_EFFICIENCY:
getenergy(p, index, &a, &v);
break;
case EN_PRICEPATTERN:
if (Link[index].Type == EN_PUMP)
v = (double)Pump[findpump(&p->network, index)].Epat;
break;
case EN_HEADCURVE:
if (Link[index].Type == EN_PUMP) {
v = (double)Pump[findpump(&p->network, index)].Hcurve;
if (v == 0) {
returnValue = 226;
}
}
else {
v = 0;
returnValue = 211;
}
break;
case EN_EFFICIENCYCURVE:
if (Link[index].Type == EN_PUMP) {
v = (double)Pump[findpump(&p->network, index)].Ecurve;
if (v == 0) {
returnValue = 268;
}
}
else {
v = 0;
returnValue = 211;
}
default:
v = 0;
returnValue = 251;
}
*value = (EN_API_FLOAT_TYPE)v;
return returnValue;
}
int DLLEXPORT EN_getcurve(EN_ProjectHandle ph, int curveIndex, char *id, int *nValues,
EN_API_FLOAT_TYPE **xValues,
EN_API_FLOAT_TYPE **yValues) {
int iPoint, nPoints;
Scurve curve;
EN_API_FLOAT_TYPE *pointX, *pointY;
EN_Project *p = (EN_Project*)ph;
/* Check that input file opened */
if (!p->Openflag)
return (102);
curve = p->network.Curve[curveIndex];
nPoints = curve.Npts;
pointX = calloc(nPoints, sizeof(EN_API_FLOAT_TYPE));
pointY = calloc(nPoints, sizeof(EN_API_FLOAT_TYPE));
for (iPoint = 0; iPoint < nPoints; iPoint++) {
double x = curve.X[iPoint];
double y = curve.Y[iPoint];
pointX[iPoint] = (EN_API_FLOAT_TYPE)x;
pointY[iPoint] = (EN_API_FLOAT_TYPE)y;
}
strncpy(id, "", MAXID);
strncpy(id, curve.ID, MAXID);
*nValues = nPoints;
*xValues = pointX;
*yValues = pointY;
return (0);
}
/*
----------------------------------------------------------------
Functions for changing network data
----------------------------------------------------------------
*/
int DLLEXPORT EN_setcontrol(EN_ProjectHandle ph, int cindex, int ctype, int lindex,
EN_API_FLOAT_TYPE setting, int nindex,
EN_API_FLOAT_TYPE level) {
char status = ACTIVE;
long t = 0;
double s = setting, lvl = level;
EN_Network *net;
Snode *Node;
Slink *Link;
Scontrol *Control;
int Nnodes;
int Njuncs;
int Nlinks;
double *Ucf;
EN_Project *p = (EN_Project*)ph;
/* Check that input file opened */
if (!p->Openflag)
return (102);
/* Check that control exists */
if (cindex < 1 || cindex > p->network.Ncontrols)
return (241);
net = &p->network;
Node = net->Node;
Link = net->Link;
Control = net->Control;
Nnodes = net->Nnodes;
Njuncs = net->Njuncs;
Nlinks = net->Nlinks;
Ucf = p->Ucf;
/* Check that controlled link exists */
if (lindex == 0) {
Control[cindex].Link = 0;
return (0);
}
if (lindex < 0 || lindex > Nlinks)
return (204);
/* Cannot control check valve. */
if (Link[lindex].Type == EN_CVPIPE)
return (207);
/* Check for valid parameters */
if (ctype < 0 || ctype > EN_TIMEOFDAY)
return (251);
if (ctype == EN_LOWLEVEL || ctype == EN_HILEVEL) {
if (nindex < 1 || nindex > Nnodes)
return (203);
} else
nindex = 0;
if (s < 0.0 || lvl < 0.0)
return (202);
/* Adjust units of control parameters */
switch (Link[lindex].Type) {
case EN_PRV:
case EN_PSV:
case EN_PBV:
s /= Ucf[PRESSURE];
break;
case EN_FCV:
s /= Ucf[FLOW];
break;
/*** Updated 9/7/00 ***/
case EN_GPV:
if (s == 0.0)
status = CLOSED;
else if (s == 1.0)
status = OPEN;
else
return (202);
s = Link[lindex].Kc;
break;
case EN_PIPE:
case EN_PUMP:
status = OPEN;
if (s == 0.0)
status = CLOSED;
default:
break;
}
if (ctype == LOWLEVEL || ctype == HILEVEL) {
if (nindex > Njuncs)
lvl = Node[nindex].El + level / Ucf[ELEV];
else
lvl = Node[nindex].El + level / Ucf[PRESSURE];
}
if (ctype == TIMER)
t = (long)ROUND(lvl);
if (ctype == TIMEOFDAY)
t = (long)ROUND(lvl) % SECperDAY;
/* Reset control's parameters */
Control[cindex].Type = (char)ctype;
Control[cindex].Link = lindex;
Control[cindex].Node = nindex;
Control[cindex].Status = status;
Control[cindex].Setting = s;
Control[cindex].Grade = lvl;
Control[cindex].Time = t;
return (0);
}
int DLLEXPORT EN_setnodevalue(EN_ProjectHandle ph, int index, int code, EN_API_FLOAT_TYPE v)
/*----------------------------------------------------------------
** Input: index = node index
** code = node parameter code (see EPANET2.H)
** value = parameter value
** Output: none
** Returns: error code
** Purpose: sets input parameter value for a node
**----------------------------------------------------------------
*/
{
EN_Project *p = (EN_Project*)ph;
EN_Network *net = &p->network;
hydraulics_t *hyd = &p->hydraulics;
quality_t *qu = &p->quality;
Snode *Node = net->Node;
Stank *Tank = net->Tank;
const int Nnodes = net->Nnodes;
const int Njuncs = net->Njuncs;
const int Npats = net->Npats;
double *Ucf = p->Ucf;
int j;
Pdemand demand;
Psource source;
double Htmp;
double value = v;
if (!p->Openflag)
return (102);
if (index <= 0 || index > Nnodes)
return (203);
switch (code) {
case EN_ELEVATION:
if (index <= Njuncs)
Node[index].El = value / Ucf[ELEV];
else {
value = (value / Ucf[ELEV]) - Node[index].El;
j = index - Njuncs;
Tank[j].H0 += value;
Tank[j].Hmin += value;
Tank[j].Hmax += value;
Node[index].El += value;
hyd->NodeHead[index] += value;
}
break;
case EN_BASEDEMAND:
/* NOTE: primary demand category is last on demand list */
if (index <= Njuncs) {
for (demand = Node[index].D; demand != NULL; demand = demand->next) {
if (demand->next == NULL)
demand->Base = value / Ucf[FLOW];
}
}
break;
case EN_PATTERN:
/* NOTE: primary demand category is last on demand list */
j = ROUND(value);
if (j < 0 || j > Npats)
return (205);
if (index <= Njuncs) {
for (demand = Node[index].D; demand != NULL; demand = demand->next) {
if (demand->next == NULL)
demand->Pat = j;
}
} else
Tank[index - Njuncs].Pat = j;
break;
case EN_EMITTER:
if (index > Njuncs)
return (203);
if (value < 0.0)
return (202);
if (value > 0.0)
value = pow((Ucf[FLOW] / value), hyd->Qexp) / Ucf[PRESSURE];
Node[index].Ke = value;
break;
case EN_INITQUAL:
if (value < 0.0)
return (202);
Node[index].C0 = value / Ucf[QUALITY];
if (index > Njuncs)
Tank[index - Njuncs].C = Node[index].C0;
break;
case EN_SOURCEQUAL:
case EN_SOURCETYPE:
case EN_SOURCEPAT:
if (value < 0.0)
return (202);
source = Node[index].S;
if (source == NULL) {
source = (struct Ssource *)malloc(sizeof(struct Ssource));
if (source == NULL)
return (101);
source->Type = CONCEN;
source->C0 = 0.0;
source->Pat = 0;
Node[index].S = source;
}
if (code == EN_SOURCEQUAL) {
source->C0 = value;
} else if (code == EN_SOURCEPAT) {
j = ROUND(value);
if (j < 0 || j > Npats)
return (205);
source->Pat = j;
} else // code == EN_SOURCETYPE
{
j = ROUND(value);
if (j < CONCEN || j > FLOWPACED)
return (251);
else
source->Type = (char)j;
}
return (0);
case EN_TANKLEVEL:
if (index <= Njuncs)
return (251);
j = index - Njuncs;
if (Tank[j].A == 0.0) /* Tank is a reservoir */
{
Tank[j].H0 = value / Ucf[ELEV];
Tank[j].Hmin = Tank[j].H0;
Tank[j].Hmax = Tank[j].H0;
Node[index].El = Tank[j].H0;
hyd->NodeHead[index] = Tank[j].H0;
} else {
value = Node[index].El + value / Ucf[ELEV];
if (value > Tank[j].Hmax || value < Tank[j].Hmin)
return (202);
Tank[j].H0 = value;
Tank[j].V0 = tankvolume(p, j, Tank[j].H0);
// Resetting Volume in addition to initial volume
Tank[j].V = Tank[j].V0;
hyd->NodeHead[index] = Tank[j].H0;
}
break;
/*** New parameters added for retrieval begins here ***/
/*** (Thanks to Nicolas Basile of Ecole Polytechnique ***/
/*** de Montreal for suggesting some of these.) ***/
case EN_TANKDIAM:
if (value <= 0.0)
return (202);
if (index <= Njuncs)
return (251);
j = index - Njuncs;
if (j > 0 && Tank[j].A > 0.0) {
value /= Ucf[ELEV];
Tank[j].A = PI * SQR(value) / 4.0;
Tank[j].Vmin = tankvolume(p, j, Tank[j].Hmin);
Tank[j].V0 = tankvolume(p, j, Tank[j].H0);
Tank[j].Vmax = tankvolume(p, j, Tank[j].Hmax);
} else {
return (251);
}
break;
case EN_MINVOLUME:
if (value < 0.0)
return (202);
if (index <= Njuncs)
return (251);
j = index - Njuncs;
if (j > 0 && Tank[j].A > 0.0) {
Tank[j].Vmin = value / Ucf[VOLUME];
Tank[j].V0 = tankvolume(p, j, Tank[j].H0);
Tank[j].Vmax = tankvolume(p, j, Tank[j].Hmax);
} else {
return (251);
}
break;
case EN_MINLEVEL:
if (value < 0.0)
return (202);
if (index <= Njuncs)
return (251); // not a tank or reservoir
j = index - Njuncs;
if (Tank[j].A == 0.0)
return (251); // node is a reservoir
Htmp = value / Ucf[ELEV] + Node[index].El;
if (Htmp < Tank[j].Hmax && Htmp <= Tank[j].H0) {
if (Tank[j].Vcurve > 0)
return (202);
Tank[j].Hmin = Htmp;
Tank[j].Vmin = tankvolume(p, j, Tank[j].Hmin);
} else {
return (251);
}
break;
case EN_MAXLEVEL:
if (value < 0.0)
return (202);
if (index <= Njuncs)
return (251); // not a tank or reservoir
j = index - Njuncs;
if (Tank[j].A == 0.0)
return (251); // node is a reservoir
Htmp = value / Ucf[ELEV] + Node[index].El;
if (Htmp > Tank[j].Hmin && Htmp >= Tank[j].H0) {
if (Tank[j].Vcurve > 0)
return (202);
Tank[j].Hmax = Htmp;
Tank[j].Vmax = tankvolume(p, j, Tank[j].Hmax);
} else {
return (251);
}
break;
case EN_MIXMODEL:
j = ROUND(value);
if (index <= Njuncs)
return (251);
if (j < MIX1 || j > LIFO)
return (202);
if (index > Njuncs && Tank[index - Njuncs].A > 0.0) {
Tank[index - Njuncs].MixModel = (char)j;
} else {
return (251);
}
break;
case EN_MIXFRACTION:
if (value < 0.0 || value > 1.0)
return (202);
if (index <= Njuncs)
return (251);
j = index - Njuncs;
if (j > 0 && Tank[j].A > 0.0) {
Tank[j].V1max = value * Tank[j].Vmax;
}
break;
case EN_TANK_KBULK:
if (index <= Njuncs)
return (251);
j = index - Njuncs;
if (j > 0 && Tank[j].A > 0.0) {
Tank[j].Kb = value / SECperDAY;
qu->Reactflag = 1;
} else {
return (251);
}
break;
/*** New parameter additions ends here. ***/
default:
return (251);
}
return (0);
}
int DLLEXPORT EN_setlinkvalue(EN_ProjectHandle ph, int index, int code,
EN_API_FLOAT_TYPE v)
/*----------------------------------------------------------------
** Input: index = link index
** code = link parameter code (see EPANET2.H)
** v = parameter value
** Output: none
** Returns: error code
** Purpose: sets input parameter value for a link
**----------------------------------------------------------------
*/
{
EN_Project *p = (EN_Project*)ph;
EN_Network *net = &p->network;
hydraulics_t *hyd = &p->hydraulics;
quality_t *qu = &p->quality;
Slink *Link = net->Link;
const int Nlinks = net->Nlinks;
double *Ucf = p->Ucf;
double *LinkSetting = hyd->LinkSetting;
char s;
double r, value = v;
if (!p->Openflag)
return (102);
if (index <= 0 || index > Nlinks)
return (204);
switch (code) {
case EN_DIAMETER:
if (Link[index].Type != EN_PUMP) {
if (value <= 0.0)
return (202);
value /= Ucf[DIAM]; /* Convert to feet */
r = Link[index].Diam / value; /* Ratio of old to new diam */
Link[index].Km *= SQR(r) * SQR(r); /* Adjust minor loss factor */
Link[index].Diam = value; /* Update diameter */
resistcoeff(p, index); /* Update resistance coeff. */
}
break;
case EN_LENGTH:
if (Link[index].Type <= EN_PIPE) {
if (value <= 0.0)
return (202);
Link[index].Len = value / Ucf[ELEV];
resistcoeff(p, index);
}
break;
case EN_ROUGHNESS:
if (Link[index].Type <= EN_PIPE) {
if (value <= 0.0)
return (202);
Link[index].Kc = value;
if (hyd->Formflag == DW)
Link[index].Kc /= (1000.0 * Ucf[ELEV]);
resistcoeff(p, index);
}
break;
case EN_MINORLOSS:
if (Link[index].Type != EN_PUMP) {
if (value <= 0.0)
return (202);
Link[index].Km =
0.02517 * value / SQR(Link[index].Diam) / SQR(Link[index].Diam);
}
break;
case EN_INITSTATUS:
case EN_STATUS:
/* Cannot set status for a check valve */
if (Link[index].Type == EN_CVPIPE)
return (207);
s = (char)ROUND(value);
if (s < 0 || s > 1)
return (251);
if (code == EN_INITSTATUS)
setlinkstatus(p, index, s, &Link[index].Stat, &Link[index].Kc);
else
setlinkstatus(p, index, s, &hyd->LinkStatus[index], &LinkSetting[index]);
break;
case EN_INITSETTING:
case EN_SETTING:
if (value < 0.0)
return (202);
if (Link[index].Type == EN_PIPE || Link[index].Type == EN_CVPIPE)
return (ENsetlinkvalue(index, EN_ROUGHNESS, v));
else {
switch (Link[index].Type) {
case EN_PUMP:
break;
case EN_PRV:
case EN_PSV:
case EN_PBV:
value /= Ucf[PRESSURE];
break;
case EN_FCV:
value /= Ucf[FLOW];
break;
case EN_TCV:
break;
/*** Updated 9/7/00 ***/
case EN_GPV:
return (202); /* Cannot modify setting for GPV */
default:
return (251);
}
if (code == EN_INITSETTING)
setlinksetting(p, index, value, &Link[index].Stat, &Link[index].Kc);
else
setlinksetting(p, index, value, &hyd->LinkStatus[index],
&LinkSetting[index]);
}
break;
case EN_KBULK:
if (Link[index].Type <= EN_PIPE) {
Link[index].Kb = value / SECperDAY;
qu->Reactflag = 1;
}
break;
case EN_KWALL:
if (Link[index].Type <= EN_PIPE) {
Link[index].Kw = value / SECperDAY;
qu->Reactflag = 1;
}
break;
default:
return (251);
}
return (0);
}
int DLLEXPORT EN_addpattern(EN_ProjectHandle ph, char *id) {
int i, j, n, err = 0;
Spattern *tmpPat;
EN_Project *p = (EN_Project*)ph;
EN_Network *net = &p->network;
parser_data_t *par = &p->parser;
Spattern *Pattern = net->Pattern;
const int Npats = net->Npats;
/* Check if a pattern with same id already exists */
if (!p->Openflag)
return (102);
if (ENgetpatternindex(id, &i) == 0)
return (215);
/* Check that id name is not too long */
if (strlen(id) > MAXID)
return (250);
/* Allocate memory for a new array of patterns */
n = Npats + 1;
tmpPat = (Spattern *)calloc(n + 1, sizeof(Spattern));
if (tmpPat == NULL)
return (101);
/* Copy contents of old pattern array to new one */
for (i = 0; i <= net->Npats; i++) {
strcpy(tmpPat[i].ID, Pattern[i].ID);
tmpPat[i].Length = Pattern[i].Length;
tmpPat[i].F = (double *)calloc(Pattern[i].Length, sizeof(double));
if (tmpPat[i].F == NULL)
err = 1;
else
for (j = 0; j < Pattern[i].Length; j++)
tmpPat[i].F[j] = Pattern[i].F[j];
}
/* Add the new pattern to the new array of patterns */
strcpy(tmpPat[n].ID, id);
tmpPat[n].Length = 1;
tmpPat[n].F = (double *)calloc(tmpPat[n].Length, sizeof(double));
if (tmpPat[n].F == NULL)
err = 1;
else
tmpPat[n].F[0] = 1.0;
/* Abort if memory allocation error */
if (err) {
for (i = 0; i <= n; i++)
if (tmpPat[i].F)
free(tmpPat[i].F);
free(tmpPat);
return (101);
}
// Replace old pattern array with new one
for (i = 0; i <= Npats; i++)
free(Pattern[i].F);
free(Pattern);
Pattern = tmpPat;
net->Npats = n;
par->MaxPats = n;
return 0;
}
int DLLEXPORT EN_setpattern(EN_ProjectHandle ph, int index, EN_API_FLOAT_TYPE *f, int n) {
int j;
EN_Project *p = (EN_Project*)ph;
EN_Network *net = &p->network;
Spattern *Pattern = net->Pattern;
const int Npats = net->Npats;
/* Check for valid arguments */
if (!p->Openflag)
return (102);
if (index <= 0 || index > Npats)
return (205);
if (n <= 0)
return (202);
/* Re-set number of time periods & reallocate memory for multipliers */
Pattern[index].Length = n;
Pattern[index].F = (double *)realloc(Pattern[index].F, n * sizeof(double));
if (Pattern[index].F == NULL)
return (101);
/* Load multipliers into pattern */
for (j = 0; j < n; j++)
Pattern[index].F[j] = f[j];
return (0);
}
int DLLEXPORT EN_setpatternvalue(EN_ProjectHandle ph, int index, int period, EN_API_FLOAT_TYPE value) {
EN_Project *p = (EN_Project*)ph;
EN_Network *net = &p->network;
Spattern *Pattern = net->Pattern;
const int Npats = net->Npats;
if (!p->Openflag)
return (102);
if (index <= 0 || index > Npats)
return (205);
if (period <= 0 || period > Pattern[index].Length)
return (251);
Pattern[index].F[period - 1] = value;
return (0);
}
int DLLEXPORT EN_addcurve(EN_ProjectHandle ph, char *id) {
EN_Project *p = (EN_Project*)ph;
EN_Network *net = &p->network;
parser_data_t *par = &p->parser;
Scurve *Curve = net->Curve;
int i, j, n, err = 0;
Scurve *tmpCur;
/* Check if a curve with same id already exists */
if (!p->Openflag)
return (102);
if (ENgetcurveindex(id, &i) == 0)
return (215);
/* Check that id name is not too long */
if (strlen(id) > MAXID)
return (250);
/* Allocate memory for a new array of curves */
n = net->Ncurves + 1;
tmpCur = (Scurve *)calloc(n + 1, sizeof(Scurve));
if (tmpCur == NULL)
return (101);
/* Copy contents of old curve array to new one */
for (i = 0; i <= net->Ncurves; i++) {
strcpy(tmpCur[i].ID, net->Curve[i].ID);
tmpCur[i].Npts = Curve[i].Npts;
tmpCur[i].X = (double *)calloc(net->Curve[i].Npts, sizeof(double));
tmpCur[i].Y = (double *)calloc(net->Curve[i].Npts, sizeof(double));
if (tmpCur[i].X == NULL)
err = 1;
else if (tmpCur[i].Y == NULL)
err = 1;
else
for (j = 0; j < Curve[i].Npts; j++) {
tmpCur[i].X[j] = net->Curve[i].X[j];
tmpCur[i].Y[j] = net->Curve[i].Y[j];
}
}
/* Add the new Curve to the new array of curves */
strcpy(tmpCur[n].ID, id);
tmpCur[n].Npts = 1;
tmpCur[n].Type = -1;
tmpCur[n].X = (double *)calloc(tmpCur[n].Npts, sizeof(double));
tmpCur[n].Y = (double *)calloc(tmpCur[n].Npts, sizeof(double));
if (tmpCur[n].X == NULL)
err = 1;
else if (tmpCur[n].Y == NULL)
err = 1;
else {
tmpCur[n].X[0] = 1.0;
tmpCur[n].Y[0] = 1.0;
}
/* Abort if memory allocation error */
if (err) {
for (i = 0; i <= n; i++) {
if (tmpCur[i].X)
free(tmpCur[i].X);
if (tmpCur[i].Y)
free(tmpCur[i].Y);
}
free(tmpCur);
return (101);
}
// Replace old curves array with new one
for (i = 0; i <= net->Ncurves; i++) {
free(net->Curve[i].X);
free(net->Curve[i].Y);
}
free(net->Curve);
net->Curve = tmpCur;
net->Ncurves = n;
par->MaxCurves = n;
return 0;
}
int DLLEXPORT EN_setcurve(EN_ProjectHandle ph, int index, EN_API_FLOAT_TYPE *x, EN_API_FLOAT_TYPE *y, int n) {
EN_Project *p = (EN_Project*)ph;
EN_Network *net = &p->network;
Scurve *Curve = net->Curve;
int j;
/* Check for valid arguments */
if (!p->Openflag)
return (102);
if (index <= 0 || index > net->Ncurves)
return (206);
if (n <= 0)
return (202);
/* Re-set number of points & reallocate memory for values */
Curve[index].Npts = n;
Curve[index].X = (double *)realloc(Curve[index].X, n * sizeof(double));
Curve[index].Y = (double *)realloc(Curve[index].Y, n * sizeof(double));
if (Curve[index].X == NULL)
return (101);
if (Curve[index].Y == NULL)
return (101);
/* Load values into curve */
for (j = 0; j < n; j++) {
Curve[index].X[j] = x[j];
Curve[index].Y[j] = y[j];
}
return (0);
}
int DLLEXPORT EN_setcurvevalue(EN_ProjectHandle ph, int index, int pnt, EN_API_FLOAT_TYPE x, EN_API_FLOAT_TYPE y) {
EN_Project *p = (EN_Project*)ph;
EN_Network *net = &p->network;
Scurve *Curve = net->Curve;
const int Ncurves = net->Ncurves;
if (!p->Openflag)
return (102);
if (index <= 0 || index > Ncurves)
return (206);
if (pnt <= 0 || pnt > Curve[index].Npts)
return (251);
Curve[index].X[pnt - 1] = x;
Curve[index].Y[pnt - 1] = y;
return (0);
}
int DLLEXPORT EN_settimeparam(EN_ProjectHandle ph, int code, long value)
{
EN_Project *p = (EN_Project*)ph;
report_options_t *rep = &p->report;
quality_t *qu = &p->quality;
time_options_t *time = &p->time_options;
if (!p->Openflag)
return (102);
if (p->hydraulics.OpenHflag || p->quality.OpenQflag) {
// --> there's nothing wrong with changing certain time parameters during a
// simulation run, or before the run has started.
// todo -- how to tell?
/*
if (code == EN_DURATION || code == EN_HTIME || code == EN_REPORTSTEP ||
code == EN_DURATION || Htime == 0) {
// it's ok
}
else {
return(109);
}
*/
}
if (value < 0)
return (202);
switch (code) {
case EN_DURATION:
time->Dur = value;
if (time->Rstart > time->Dur)
time->Rstart = 0;
break;
case EN_HYDSTEP:
if (value == 0)
return (202);
time->Hstep = value;
time->Hstep = MIN(time->Pstep, time->Hstep);
time->Hstep = MIN(time->Rstep, time->Hstep);
qu->Qstep = MIN(qu->Qstep, time->Hstep);
break;
case EN_QUALSTEP:
if (value == 0)
return (202);
qu->Qstep = value;
qu->Qstep = MIN(qu->Qstep, time->Hstep);
break;
case EN_PATTERNSTEP:
if (value == 0)
return (202);
time->Pstep = value;
if (time->Hstep > time->Pstep)
time->Hstep = time->Pstep;
break;
case EN_PATTERNSTART:
time->Pstart = value;
break;
case EN_REPORTSTEP:
if (value == 0)
return (202);
time->Rstep = value;
if (time->Hstep > time->Rstep)
time->Hstep = time->Rstep;
break;
case EN_REPORTSTART:
if (time->Rstart > time->Dur)
return (202);
time->Rstart = value;
break;
case EN_RULESTEP:
if (value == 0)
return (202);
time->Rulestep = value;
time->Rulestep = MIN(time->Rulestep, time->Hstep);
break;
case EN_STATISTIC:
if (value > RANGE)
return (202);
rep->Tstatflag = (char)value;
break;
case EN_HTIME:
time->Htime = value;
break;
case EN_QTIME:
qu->Qtime = value;
break;
default:
return (251);
}
return (0);
}
int DLLEXPORT EN_setoption(EN_ProjectHandle ph, int code, EN_API_FLOAT_TYPE v)
/*----------------------------------------------------------------
** Input: code = option code (see EPANET2.H)
** v = option value
** Output: none
** Returns: error code
** Purpose: sets value for an analysis option
**----------------------------------------------------------------
*/
{
EN_Project *p = (EN_Project*)ph;
EN_Network *net = &p->network;
hydraulics_t *hyd = &p->hydraulics;
quality_t *qu = &p->quality;
Snode *Node = net->Node;
const int Njuncs = net->Njuncs;
double *Ucf = p->Ucf;
int i, j;
double Ke, n, ucf, value = v;
if (!p->Openflag)
return (102);
switch (code) {
case EN_TRIALS:
if (value < 1.0)
return (202);
hyd->MaxIter = (int)value;
break;
case EN_ACCURACY:
if (value < 1.e-5 || value > 1.e-1)
return (202);
hyd->Hacc = value;
break;
case EN_TOLERANCE:
if (value < 0.0)
return (202);
qu->Ctol = value / Ucf[QUALITY];
break;
case EN_EMITEXPON:
if (value <= 0.0)
return (202);
n = 1.0 / value;
ucf = pow(Ucf[FLOW], n) / Ucf[PRESSURE];
for (i = 1; i <= Njuncs; i++) {
j = ENgetnodevalue(i, EN_EMITTER, &v);
Ke = v;
if (j == 0 && Ke > 0.0)
Node[i].Ke = ucf / pow(Ke, n);
}
hyd->Qexp = n;
break;
case EN_DEMANDMULT:
if (value <= 0.0)
return (202);
hyd->Dmult = value;
break;
case EN_HEADERROR:
if (value < 0.0)
return (202);
hyd->HeadErrorLimit = value / Ucf[HEAD];
break;
case EN_FLOWCHANGE:
if (value < 0.0)
return (202);
hyd->FlowChangeLimit = value / Ucf[FLOW];
break;
default:
return (251);
}
return (0);
}
int DLLEXPORT EN_setstatusreport(EN_ProjectHandle ph, int code) {
int errcode = 0;
EN_Project *p = (EN_Project*)ph;
if (code >= 0 && code <= 2)
p->report.Statflag = (char)code;
else
errcode = 202;
return (errcode);
}
int DLLEXPORT EN_setqualtype(EN_ProjectHandle ph, int qualcode, char *chemname, char *chemunits, char *tracenode) {
EN_Project *p = (EN_Project*)ph;
EN_Network *net = &p->network;
report_options_t *rep = &p->report;
quality_t *qu = &p->quality;
double *Ucf = p->Ucf;
int i;
/*** Updated 3/1/01 ***/
double ccf = 1.0;
if (!p->Openflag)
return (102);
if (qualcode < EN_NONE || qualcode > EN_TRACE)
return (251);
qu->Qualflag = (char)qualcode;
qu->Ctol *= Ucf[QUALITY];
if (qu->Qualflag == CHEM) /* Chemical constituent */
{
strncpy(qu->ChemName, chemname, MAXID);
strncpy(qu->ChemUnits, chemunits, MAXID);
/*** Updated 3/1/01 ***/
strncpy(rep->Field[QUALITY].Units, qu->ChemUnits, MAXID);
strncpy(rep->Field[REACTRATE].Units, qu->ChemUnits, MAXID);
strcat(rep->Field[REACTRATE].Units, t_PERDAY);
ccf = 1.0 / LperFT3;
}
if (qu->Qualflag == TRACE) /* Source tracing option */
{
qu->TraceNode = findnode(net,tracenode);
if (qu->TraceNode == 0)
return (203);
strncpy(qu->ChemName, u_PERCENT, MAXID);
strncpy(qu->ChemUnits, tracenode, MAXID);
/*** Updated 3/1/01 ***/
strcpy(rep->Field[QUALITY].Units, u_PERCENT);
}
if (qu->Qualflag == AGE) /* Water age analysis */
{
strncpy(qu->ChemName, w_AGE, MAXID);
strncpy(qu->ChemUnits, u_HOURS, MAXID);
/*** Updated 3/1/01 ***/
strcpy(rep->Field[QUALITY].Units, u_HOURS);
}
/* when changing from CHEM to AGE or TRACE, nodes initial quality values must be returned to their original ones */
if ((qu->Qualflag == AGE || qu->Qualflag == TRACE) & (Ucf[QUALITY] != 1)) {
for (i=1; i<=p->network.Nnodes; i++) {
p->network.Node[i].C0 *= Ucf[QUALITY];
}
}
/*** Updated 3/1/01 ***/
Ucf[QUALITY] = ccf;
Ucf[LINKQUAL] = ccf;
Ucf[REACTRATE] = ccf;
qu->Ctol /= Ucf[QUALITY];
return (0);
}
int DLLEXPORT EN_getheadcurveindex(EN_ProjectHandle ph, int index, int *curveindex) {
EN_Project *p = (EN_Project*)ph;
EN_Network *net = &p->network;
Slink *Link = net->Link;
Spump *Pump = net->Pump;
const int Nlinks = net->Nlinks;
if (!p->Openflag)
return (102);
if (index < 1 || index > Nlinks || EN_PUMP != Link[index].Type)
return (204);
*curveindex = Pump[findpump(net, index)].Hcurve;
return (0);
}
int DLLEXPORT EN_setheadcurveindex(EN_ProjectHandle ph, int index, int curveindex) {
EN_Project *p = (EN_Project*)ph;
EN_Network *net = &p->network;
Slink *Link = net->Link;
const int Nlinks = net->Nlinks;
const int Ncurves = net->Ncurves;
double *Ucf = p->Ucf;
int pIdx;
Spump *pump;
if (!p->Openflag)
return (102);
if (index < 1 || index > Nlinks || EN_PUMP != Link[index].Type) {
return (204);
}
if (curveindex <= 0 || curveindex > Ncurves) {
return (206);
}
pIdx = findpump(net, index);
pump = &p->network.Pump[pIdx];
pump->Ptype = NOCURVE;
pump->Hcurve = curveindex;
// update pump parameters
getpumpparams(p);
// convert units
if (pump->Ptype == POWER_FUNC) {
pump->H0 /= Ucf[HEAD];
pump->R *= (pow(Ucf[FLOW], pump->N) / Ucf[HEAD]);
}
/* Convert flow range & max. head units */
pump->Q0 /= Ucf[FLOW];
pump->Qmax /= Ucf[FLOW];
pump->Hmax /= Ucf[HEAD];
return (0);
}
int DLLEXPORT EN_getpumptype(EN_ProjectHandle ph, int index, int *type) {
EN_Project *p = (EN_Project*)ph;
EN_Network *net = &p->network;
Slink *Link = net->Link;
Spump *Pump = net->Pump;
const int Nlinks = net->Nlinks;
*type = -1;
if (!p->Openflag)
return (102);
if (index < 1 || index > Nlinks || EN_PUMP != Link[index].Type)
return (204);
*type = Pump[findpump(&p->network, index)].Ptype;
return (0);
}
/*
----------------------------------------------------------------
Functions for opening files
----------------------------------------------------------------
*/
int openfiles(EN_Project *p, const char *f1, const char *f2, const char *f3)
/*----------------------------------------------------------------
** Input: f1 = pointer to name of input file
** f2 = pointer to name of report file
** f3 = pointer to name of binary output file
** Output: none
** Returns: error code
** Purpose: opens input & report files
**----------------------------------------------------------------
*/
{
out_file_t *out = &p->out_files;
report_options_t *rep = &p->report;
parser_data_t *par = &p->parser;
/* Initialize file pointers to NULL */
par->InFile = NULL;
rep->RptFile = NULL;
out->OutFile = NULL;
out->HydFile = NULL;
/* Save file names */
strncpy(par->InpFname, f1, MAXFNAME);
strncpy(rep->Rpt1Fname, f2, MAXFNAME);
strncpy(out->OutFname, f3, MAXFNAME);
if (strlen(f3) > 0)
out->Outflag = SAVE;
else
out->Outflag = SCRATCH;
/* Check that file names are not identical */
if (strcomp(f1, f2) || strcomp(f1, f3) ||
(strcomp(f2, f3) && (strlen(f2) > 0 || strlen(f3) > 0))) {
writecon(FMT04);
return (301);
}
/* Attempt to open input and report files */
if ((par->InFile = fopen(f1, "rt")) == NULL) {
writecon(FMT05);
writecon(f1);
return (302);
}
if (strlen(f2) == 0)
rep->RptFile = stdout;
else if ((rep->RptFile = fopen(f2, "wt")) == NULL) {
writecon(FMT06);
return (303);
}
return (0);
} /* End of openfiles */
int openhydfile(EN_Project *p)
/*----------------------------------------------------------------
** Input: none
** Output: none
** Returns: error code
** Purpose: opens file that saves hydraulics solution
**----------------------------------------------------------------
*/
{
EN_Network *net = &p->network;
out_file_t *out = &p->out_files;
time_options_t *time = &p->time_options;
const int Nnodes = net->Nnodes;
const int Ntanks = net->Ntanks;
const int Nlinks = net->Nlinks;
const int Nvalves = net->Nvalves;
const int Npumps = net->Npumps;
INT4 nsize[6]; /* Temporary array */
INT4 magic;
INT4 version;
int errcode = 0;
/* If HydFile currently open, then close it if its not a scratch file */
if (out->HydFile != NULL) {
if (out->Hydflag == SCRATCH)
return (0);
fclose(out->HydFile);
}
/* Use Hydflag to determine the type of hydraulics file to use. */
/* Write error message if the file cannot be opened. */
out->HydFile = NULL;
switch (out->Hydflag) {
case SCRATCH:
getTmpName(p, out->HydFname);
out->HydFile = fopen(out->HydFname, "w+b");
break;
case SAVE:
out->HydFile = fopen(out->HydFname, "w+b");
break;
case USE:
out->HydFile = fopen(out->HydFname, "rb");
break;
}
if (out->HydFile == NULL)
return (305);
/* If a previous hydraulics solution is not being used, then */
/* save the current network size parameters to the file. */
if (out->Hydflag != USE) {
magic = MAGICNUMBER;
version = ENGINE_VERSION;
nsize[0] = Nnodes;
nsize[1] = Nlinks;
nsize[2] = Ntanks;
nsize[3] = Npumps;
nsize[4] = Nvalves;
nsize[5] = (int)time->Dur;
fwrite(&magic, sizeof(INT4), 1, out->HydFile);
fwrite(&version, sizeof(INT4), 1, out->HydFile);
fwrite(nsize, sizeof(INT4), 6, out->HydFile);
}
/* If a previous hydraulics solution is being used, then */
/* make sure its network size parameters match those of */
/* the current network. */
if (out->Hydflag == USE) {
fread(&magic, sizeof(INT4), 1, out->HydFile);
if (magic != MAGICNUMBER)
return (306);
fread(&version, sizeof(INT4), 1, out->HydFile);
if (version != ENGINE_VERSION)
return (306);
if (fread(nsize, sizeof(INT4), 6, out->HydFile) < 6)
return (306);
if (nsize[0] != Nnodes || nsize[1] != Nlinks || nsize[2] != Ntanks ||
nsize[3] != Npumps || nsize[4] != Nvalves || nsize[5] != time->Dur)
return (306);
p->save_options.SaveHflag = TRUE;
}
/* Save current position in hydraulics file */
/* where storage of hydraulic results begins */
out->HydOffset = ftell(out->HydFile);
return (errcode);
}
int openoutfile(EN_Project *p)
/*----------------------------------------------------------------
** Input: none
** Output: none
** Returns: error code
** Purpose: opens binary output file.
**----------------------------------------------------------------
*/
{
int errcode = 0;
out_file_t *out = &p->out_files;
report_options_t *rep = &p->report;
/* Close output file if already opened */
if (out->OutFile != NULL)
fclose(out->OutFile);
out->OutFile = NULL;
if (out->TmpOutFile != NULL)
fclose(out->TmpOutFile);
out->TmpOutFile = NULL;
if (out->Outflag == SCRATCH) {
remove(out->OutFname);
}
remove(out->TmpFname);
/* If output file name was supplied, then attempt to */
/* open it. Otherwise open a temporary output file. */
// if (strlen(OutFname) != 0)
if (out->Outflag == SAVE)
{
if ((out->OutFile = fopen(out->OutFname, "w+b")) == NULL) {
writecon(FMT07);
errcode = 304;
}
}
// else if ( (OutFile = tmpfile()) == NULL)
else
{
getTmpName(p, out->OutFname);
if ((out->OutFile = fopen(out->OutFname, "w+b")) == NULL)
{
writecon(FMT08);
errcode = 304;
}
}
/* Save basic network data & energy usage results */
ERRCODE(savenetdata(p));
out->OutOffset1 = ftell(out->OutFile);
ERRCODE(saveenergy(p));
out->OutOffset2 = ftell(out->OutFile);
/* Open temporary file if computing time series statistic */
if (!errcode) {
if (rep->Tstatflag != SERIES) {
// if ( (TmpOutFile = tmpfile()) == NULL) errcode = 304;
getTmpName(p, out->TmpFname);
out->TmpOutFile = fopen(out->TmpFname, "w+b");
if (out->TmpOutFile == NULL)
errcode = 304;
} else
out->TmpOutFile = out->OutFile;
}
return (errcode);
}
/*
----------------------------------------------------------------
Global memory management functions
----------------------------------------------------------------
*/
void initpointers(EN_Project *p)
/*----------------------------------------------------------------
** Input: none
** Output: none
** Purpose: initializes global pointers to NULL
**----------------------------------------------------------------
*/
{
hydraulics_t *hyd = &p->hydraulics;
quality_t *q = &p->quality;
EN_Network *n = &p->network;
parser_data_t *pars = &p->parser;
solver_t *s = &p->hydraulics.solver;
hyd->NodeDemand = NULL;
q->NodeQual = NULL;
hyd->NodeHead = NULL;
hyd->LinkFlows = NULL;
q->PipeRateCoeff = NULL;
hyd->LinkStatus = NULL;
hyd->LinkSetting = NULL;
hyd->OldStat = NULL;
n->Node = NULL;
n->Link = NULL;
n->Tank = NULL;
n->Pump = NULL;
n->Valve = NULL;
n->Pattern = NULL;
n->Curve = NULL;
n->Control = NULL;
n->Coord = NULL;
hyd->X_tmp = NULL;
pars->Patlist = NULL;
pars->Curvelist = NULL;
n->Adjlist = NULL;
s->Aii = NULL;
s->Aij = NULL;
s->F = NULL;
s->P = NULL;
s->Y = NULL;
s->Order = NULL;
s->Row = NULL;
s->Ndx = NULL;
s->XLNZ = NULL;
s->NZSUB = NULL;
s->LNZ = NULL;
n->NodeHashTable = NULL;
n->LinkHashTable = NULL;
initrules(&p->rules);
}
int allocdata(EN_Project *p)
/*----------------------------------------------------------------
** Input: none
** Output: none
** Returns: error code
** Purpose: allocates memory for network data structures
**----------------------------------------------------------------
*/
{
int n;
int errcode = 0;
EN_Network *net;
hydraulics_t *hyd;
quality_t *qu;
parser_data_t *par;
/* Allocate node & link ID hash tables */
p->network.NodeHashTable = ENHashTableCreate();
p->network.LinkHashTable = ENHashTableCreate();
ERRCODE(MEMCHECK(p->network.NodeHashTable));
ERRCODE(MEMCHECK(p->network.LinkHashTable));
net = &p->network;
hyd = &p->hydraulics;
qu = &p->quality;
par = &p->parser;
/* Allocate memory for network nodes */
/*************************************************************
NOTE: Because network components of a given type are indexed
starting from 1, their arrays must be sized 1
element larger than the number of components.
*************************************************************/
if (!errcode) {
n = par->MaxNodes + 1;
net->Node = (Snode *)calloc(n, sizeof(Snode));
hyd->NodeDemand = (double *)calloc(n, sizeof(double));
qu->NodeQual = (double *)calloc(n, sizeof(double));
hyd->NodeHead = (double *)calloc(n, sizeof(double));
ERRCODE(MEMCHECK(net->Node));
ERRCODE(MEMCHECK(hyd->NodeDemand));
ERRCODE(MEMCHECK(qu->NodeQual));
ERRCODE(MEMCHECK(hyd->NodeHead));
}
/* Allocate memory for network links */
if (!errcode) {
n = par->MaxLinks + 1;
net->Link = (Slink *)calloc(n, sizeof(Slink));
hyd->LinkFlows = (double *)calloc(n, sizeof(double));
hyd->LinkSetting = (double *)calloc(n, sizeof(double));
hyd->LinkStatus = (StatType *)calloc(n, sizeof(StatType));
ERRCODE(MEMCHECK(net->Link));
ERRCODE(MEMCHECK(hyd->LinkFlows));
ERRCODE(MEMCHECK(hyd->LinkSetting));
ERRCODE(MEMCHECK(hyd->LinkStatus));
}
/* Allocate memory for tanks, sources, pumps, valves, */
/* controls, demands, time patterns, & operating curves */
if (!errcode) {
net->Tank = (Stank *)calloc(par->MaxTanks + 1, sizeof(Stank));
net->Pump = (Spump *)calloc(par->MaxPumps + 1, sizeof(Spump));
net->Valve = (Svalve *)calloc(par->MaxValves + 1, sizeof(Svalve));
net->Control = (Scontrol *)calloc(par->MaxControls + 1, sizeof(Scontrol));
net->Pattern = (Spattern *)calloc(par->MaxPats + 1, sizeof(Spattern));
net->Curve = (Scurve *)calloc(par->MaxCurves + 1, sizeof(Scurve));
if (p->parser.Coordflag == TRUE) {
net->Coord = (Scoord *)calloc(par->MaxNodes + 1, sizeof(Scoord));
}
ERRCODE(MEMCHECK(net->Tank));
ERRCODE(MEMCHECK(net->Pump));
ERRCODE(MEMCHECK(net->Valve));
ERRCODE(MEMCHECK(net->Control));
ERRCODE(MEMCHECK(net->Pattern));
ERRCODE(MEMCHECK(net->Curve));
if (p->parser.Coordflag == TRUE)
ERRCODE(MEMCHECK(net->Coord));
}
/* Initialize pointers used in patterns, curves, and demand category lists */
if (!errcode) {
for (n = 0; n <= par->MaxPats; n++) {
net->Pattern[n].Length = 0;
net->Pattern[n].F = NULL;
}
for (n = 0; n <= par->MaxCurves; n++) {
net->Curve[n].Npts = 0;
net->Curve[n].Type = -1;
net->Curve[n].X = NULL;
net->Curve[n].Y = NULL;
}
for (n = 0; n <= par->MaxNodes; n++) {
// node demand
net->Node[n].D = NULL;
/* ini coord data */
if (p->parser.Coordflag == TRUE) {
net->Coord[n].X = 0;
net->Coord[n].Y = 0;
net->Coord[n].HaveCoords = FALSE;
}
}
}
/* Allocate memory for rule base (see RULES.C) */
if (!errcode)
errcode = allocrules(p);
return (errcode);
} /* End of allocdata */
void freeTmplist(STmplist *t)
/*----------------------------------------------------------------
** Input: t = pointer to start of a temporary list
** Output: none
** Purpose: frees memory used for temporary storage
** of pattern & curve data
**----------------------------------------------------------------
*/
{
STmplist *tnext;
while (t != NULL) {
tnext = t->next;
freeFloatlist(t->x);
freeFloatlist(t->y);
free(t);
t = tnext;
}
}
void freeFloatlist(SFloatlist *f)
/*----------------------------------------------------------------
** Input: f = pointer to start of list of floats
** Output: none
** Purpose: frees memory used for storing list of floats
**----------------------------------------------------------------
*/
{
SFloatlist *fnext;
while (f != NULL) {
fnext = f->next;
free(f);
f = fnext;
}
}
void freedata(EN_Project *p)
/*----------------------------------------------------------------
** Input: none
** Output: none
** Purpose: frees memory allocated for network data structures.
**----------------------------------------------------------------
*/
{
EN_Network *net = &p->network;
hydraulics_t *hyd = &p->hydraulics;
quality_t *qu = &p->quality;
parser_data_t *par = &p->parser;
int j;
Pdemand demand, nextdemand;
Psource source;
/* Free memory for computed results */
free(hyd->NodeDemand);
free(qu->NodeQual);
free(hyd->NodeHead);
free(hyd->LinkFlows);
free(hyd->LinkSetting);
free(hyd->LinkStatus);
/* Free memory for node data */
if (net->Node != NULL) {
for (j = 0; j <= par->MaxNodes; j++) {
/* Free memory used for demand category list */
demand = net->Node[j].D;
while (demand != NULL) {
nextdemand = demand->next;
free(demand);
demand = nextdemand;
}
/* Free memory used for WQ source data */
source = net->Node[j].S;
if (source != NULL)
free(source);
}
free(net->Node);
}
/* Free memory for other network objects */
free(net->Link);
free(net->Tank);
free(net->Pump);
free(net->Valve);
free(net->Control);
/* Free memory for time patterns */
if (net->Pattern != NULL) {
for (j = 0; j <= par->MaxPats; j++)
free(net->Pattern[j].F);
free(net->Pattern);
}
/* Free memory for curves */
if (net->Curve != NULL) {
for (j = 0; j <= par->MaxCurves; j++) {
free(net->Curve[j].X);
free(net->Curve[j].Y);
}
free(net->Curve);
}
/* Free memory for node coordinates */
if (p->parser.Coordflag == TRUE) {
free(net->Coord);
}
/* Free memory for rule base (see RULES.C) */
freerules(p);
/* Free hash table memory */
if (net->NodeHashTable != NULL)
ENHashTableFree(net->NodeHashTable);
if (net->LinkHashTable != NULL)
ENHashTableFree(net->LinkHashTable);
}
/*
----------------------------------------------------------------
General purpose functions
----------------------------------------------------------------
*/
char *getTmpName(EN_Project *p, char *fname)
//
// Input: fname = file name string
// Output: returns pointer to file name
// Purpose: creates a temporary file name with path prepended to it.
//
{
#ifdef _WIN32
char* name = NULL;
// --- use Windows _tempnam function to get a pointer to an
// unused file name that begins with "en"
name = _tempnam(NULL, "en");
if (name == NULL) return NULL;
// --- copy the file name to fname
if (strlen(name) < MAXFNAME) strncpy(fname, name, MAXFNAME);
else fname = NULL;
// --- free the pointer returned by _tempnam
if (name) free(name);
/*
/////////////////// DEPRECATED /////////////////////////////////////
// --- for Windows systems:
#ifdef WINDOWS
// --- use system function tmpnam() to create a temporary file name
char name[MAXFNAME + 1];
int n;
tmpnam(name);
// --- if user supplied the name of a temporary directory,
// then make it be the prefix of the full file name
n = (int)strlen(out->TmpDir);
if (n > 0) {
strcpy(fname, out->TmpDir);
if (fname[n - 1] != '\\')
strcat(fname, "\\");
}
// --- otherwise, use the relative path notation as the file name
// prefix so that the file will be placed in the current directory
else {
strcpy(fname, ".\\");
}
// --- now add the prefix to the file name
strcat(fname, name);
*/
// --- for non-Windows systems:
#else
// --- use system function mkstemp() to create a temporary file name
strcpy(fname, "enXXXXXX");
mkstemp(fname);
#endif
return fname;
}
int strcomp(const char *s1, const char *s2)
/*---------------------------------------------------------------
** Input: s1 = character string
** s2 = character string
** Output: none
** Returns: 1 if s1 is same as s2, 0 otherwise
** Purpose: case insensitive comparison of strings s1 & s2
**---------------------------------------------------------------
*/
{
int i;
for (i = 0; UCHAR(s1[i]) == UCHAR(s2[i]); i++)
if (!s1[i + 1] && !s2[i + 1])
return (1);
return (0);
} /* End of strcomp */
double interp(int n, double x[], double y[], double xx)
/*----------------------------------------------------------------
** Input: n = number of data pairs defining a curve
** x = x-data values of curve
** y = y-data values of curve
** xx = specified x-value
** Output: none
** Returns: y-value on curve at x = xx
** Purpose: uses linear interpolation to find y-value on a
** data curve corresponding to specified x-value.
** NOTE: does not extrapolate beyond endpoints of curve.
**----------------------------------------------------------------
*/
{
int k, m;
double dx, dy;
m = n - 1; /* Highest data index */
if (xx <= x[0])
return (y[0]); /* xx off low end of curve */
for (k = 1; k <= m; k++) /* Bracket xx on curve */
{
if (x[k] >= xx) /* Interp. over interval */
{
dx = x[k] - x[k - 1];
dy = y[k] - y[k - 1];
if (ABS(dx) < TINY)
return (y[k]);
else
return (y[k] - (x[k] - xx) * dy / dx);
}
}
return (y[m]); /* xx off high end of curve */
} /* End of interp */
int findnode(EN_Network *n, char *id)
/*----------------------------------------------------------------
** Input: id = node ID
** Output: none
** Returns: index of node with given ID, or 0 if ID not found
** Purpose: uses hash table to find index of node with given ID
**----------------------------------------------------------------
*/
{
return (ENHashTableFind(n->NodeHashTable, id));
}
int findlink(EN_Network *n, char *id)
/*----------------------------------------------------------------
** Input: id = link ID
** Output: none
** Returns: index of link with given ID, or 0 if ID not found
** Purpose: uses hash table to find index of link with given ID
**----------------------------------------------------------------
*/
{
return (ENHashTableFind(n->LinkHashTable, id));
}
int findtank(EN_Network *n, int index)
/*----------------------------------------------------------------
** Input: index = node index
** Output: none
** Returns: index of tank with given node id, or NOTFOUND if tank not found
** Purpose: for use in the deletenode function
**----------------------------------------------------------------
*/
{
int i;
for (i = 1; i <= n->Ntanks; i++) {
if (n->Tank[i].Node == index) {
return (i);
}
}
return (NOTFOUND);
}
int findpump(EN_Network *n, int index)
/*----------------------------------------------------------------
** Input: index = link ID
** Output: none
** Returns: index of pump with given link id, or NOTFOUND if pump not found
** Purpose: for use in the deletelink function
**----------------------------------------------------------------
*/
{
int i;
for (i = 1; i <= n->Npumps; i++) {
if (n->Pump[i].Link == index) {
return (i);
}
}
return (NOTFOUND);
}
int findvalve(EN_Network *n, int index)
/*----------------------------------------------------------------
** Input: index = link ID
** Output: none
** Returns: index of valve with given link id, or NOTFOUND if valve not found
** Purpose: for use in the deletelink function
**----------------------------------------------------------------
*/
{
int i;
for (i = 1; i <= n->Nvalves; i++) {
if (n->Valve[i].Link == index) {
return (i);
}
}
return (NOTFOUND);
}
char *geterrmsg(int errcode, char *msg)
/*----------------------------------------------------------------
** Input: errcode = error code
** Output: none
** Returns: pointer to string with error message
** Purpose: retrieves text of error message
**----------------------------------------------------------------
*/
{
switch (errcode) { /* Warnings */
#define DAT(code,enumer,string) case code: strcpy(msg, string); break;
#include "errors.dat"
#undef DAT
default:
strcpy(msg, "");
}
return (msg);
}
void errmsg(EN_Project *p, int errcode)
/*----------------------------------------------------------------
** Input: errcode = error code
** Output: none
** Purpose: writes error message to report file
**----------------------------------------------------------------
*/
{
if (errcode == 309) /* Report file write error - */
{ /* Do not write msg to file. */
writecon("\n ");
writecon(geterrmsg(errcode,p->Msg));
} else if (p->report.RptFile != NULL && p->report.Messageflag) {
writeline(p, geterrmsg(errcode,p->Msg));
}
}
void writecon(const char *s)
/*----------------------------------------------------------------
** Input: text string
** Output: none
** Purpose: writes string of characters to console
**----------------------------------------------------------------
*/
{
fprintf(stdout, "%s", s);
fflush(stdout);
}
void writewin(void (*vp)(char *), char *s)
/*----------------------------------------------------------------
** Input: text string
** Output: none
** Purpose: passes character string to viewprog() in
** application which calls the EPANET DLL
**----------------------------------------------------------------
*/
{
char progmsg[MAXMSG + 1];
if (vp != NULL) {
strncpy(progmsg, s, MAXMSG);
vp(progmsg);
}
}
int DLLEXPORT EN_getnumdemands(EN_ProjectHandle ph, int nodeIndex, int *numDemands) {
Pdemand d;
int n = 0;
EN_Project *p = (EN_Project*)ph;
/* Check for valid arguments */
if (!p->Openflag)
return (102);
if (nodeIndex <= 0 || nodeIndex > p->network.Nnodes)
return (203);
for (d = p->network.Node[nodeIndex].D; d != NULL; d = d->next)
n++;
*numDemands = n;
return 0;
}
int DLLEXPORT EN_getbasedemand(EN_ProjectHandle ph, int nodeIndex, int demandIdx, EN_API_FLOAT_TYPE *baseDemand) {
Pdemand d;
int n = 1;
EN_Project *p = (EN_Project*)ph;
/* Check for valid arguments */
if (!p->Openflag)
return (102);
if (nodeIndex <= 0 || nodeIndex > p->network.Nnodes)
return (203);
if (nodeIndex <= p->network.Njuncs) {
for (d = p->network.Node[nodeIndex].D; n < demandIdx && d != NULL; d = d->next) {
n++;
}
if (n != demandIdx) {
return (253);
}
*baseDemand = (EN_API_FLOAT_TYPE)(d->Base * p->Ucf[FLOW]);
} else {
*baseDemand = (EN_API_FLOAT_TYPE)(0.0);
}
return 0;
}
int DLLEXPORT EN_setbasedemand(EN_ProjectHandle ph, int nodeIndex, int demandIdx, EN_API_FLOAT_TYPE baseDemand) {
EN_Project *pr = (EN_Project*)ph;
EN_Network *net = &pr->network;
Snode *Node = net->Node;
const int Nnodes = net->Nnodes;
const int Njuncs = net->Njuncs;
double *Ucf = pr->Ucf;
Pdemand d;
int n = 1;
/* Check for valid arguments */
if (!pr->Openflag)
return (102);
if (nodeIndex <= 0 || nodeIndex > Nnodes)
return (203);
if (nodeIndex <= Njuncs) {
for (d = Node[nodeIndex].D; n < demandIdx && d != NULL; d = d->next)
n++;
if (n != demandIdx)
return (253);
d->Base = baseDemand / Ucf[FLOW];
}
return 0;
}
int DLLEXPORT EN_getdemandpattern(EN_ProjectHandle ph, int nodeIndex, int demandIdx, int *pattIdx) {
EN_Project *p = (EN_Project*)ph;
EN_Network *net = &p->network;
Snode *Node = net->Node;
const int Nnodes = net->Nnodes;
Pdemand d;
int n = 1;
/* Check for valid arguments */
if (!p->Openflag)
return (102);
if (nodeIndex <= 0 || nodeIndex > Nnodes)
return (203);
for (d = Node[nodeIndex].D; n < demandIdx && d != NULL; d = d->next)
n++;
if (n != demandIdx)
return (253);
*pattIdx = d->Pat;
return 0;
}
int DLLEXPORT EN_getaveragepatternvalue(EN_ProjectHandle ph, int index, EN_API_FLOAT_TYPE *value) {
EN_Project *p = (EN_Project*)ph;
EN_Network *net = &p->network;
Spattern *Pattern = net->Pattern;
const int Npats = net->Npats;
int i;
*value = 0.0;
if (!p->Openflag)
return (102);
if (index < 1 || index > Npats)
return (205);
// if (period < 1 || period > Pattern[index].Length) return(251);
for (i = 0; i < Pattern[index].Length; i++) {
*value += (EN_API_FLOAT_TYPE)Pattern[index].F[i];
}
*value /= (EN_API_FLOAT_TYPE)Pattern[index].Length;
return (0);
}
int DLLEXPORT EN_setlinktype(EN_ProjectHandle ph, char *id, EN_LinkType toType) {
int i;
EN_LinkType fromType;
EN_Project *p = (EN_Project*)ph;
EN_Network *net = &p->network;
if (!p->Openflag)
return (102);
/* Check if a link with the id exists */
if (EN_getlinkindex(p, id, &i) != 0)
return (215);
/* Get the current type of the link */
ENgetlinktype(i, &fromType);
if (fromType == toType)
return (0);
/* Change link from Pipe */
if (toType <= EN_PIPE) {
net->Npipes++;
} else if (toType == EN_PUMP) {
net->Npumps++;
net->Pump[net->Npumps].Link = i;
} else {
net->Nvalves++;
net->Valve[net->Nvalves].Link = i;
}
if (fromType <= EN_PIPE) {
net->Npipes--;
} else if (fromType == EN_PUMP) {
net->Npumps--;
}
return 0;
}
int DLLEXPORT EN_addnode(EN_ProjectHandle ph, char *id, EN_NodeType nodeType) {
int i, nIdx;
int index;
struct Sdemand *demand;
EN_Project *p = (EN_Project*)ph;
EN_Network *net = &p->network;
hydraulics_t *hyd = &p->hydraulics;
quality_t *qu = &p->quality;
Stank *tank;
Snode *node;
Scoord *coord;
/* Check if a node with same id already exists */
if (!p->Openflag)
return (102);
if (ENgetnodeindex(id, &i) == 0)
return (215);
/* Check that id name is not too long */
if (strlen(id) > MAXID)
return (250);
/* Grow arrays to accomodate the new values */
net->Node = (Snode *)realloc(net->Node, (net->Nnodes + 2) * sizeof(Snode));
net->Coord = realloc(net->Coord, (net->Nnodes + 2) * sizeof(Scoord));
hyd->NodeDemand = (double *)realloc(hyd->NodeDemand, (net->Nnodes + 2) * sizeof(double));
qu->NodeQual = (double *)realloc(qu->NodeQual, (net->Nnodes + 2) * sizeof(double));
hyd->NodeHead = (double *)realloc(hyd->NodeHead, (net->Nnodes + 2) * sizeof(double));
if (nodeType == EN_JUNCTION) {
net->Njuncs++;
nIdx = net->Njuncs;
node = &net->Node[nIdx];
coord = &net->Coord[nIdx];
demand = (struct Sdemand *)malloc(sizeof(struct Sdemand));
demand->Base = 0.0;
demand->Pat = hyd->DefPat; // Use default pattern
demand->next = NULL;
node->D = demand;
// shift rest of Node array
for (index = net->Nnodes; index >= net->Njuncs; index--) {
ENHashTableUpdate(net->NodeHashTable, net->Node[index].ID, index + 1);
net->Node[index + 1] = net->Node[index];
net->Coord[index + 1] = net->Coord[index];
}
// shift indices of Tank array
for (index = 1; index <= net->Ntanks; index++) {
net->Tank[index].Node += 1;
}
// shift indices of Links, if necessary
for (index = 1; index <= net->Nlinks; index++) {
if (net->Link[index].N1 > net->Njuncs - 1) {
net->Link[index].N1 += 1;
}
if (net->Link[index].N2 > net->Njuncs - 1) {
net->Link[index].N2 += 1;
}
}
} else {
nIdx = net->Nnodes++;
node = &net->Node[nIdx];
coord = &net->Coord[nIdx];
net->Ntanks++;
/* resize tanks array */
net->Tank = (Stank *)realloc(net->Tank, (net->Ntanks + 1) * sizeof(Stank));
tank = &net->Tank[net->Ntanks];
/* set default values for new tank or reservoir */
tank->Node = nIdx;
tank->Pat = 0;
if (nodeType == EN_TANK) {
tank->A = 1.0;
} else {
tank->A = 0;
}
tank->Hmin = 0;
tank->Hmax = 0;
tank->H0 = 0;
tank->Vmin = 0;
tank->Vmax = 0;
tank->V0 = 0;
tank->Kb = 0;
tank->V = 0;
tank->C = 0;
tank->Pat = 0;
tank->Vcurve = 0;
tank->MixModel = 0;
tank->V1max = 10000;
}
net->Nnodes++;
/* set default values for new node */
strncpy(node->ID, id, MAXID);
node->El = 0;
node->S = NULL;
node->C0 = 0;
node->Ke = 0;
node->Rpt = 0;
coord->HaveCoords = FALSE;
coord->X = 0;
coord->Y = 0;
/* Insert new node into hash table */
ENHashTableInsert(net->NodeHashTable, node->ID, nIdx); /* see HASH.C */
return (0);
}
int DLLEXPORT EN_addlink(EN_ProjectHandle ph, char *id, EN_LinkType linkType, char *fromNode,
char *toNode) {
int i, n;
int N1, N2;
EN_Project *p = (EN_Project*)ph;
EN_Network *net = &p->network;
hydraulics_t *hyd = &p->hydraulics;
Slink *link;
Spump *pump;
/* Check if a link with same id already exists */
if (!p->Openflag)
return (102);
if (ENgetlinkindex(id, &i) == 0)
return (215);
/* Lookup the from and to nodes */
N1 = ENHashTableFind(net->NodeHashTable, fromNode);
N2 = ENHashTableFind(net->NodeHashTable, toNode);
if (N1 == 0 || N2 == 0) {
return (203);
}
/* Check that id name is not too long */
if (strlen(id) > MAXID)
return (250);
net->Nlinks++;
n = net->Nlinks;
/* Grow arrays to accomodate the new value */
net->Link = (Slink *)realloc(net->Link, (net->Nlinks + 1) * sizeof(Slink));
hyd->LinkFlows = (double *)realloc(hyd->LinkFlows, (net->Nlinks + 1) * sizeof(double));
hyd->LinkSetting = (double *)realloc(hyd->LinkSetting, (net->Nlinks + 1) * sizeof(double));
hyd->LinkStatus = (StatType *)realloc(hyd->LinkStatus, (net->Nlinks + 1) * sizeof(StatType));
link = &net->Link[net->Nlinks];
strncpy(net->Link[n].ID, id, MAXID);
if (linkType <= EN_PIPE) {
net->Npipes++;
} else if (linkType == EN_PUMP) {
net->Npumps++;
/* Grow pump array to accomodate the new value */
net->Pump = (Spump *)realloc(net->Pump, (net->Npumps + 1) * sizeof(Spump));
pump = &net->Pump[net->Npumps];
pump->Link = n;
pump->Ptype = 0;
pump->Q0 = 0;
pump->Qmax = 0;
pump->Hmax = 0;
pump->H0 = 0;
pump->R = 0;
pump->N = 0;
pump->Hcurve = 0;
pump->Ecurve = 0;
pump->Upat = 0;
pump->Epat = 0;
pump->Ecost = 0;
pump->Energy[5] = MISSING;
} else {
/* Grow valve array to accomodate the new value */
net->Nvalves++;
net->Valve = (Svalve *)realloc(net->Valve, (net->Nvalves + 1) * sizeof(Svalve));
net->Valve[net->Nvalves].Link = n;
}
link->Type = linkType;
link->N1 = N1;
link->N2 = N2;
if (linkType == EN_PUMP) {
link->Kc = 1.0; // Speed factor
link->Km = 0.0; // Horsepower
link->Len = 0.0;
} else if (linkType <= EN_PIPE) { // pipe or cvpipe
link->Diam = 10 / p->Ucf[DIAM];
link->Kc = 100; // Rough. coeff
link->Km = 0.0; // Loss coeff
link->Len = 1000;
} else { // Valve
link->Diam = 10 / p->Ucf[DIAM];
link->Kc = 0.0; // Valve setting.
link->Km = 0.0; // Loss coeff
link->Len = 0.0;
}
// link->Len = 0.0;
// link->Kc = 0.01;
// link->Km = 0;
link->Kb = 0;
link->Kw = 0;
link->R = 0;
link->Rc = 0;
link->Rpt = 0;
if (linkType == EN_CVPIPE) {
link->Stat = OPEN;
}
else {
link->Stat = CLOSED;
}
ENHashTableInsert(net->LinkHashTable, link->ID, n);
return (0);
}
int DLLEXPORT EN_deletelink(EN_ProjectHandle ph, int index) {
int i;
EN_LinkType linkType;
EN_Project *p = (EN_Project*)ph;
EN_Network *net = &p->network;
Slink *link;
if (!p->Openflag)
return (102);
if (index <= 0 || index > net->Nlinks)
return (203);
EN_getlinktype(p, index, &linkType);
link = &net->Link[index];
// remove from hash table
ENHashTableDelete(net->LinkHashTable, link->ID);
ENHashTableFind(net->LinkHashTable, link->ID);
// shift link and pump arrays to re-sort link indices
net->Nlinks--;
for (i = index; i <= net->Nlinks - 1; i++) {
net->Link[i] = net->Link[i + 1];
// update hashtable
ENHashTableUpdate(net->LinkHashTable, net->Link[i].ID, i);
}
for (i = 1; i <= net->Npumps; i++) {
if (net->Pump[i].Link > index) {
net->Pump[i].Link -= 1;
}
}
for (i = 1; i <= net->Nvalves; i++) {
if (net->Valve[i].Link > index) {
net->Valve[i].Link -= 1;
}
}
// update pumps
if (linkType == EN_PUMP) {
int pumpindex = findpump(net,index);
for (i = pumpindex; i <= net->Npumps - 1; i++) {
net->Pump[i] = net->Pump[i + 1];
}
net->Npumps--;
}
// update valves
if (linkType > EN_PUMP) {
int valveindex = findvalve(net,index);
for (i = valveindex; i <= net->Nvalves - 1; i++) {
net->Valve[i] = net->Valve[i + 1];
}
net->Nvalves--;
}
return 0;
}
int DLLEXPORT EN_deletenode(EN_ProjectHandle ph, int index) {
EN_Project *p = (EN_Project*)ph;
EN_Network *net = &p->network;
int i, nodeType;
if (!p->Openflag)
return (102);
if (index <= 0 || index > net->Nnodes)
return (203);
EN_getnodetype(p, index, &nodeType);
// remove from hash table
ENHashTableDelete(net->NodeHashTable, net->Node[index].ID);
// shift node and coord array to remove node
for (i = index; i <= net->Nnodes - 1; i++) {
net->Node[i] = net->Node[i + 1];
net->Coord[i] = net->Coord[i + 1];
// update hashtable
ENHashTableUpdate(net->NodeHashTable, net->Node[i].ID, i);
}
// update tank array
if (nodeType != EN_JUNCTION) {
int tankindex = findtank(net, index);
for (i = tankindex; i <= net->Ntanks - 1; i++) {
net->Tank[i] = net->Tank[i + 1];
}
}
// update tank node indices
for (i = 1; i <= net->Ntanks; i++) {
if (net->Tank[i].Node > index) {
net->Tank[i].Node -= 1;
}
}
// gather a list of link ids to remove in reverse order,
// so that re-shuffling doesn't destroy the indexing
for (i = net->Nlinks; i >= 1; i--) {
if (net->Link[i].N1 == index || net->Link[i].N2 == index) {
EN_deletelink(p,i);
}
}
for (i = 1; i <= net->Nlinks; i++) {
if (net->Link[i].N1 > index) {
net->Link[i].N1 -= 1;
}
if (net->Link[i].N2 > index) {
net->Link[i].N2 -= 1;
}
}
// update counters
if (nodeType == EN_JUNCTION) {
net->Njuncs--;
} else {
net->Ntanks--;
}
net->Nnodes--;
return (0);
}
int DLLEXPORT EN_getrule(EN_ProjectHandle ph, int index, int *nPremises, int *nTrueActions, int *nFalseActions, EN_API_FLOAT_TYPE *priority)
/*----------------------------------------------------------------
** Input: index = index of the rule
** nPremises = number of conditions (IF AND OR)
** nTrueActions = number of actions with true conditions (THEN)
** nFalseActions = number of actions with false conditions (ELSE)
** priority = rule priority
** Output: none
** Returns: error code
**----------------------------------------------------------------
*/
{
int count;
Premise *p;
Action *c;
EN_Project *pr = (EN_Project*)ph;
EN_Network *net = &pr->network;
if (index > net->Nrules)
return (257);
*priority = (EN_API_FLOAT_TYPE)pr->rules.Rule[index].priority;
count = 1;
p = pr->rules.Rule[index].Pchain;
while (p->next != NULL) {
count++;
p = p->next;
}
*nPremises = count;
count = 1;
c = pr->rules.Rule[index].Tchain;
while (c->next != NULL) {
count++;
c = c->next;
}
*nTrueActions = count;
c = pr->rules.Rule[index].Fchain;
count = 0;
if (c != NULL) {
count = 1;
while (c->next != NULL) {
count++;
c = c->next;
}
}
*nFalseActions = count;
return (0);
}
int DLLEXPORT EN_getpremise(EN_ProjectHandle ph, int indexRule, int idxPremise, int *logop,
int *object, int *indexObj, int *variable,
int *relop, int *status, EN_API_FLOAT_TYPE *value) {
int count = 1, error = 0, nPremises, a, b;
EN_API_FLOAT_TYPE priority;
Premise *p;
EN_Project *pr;
pr = (EN_Project*)ph;
if (indexRule > pr->network.Nrules) {
return (257);
}
error = EN_getrule(pr, indexRule, &nPremises, &a, &b, &priority);
if (idxPremise > nPremises) {
return (258);
}
p = pr->rules.Rule[indexRule].Pchain;
while (count < idxPremise) {
count++;
p = p->next;
}
*logop = p->logop;
*object = p->object;
*indexObj = p->index;
*variable = p->variable;
*relop = p->relop;
*status = p->status;
*value = (EN_API_FLOAT_TYPE)p[0].value;
return (0);
}
int DLLEXPORT EN_setrulepriority(EN_ProjectHandle ph, int index, EN_API_FLOAT_TYPE priority)
/*----------------------------------------------------------------
** Input: index = index of the rule
** priority = rule priority
** Output: none
** Returns: error code
**----------------------------------------------------------------
*/
{
EN_Project *pr = (EN_Project*)ph;
if (index > pr->network.Nrules) {
return (257);
}
pr->rules.Rule[index].priority = priority;
return (0);
}
int DLLEXPORT EN_setpremise(EN_ProjectHandle ph, int indexRule, int indexPremise, int logop,
int object, int indexObj, int variable, int relop,
int status, EN_API_FLOAT_TYPE value) {
int count = 1, error = 0, nPremises, a, b;
EN_API_FLOAT_TYPE priority;
Premise *p;
EN_Project *pr;
pr = (EN_Project*)ph;
if (indexRule > pr->network.Nrules) {
return (257);
}
error = EN_getrule(pr, indexRule, &nPremises, &a, &b, &priority);
if (indexPremise > nPremises) {
return (258);
}
p = pr->rules.Rule[indexRule].Pchain;
while (count < indexPremise) {
count++;
p = p->next;
}
p->logop = logop;
p->object = object;
p->index = indexObj;
p->variable = variable;
p->relop = relop;
p->status = status;
p->value = value;
return (0);
}
int DLLEXPORT EN_setpremiseindex(EN_ProjectHandle ph, int indexRule, int indexPremise, int indexObj) {
int count = 1, error = 0, nPremises, a, b;
EN_API_FLOAT_TYPE priority;
Premise *p;
EN_Project *pr = (EN_Project*)ph;
if (indexRule > pr->network.Nrules)
return (257);
error = EN_getrule(pr, indexRule, &nPremises, &a, &b, &priority);
if (indexPremise > nPremises) {
return (258);
}
p = pr->rules.Rule[indexRule].Pchain;
while (count < indexPremise) {
count++;
p = p->next;
}
p->index = indexObj;
return (0);
}
int DLLEXPORT EN_setpremisestatus(EN_ProjectHandle ph, int indexRule, int indexPremise, int status) {
int count = 1, error = 0, nPremises, a, b;
EN_API_FLOAT_TYPE priority;
Premise *p;
EN_Project *pr = (EN_Project*)ph;
if (indexRule > pr->network.Nrules) {
return (257);
}
error = EN_getrule(pr, indexRule, &nPremises, &a, &b, &priority);
if (indexPremise > nPremises) {
return (258);
}
p = pr->rules.Rule[indexRule].Pchain;
while (count < indexPremise) {
count++;
p = p->next;
}
p->status = status;
return (0);
}
int DLLEXPORT EN_setpremisevalue(EN_ProjectHandle ph, int indexRule, int indexPremise,
EN_API_FLOAT_TYPE value) {
int count = 1, error = 0, nPremises, a, b;
EN_API_FLOAT_TYPE priority;
Premise *p;
EN_Project *pr = (EN_Project*)ph;
if (indexRule > pr->network.Nrules)
return (257);
error = EN_getrule(pr, indexRule, &nPremises, &a, &b, &priority);
if (indexPremise > nPremises) {
return (258);
}
p = pr->rules.Rule[indexRule].Pchain;
while (count < indexPremise) {
count++;
p = p->next;
}
p->value = value;
return (0);
}
int DLLEXPORT EN_gettrueaction(EN_ProjectHandle ph, int indexRule, int indexAction, int *indexLink,
int *status, EN_API_FLOAT_TYPE *setting) {
int count = 1, error = 0, nTrueAction, c, b;
EN_API_FLOAT_TYPE priority;
Action *a;
EN_Project *pr = (EN_Project*)ph;
if (indexRule > pr->network.Nrules) {
return (252);
}
error = EN_getrule(pr, indexRule, &c, &nTrueAction, &b, &priority);
if (indexAction > nTrueAction) {
return (253);
}
a = pr->rules.Rule[indexRule].Tchain;
while (count < indexAction) {
count++;
a = a->next;
}
*indexLink = a->link;
*status = a->status;
*setting = (EN_API_FLOAT_TYPE)a->setting;
return (0);
}
int DLLEXPORT EN_settrueaction(EN_ProjectHandle ph, int indexRule, int indexAction, int indexLink,
int status, EN_API_FLOAT_TYPE setting) {
int count = 1, error = 0, nTrueAction, c, b;
EN_API_FLOAT_TYPE priority;
Action *a;
EN_Project *pr = (EN_Project*)ph;
if (indexRule > pr->network.Nrules) {
return (257);
}
error = EN_getrule(pr, indexRule, &c, &nTrueAction, &b, &priority);
if (indexAction > nTrueAction) {
return (258);
}
a = pr->rules.Rule[indexRule].Tchain;
while (count < indexAction) {
count++;
a = a->next;
}
a->link = indexLink;
a->status = status;
a->setting = setting;
return (0);
}
int DLLEXPORT EN_getfalseaction(EN_ProjectHandle ph, int indexRule, int indexAction, int *indexLink,
int *status, EN_API_FLOAT_TYPE *setting) {
int count = 1, error = 0, nFalseAction, c, b;
EN_API_FLOAT_TYPE priority;
Action *a;
EN_Project *pr = (EN_Project*)ph;
if (indexRule > pr->network.Nrules) {
return (257);
}
error = EN_getrule(pr, indexRule, &c, &b, &nFalseAction, &priority);
if (indexAction > nFalseAction) {
return (258);
}
a = pr->rules.Rule[indexRule].Fchain;
while (count < indexAction) {
count++;
a = a->next;
}
*indexLink = a->link;
*status = a->status;
*setting = (EN_API_FLOAT_TYPE)a->setting;
return (0);
}
int DLLEXPORT EN_setfalseaction(EN_ProjectHandle ph, int indexRule, int indexAction, int indexLink,
int status, EN_API_FLOAT_TYPE setting) {
int count = 1, error = 0, nFalseAction, c, b;
EN_API_FLOAT_TYPE priority;
Action *a;
EN_Project *pr = (EN_Project*)ph;
if (indexRule > pr->network.Nrules) {
return (257);
}
error = EN_getrule(pr, indexRule, &c, &b, &nFalseAction, &priority);
if (indexAction > nFalseAction) {
return (258);
}
a = pr->rules.Rule[indexRule].Fchain;
while (count < indexAction) {
count++;
a = a->next;
}
a->link = indexLink;
a->status = status;
a->setting = setting;
return (0);
}
int DLLEXPORT EN_getruleID(EN_ProjectHandle ph, int indexRule, char *id) {
EN_Project *pr = (EN_Project*)ph;
strcpy(id, "");
if (!pr->Openflag)
return (102);
if (indexRule < 1 || indexRule > pr->network.Nrules)
return (257);
strcpy(id, pr->rules.Rule[indexRule].label);
return (0);
}
/*************************** END OF EPANET.C ***************************/