4999 lines
147 KiB
C
4999 lines
147 KiB
C
/*
|
|
******************************************************************************
|
|
Project: OWA EPANET
|
|
Version: 2.2
|
|
Module: epanet.c
|
|
Description: implementation of EPANET's API functions
|
|
Authors: see AUTHORS
|
|
Copyright: see AUTHORS
|
|
License: see LICENSE
|
|
Last Updated: 01/11/2019
|
|
******************************************************************************
|
|
*/
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#ifndef __APPLE__
|
|
#include <malloc.h>
|
|
#endif
|
|
#include <float.h>
|
|
#include <math.h>
|
|
|
|
#include "epanet2_2.h"
|
|
#include "types.h"
|
|
#include "funcs.h"
|
|
#include "text.h"
|
|
#include "enumstxt.h"
|
|
|
|
#ifdef WINDOWS
|
|
#define snprintf _snprintf
|
|
#endif
|
|
|
|
/********************************************************************
|
|
|
|
System Functions
|
|
|
|
********************************************************************/
|
|
|
|
int DLLEXPORT EN_createproject(EN_Project *p)
|
|
/*----------------------------------------------------------------
|
|
** Input: none
|
|
** Output: p = pointer to a new EPANET project
|
|
** Returns: error code
|
|
** Purpose: creates a new EPANET project
|
|
**----------------------------------------------------------------
|
|
*/
|
|
{
|
|
struct Project *project = (struct Project *)calloc(1, sizeof(struct Project));
|
|
if (project == NULL) return -1;
|
|
getTmpName(project->TmpHydFname);
|
|
getTmpName(project->TmpOutFname);
|
|
getTmpName(project->TmpStatFname);
|
|
*p = project;
|
|
return 0;
|
|
}
|
|
|
|
int DLLEXPORT EN_deleteproject(EN_Project *p)
|
|
/*----------------------------------------------------------------
|
|
** Input: none
|
|
** Output: none
|
|
** Returns: error code
|
|
** Purpose: deletes an EPANET project
|
|
**----------------------------------------------------------------
|
|
*/
|
|
{
|
|
if (*p == NULL) return -1;
|
|
if ((*p)->Openflag) EN_close(*p);
|
|
remove((*p)->TmpHydFname);
|
|
remove((*p)->TmpOutFname);
|
|
remove((*p)->TmpStatFname);
|
|
free(*p);
|
|
*p = NULL;
|
|
return 0;
|
|
}
|
|
|
|
int DLLEXPORT EN_runproject(EN_Project p, const char *inpFile,
|
|
const char *rptFile, const char *outFile,
|
|
void (*pviewprog)(char *))
|
|
/*------------------------------------------------------------------------
|
|
** Input: inpFile = name of EPANET formatted input file
|
|
** rptFile = name of report file
|
|
** outFile = 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 errcode = 0;
|
|
|
|
// Read in project data from an input file
|
|
ERRCODE(EN_open(p, inpFile, rptFile, outFile));
|
|
p->viewprog = pviewprog;
|
|
|
|
// Solve for system hydraulics
|
|
if (p->outfile.Hydflag != USE)
|
|
{
|
|
ERRCODE(EN_solveH(p));
|
|
}
|
|
|
|
// Solve for system water quality
|
|
ERRCODE(EN_solveQ(p));
|
|
|
|
// Write a formatted output report
|
|
ERRCODE(EN_report(p));
|
|
EN_close(p);
|
|
|
|
// Return any error or warning code
|
|
if (p->Warnflag) errcode = MAX(errcode, p->Warnflag);
|
|
return errcode;
|
|
}
|
|
|
|
int DLLEXPORT EN_init(EN_Project p, const char *rptFile, const char *outFile,
|
|
int unitsType, int headLossType)
|
|
/*----------------------------------------------------------------
|
|
** Input: rptFile = name of report file
|
|
** outFile = name of binary output file
|
|
** unitsType = type of flow units (see FlowUnitsType)
|
|
** headLossType = type of head loss formula (see HeadLossType)
|
|
** Output: none
|
|
** Returns: error code
|
|
** Purpose: initializes an EPANET project that isn't opened with
|
|
** an input file
|
|
**----------------------------------------------------------------
|
|
*/
|
|
{
|
|
int errcode = 0;
|
|
|
|
// Set system flags
|
|
p->Openflag = FALSE;
|
|
p->hydraul.OpenHflag = FALSE;
|
|
p->quality.OpenQflag = FALSE;
|
|
p->outfile.SaveHflag = FALSE;
|
|
p->outfile.SaveQflag = FALSE;
|
|
p->Warnflag = FALSE;
|
|
p->report.Messageflag = TRUE;
|
|
p->report.Rptflag = 1;
|
|
|
|
// Check for valid arguments
|
|
if (unitsType < 0 || unitsType > CMD) return 251;
|
|
if (headLossType < 0 || headLossType > CM) return 251;
|
|
|
|
// Open files
|
|
errcode = openfiles(p, "", rptFile, outFile);
|
|
|
|
// Initialize memory used for project's data objects
|
|
initpointers(p);
|
|
ERRCODE(netsize(p));
|
|
ERRCODE(allocdata(p));
|
|
if (errcode) return (errcode);
|
|
|
|
// Set analysis options
|
|
setdefaults(p);
|
|
p->parser.Flowflag = unitsType;
|
|
p->hydraul.Formflag = headLossType;
|
|
|
|
// Perform additional initializations
|
|
adjustdata(p);
|
|
initreport(&p->report);
|
|
initunits(p);
|
|
inittanks(p);
|
|
convertunits(p);
|
|
|
|
// Initialize the default demand pattern
|
|
p->parser.MaxPats = 0;
|
|
getpatterns(p);
|
|
p->Openflag = TRUE;
|
|
return errcode;
|
|
}
|
|
|
|
int DLLEXPORT EN_open(EN_Project p, const char *inpFile, const char *rptFile,
|
|
const char *outFile)
|
|
/*----------------------------------------------------------------
|
|
** Input: inpFile = name of input file
|
|
** rptFile = name of report file
|
|
** outFile = name of binary output file
|
|
** Output: none
|
|
** Returns: error code
|
|
** Purpose: opens an EPANET input file & reads in network data
|
|
**----------------------------------------------------------------
|
|
*/
|
|
{
|
|
int errcode = 0;
|
|
|
|
// Set system flags
|
|
p->Openflag = FALSE;
|
|
p->hydraul.OpenHflag = FALSE;
|
|
p->quality.OpenQflag = FALSE;
|
|
p->outfile.SaveHflag = FALSE;
|
|
p->outfile.SaveQflag = FALSE;
|
|
p->Warnflag = FALSE;
|
|
p->report.Messageflag = TRUE;
|
|
p->report.Rptflag = 1;
|
|
|
|
// Initialize data arrays to NULL
|
|
initpointers(p);
|
|
|
|
// Open input & report files
|
|
ERRCODE(openfiles(p, inpFile, rptFile, outFile));
|
|
if (errcode > 0)
|
|
{
|
|
errmsg(p, errcode);
|
|
return errcode;
|
|
}
|
|
|
|
// Allocate memory for project's data arrays
|
|
writewin(p->viewprog, FMT100);
|
|
ERRCODE(netsize(p));
|
|
ERRCODE(allocdata(p));
|
|
|
|
// Read 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 file then open it
|
|
if (p->outfile.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_Project p, const char *filename)
|
|
/*----------------------------------------------------------------
|
|
** Input: filename = name of file to which project is saved
|
|
** Output: none
|
|
** Returns: error code
|
|
** Purpose: saves project to an EPANET formatted file
|
|
**----------------------------------------------------------------
|
|
*/
|
|
{
|
|
if (!p->Openflag) return 102;
|
|
return saveinpfile(p, filename);
|
|
}
|
|
|
|
int DLLEXPORT EN_close(EN_Project p)
|
|
/*----------------------------------------------------------------
|
|
** Input: none
|
|
** Output: none
|
|
** Returns: error code
|
|
** Purpose: frees all memory & files used by a project
|
|
**----------------------------------------------------------------
|
|
*/
|
|
{
|
|
Outfile *out;
|
|
|
|
// Free all project data
|
|
if (p->Openflag) writetime(p, FMT105);
|
|
freedata(p);
|
|
|
|
// Close output file
|
|
out = &p->outfile;
|
|
if (out->TmpOutFile != out->OutFile)
|
|
{
|
|
if (out->TmpOutFile != NULL) fclose(out->TmpOutFile);
|
|
out->TmpOutFile = NULL;
|
|
}
|
|
if (out->OutFile != NULL)
|
|
{
|
|
fclose(out->OutFile);
|
|
out->OutFile = NULL;
|
|
}
|
|
|
|
// Close input file
|
|
if (p->parser.InFile != NULL)
|
|
{
|
|
fclose(p->parser.InFile);
|
|
p->parser.InFile = NULL;
|
|
}
|
|
|
|
// Close report file
|
|
if (p->report.RptFile != NULL && p->report.RptFile != stdout)
|
|
{
|
|
fclose(p->report.RptFile);
|
|
p->report.RptFile = NULL;
|
|
}
|
|
|
|
// Close hydraulics file
|
|
if (out->HydFile != NULL)
|
|
{
|
|
fclose(out->HydFile);
|
|
out->HydFile = NULL;
|
|
}
|
|
|
|
// Reset system flags
|
|
p->Openflag = FALSE;
|
|
p->hydraul.OpenHflag = FALSE;
|
|
p->outfile.SaveHflag = FALSE;
|
|
p->quality.OpenQflag = FALSE;
|
|
p->outfile.SaveQflag = FALSE;
|
|
return 0;
|
|
}
|
|
|
|
/********************************************************************
|
|
|
|
Hydraulic Analysis Functions
|
|
|
|
********************************************************************/
|
|
|
|
int DLLEXPORT EN_solveH(EN_Project p)
|
|
/*----------------------------------------------------------------
|
|
** Input: none
|
|
** Output: none
|
|
** Returns: error code
|
|
** Purpose: solves for network hydraulics in all time periods
|
|
**----------------------------------------------------------------
|
|
*/
|
|
{
|
|
int errcode;
|
|
long t, tstep;
|
|
|
|
// Open hydraulics solver
|
|
errcode = EN_openH(p);
|
|
if (!errcode)
|
|
{
|
|
// Initialize hydraulics
|
|
errcode = EN_initH(p, EN_SAVE);
|
|
|
|
// Analyze each hydraulic time period
|
|
if (!errcode) do
|
|
{
|
|
// Display progress message
|
|
sprintf(p->Msg, "%-10s",
|
|
clocktime(p->report.Atime, p->times.Htime));
|
|
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(p, &t));
|
|
ERRCODE(EN_nextH(p, &tstep));
|
|
} while (tstep > 0);
|
|
}
|
|
|
|
// Close hydraulics solver
|
|
EN_closeH(p);
|
|
errcode = MAX(errcode, p->Warnflag);
|
|
return errcode;
|
|
}
|
|
|
|
int DLLEXPORT EN_saveH(EN_Project p)
|
|
/*----------------------------------------------------------------
|
|
** Input: none
|
|
** Output: none
|
|
** Returns: error code
|
|
** Purpose: saves hydraulic results to binary file
|
|
**
|
|
** Must be called before EN_report() if no WQ simulation made.
|
|
** Should not be called if EN_solveQ() will be used.
|
|
**----------------------------------------------------------------
|
|
*/
|
|
{
|
|
int tmpflag;
|
|
int errcode;
|
|
|
|
// Check if hydraulic results exist
|
|
if (!p->outfile.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_Project p)
|
|
/*----------------------------------------------------------------
|
|
** Input: none
|
|
** Output: none
|
|
** Returns: error code
|
|
** Purpose: opens a project's hydraulic solver
|
|
**----------------------------------------------------------------
|
|
*/
|
|
{
|
|
int errcode = 0;
|
|
|
|
// Check that input data exists
|
|
p->hydraul.OpenHflag = FALSE;
|
|
p->outfile.SaveHflag = FALSE;
|
|
if (!p->Openflag) return 102;
|
|
|
|
// Check that previously saved hydraulics file not in use
|
|
if (p->outfile.Hydflag == USE) return 107;
|
|
|
|
// Open hydraulics solver
|
|
ERRCODE(openhyd(p));
|
|
if (!errcode) p->hydraul.OpenHflag = TRUE;
|
|
else errmsg(p, errcode);
|
|
return errcode;
|
|
}
|
|
|
|
int DLLEXPORT EN_initH(EN_Project p, int initFlag)
|
|
/*----------------------------------------------------------------
|
|
** Input: initFlag = 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 a project's hydraulic solver
|
|
**----------------------------------------------------------------
|
|
*/
|
|
{
|
|
int errcode = 0;
|
|
int sflag, fflag;
|
|
|
|
// Reset status flags
|
|
p->outfile.SaveHflag = FALSE;
|
|
p->Warnflag = FALSE;
|
|
|
|
// Get values of save-to-file flag and reinitialize-flows flag
|
|
fflag = initFlag / EN_INITFLOW;
|
|
sflag = initFlag - fflag * EN_INITFLOW;
|
|
|
|
// Check that hydraulics solver was opened
|
|
if (!p->hydraul.OpenHflag) return 103;
|
|
|
|
// Open hydraulics file if requested
|
|
p->outfile.Saveflag = FALSE;
|
|
if (sflag > 0)
|
|
{
|
|
errcode = openhydfile(p);
|
|
if (!errcode) p->outfile.Saveflag = TRUE;
|
|
else
|
|
{
|
|
errmsg(p, errcode);
|
|
return errcode;
|
|
}
|
|
}
|
|
|
|
// Initialize hydraulics solver
|
|
inithyd(p, fflag);
|
|
if (p->report.Statflag > 0) writeheader(p, STATHDR, 0);
|
|
return errcode;
|
|
}
|
|
|
|
int DLLEXPORT EN_runH(EN_Project p, long *currentTime)
|
|
/*----------------------------------------------------------------
|
|
** Input: none
|
|
** Output: currentTime = current elapsed time (sec)
|
|
** Returns: error code
|
|
** Purpose: solves network hydraulics at current time point
|
|
**----------------------------------------------------------------
|
|
*/
|
|
{
|
|
int errcode;
|
|
|
|
*currentTime = 0;
|
|
if (!p->hydraul.OpenHflag) return 103;
|
|
errcode = runhyd(p, currentTime);
|
|
if (errcode) errmsg(p, errcode);
|
|
return errcode;
|
|
}
|
|
|
|
int DLLEXPORT EN_nextH(EN_Project p, long *tStep)
|
|
/*----------------------------------------------------------------
|
|
** Input: none
|
|
** Output: tStep = next hydraulic time step to take (sec)
|
|
** Returns: error code
|
|
** Purpose: determines the time step until the next hydraulic event
|
|
**----------------------------------------------------------------
|
|
*/
|
|
{
|
|
int errcode;
|
|
|
|
*tStep = 0;
|
|
if (!p->hydraul.OpenHflag) return 103;
|
|
errcode = nexthyd(p, tStep);
|
|
if (errcode) errmsg(p, errcode);
|
|
else if (p->outfile.Saveflag && *tStep == 0) p->outfile.SaveHflag = TRUE;
|
|
return errcode;
|
|
}
|
|
|
|
int DLLEXPORT EN_closeH(EN_Project p)
|
|
/*----------------------------------------------------------------
|
|
** Input: none
|
|
** Output: none
|
|
** Returns: error code
|
|
** Purpose: closes a project's hydraulic solver
|
|
**----------------------------------------------------------------
|
|
*/
|
|
{
|
|
if (!p->Openflag) return 102;
|
|
if (p->hydraul.OpenHflag) closehyd(p);
|
|
p->hydraul.OpenHflag = FALSE;
|
|
return 0;
|
|
}
|
|
|
|
int DLLEXPORT EN_savehydfile(EN_Project p, char *filename)
|
|
/*----------------------------------------------------------------
|
|
** Input: filename = name of file to which hydraulic results are saved
|
|
** Output: none
|
|
** Returns: error code
|
|
** Purpose: saves results from a scratch hydraulics file to a
|
|
** permanent one
|
|
**----------------------------------------------------------------
|
|
*/
|
|
{
|
|
FILE *f;
|
|
FILE *HydFile;
|
|
int c;
|
|
|
|
// Check that hydraulics results exist
|
|
if (p->outfile.HydFile == NULL || !p->outfile.SaveHflag) return 104;
|
|
|
|
// Open the permanent hydraulics file
|
|
if ((f = fopen(filename, "w+b")) == NULL) return 305;
|
|
|
|
// Copy from the scratch file to f
|
|
HydFile = p->outfile.HydFile;
|
|
fseek(HydFile, 0, SEEK_SET);
|
|
while ((c = fgetc(HydFile)) != EOF) fputc(c, f);
|
|
fclose(f);
|
|
return 0;
|
|
}
|
|
|
|
int DLLEXPORT EN_usehydfile(EN_Project p, char *filename)
|
|
/*----------------------------------------------------------------
|
|
** Input: filename = name of previously saved hydraulics file
|
|
** Output: none
|
|
** Returns: error code
|
|
** Purpose: uses contents of a previously saved hydraulics file to
|
|
** run a water quality analysis
|
|
**----------------------------------------------------------------
|
|
*/
|
|
{
|
|
int errcode;
|
|
|
|
// Check that project was opened & hydraulic solver is closed
|
|
if (!p->Openflag) return 102;
|
|
if (p->hydraul.OpenHflag) return 108;
|
|
|
|
// Try to open hydraulics file
|
|
strncpy(p->outfile.HydFname, filename, MAXFNAME);
|
|
p->outfile.Hydflag = USE;
|
|
p->outfile.SaveHflag = TRUE;
|
|
errcode = openhydfile(p);
|
|
|
|
// If error, then reset flags
|
|
if (errcode)
|
|
{
|
|
strcpy(p->outfile.HydFname, "");
|
|
p->outfile.Hydflag = SCRATCH;
|
|
p->outfile.SaveHflag = FALSE;
|
|
}
|
|
return errcode;
|
|
}
|
|
|
|
/********************************************************************
|
|
|
|
Water Quality Analysis Functions
|
|
|
|
********************************************************************/
|
|
|
|
int DLLEXPORT EN_solveQ(EN_Project p)
|
|
/*----------------------------------------------------------------
|
|
** Input: none
|
|
** Output: none
|
|
** Returns: error code
|
|
** Purpose: solves for network water quality in all time periods
|
|
**----------------------------------------------------------------
|
|
*/
|
|
{
|
|
int errcode;
|
|
long t, tstep;
|
|
|
|
// Open WQ solver
|
|
errcode = EN_openQ(p);
|
|
if (!errcode)
|
|
{
|
|
// Initialize WQ solver
|
|
errcode = EN_initQ(p, EN_SAVE);
|
|
if (!p->quality.Qualflag) writewin(p->viewprog, FMT106);
|
|
|
|
// Analyze each hydraulic period
|
|
if (!errcode) do
|
|
{
|
|
// Display progress message
|
|
sprintf(p->Msg, "%-10s",
|
|
clocktime(p->report.Atime, p->times.Htime));
|
|
if (p->quality.Qualflag)
|
|
{
|
|
sprintf(p->Msg, FMT102, p->report.Atime);
|
|
writewin(p->viewprog, p->Msg);
|
|
}
|
|
|
|
// Retrieve current hydraulic results & update water quality
|
|
// to start of next time period
|
|
tstep = 0;
|
|
ERRCODE(EN_runQ(p, &t));
|
|
ERRCODE(EN_nextQ(p, &tstep));
|
|
} while (tstep > 0);
|
|
}
|
|
|
|
// Close WQ solver
|
|
EN_closeQ(p);
|
|
return errcode;
|
|
}
|
|
|
|
int DLLEXPORT EN_openQ(EN_Project p)
|
|
/*----------------------------------------------------------------
|
|
** Input: none
|
|
** Output: none
|
|
** Returns: error code
|
|
** Purpose: opens a project's water quality solver
|
|
**----------------------------------------------------------------
|
|
*/
|
|
{
|
|
int errcode = 0;
|
|
|
|
// Check that hydraulics results exist
|
|
p->quality.OpenQflag = FALSE;
|
|
p->outfile.SaveQflag = FALSE;
|
|
if (!p->Openflag) return 102;
|
|
if (!p->hydraul.OpenHflag && !p->outfile.SaveHflag) return 104;
|
|
|
|
// Open water quality solver
|
|
ERRCODE(openqual(p));
|
|
if (!errcode) p->quality.OpenQflag = TRUE;
|
|
else errmsg(p, errcode);
|
|
return errcode;
|
|
}
|
|
|
|
int DLLEXPORT EN_initQ(EN_Project p, int saveFlag)
|
|
/*----------------------------------------------------------------
|
|
** Input: saveFlag = flag indicating if results should be saved
|
|
** to the binary output file or not
|
|
** Output: none
|
|
** Returns: error code
|
|
** Purpose: initializes the water quality solver
|
|
**----------------------------------------------------------------
|
|
*/
|
|
{
|
|
int errcode = 0;
|
|
|
|
if (!p->quality.OpenQflag) return 105;
|
|
initqual(p);
|
|
p->outfile.SaveQflag = FALSE;
|
|
p->outfile.Saveflag = FALSE;
|
|
if (saveFlag)
|
|
{
|
|
errcode = openoutfile(p);
|
|
if (!errcode) p->outfile.Saveflag = TRUE;
|
|
}
|
|
return errcode;
|
|
}
|
|
|
|
int DLLEXPORT EN_runQ(EN_Project p, long *currentTime)
|
|
/*----------------------------------------------------------------
|
|
** Input: none
|
|
** Output: currentTime = current simulation time (sec)
|
|
** Returns: error code
|
|
** Purpose: retrieves current hydraulic results and saves current
|
|
** results to file.
|
|
**----------------------------------------------------------------
|
|
*/
|
|
{
|
|
int errcode;
|
|
|
|
*currentTime = 0;
|
|
if (!p->quality.OpenQflag) return 105;
|
|
errcode = runqual(p, currentTime);
|
|
if (errcode) errmsg(p, errcode);
|
|
return errcode;
|
|
}
|
|
|
|
int DLLEXPORT EN_nextQ(EN_Project p, long *tStep)
|
|
/*----------------------------------------------------------------
|
|
** Input: none
|
|
** Output: tStep = time step over which water quality is updated (sec)
|
|
** Returns: error code
|
|
** Purpose: updates water quality throughout the network until
|
|
** next hydraulic event occurs
|
|
**----------------------------------------------------------------
|
|
*/
|
|
{
|
|
int errcode;
|
|
|
|
*tStep = 0;
|
|
if (!p->quality.OpenQflag) return 105;
|
|
errcode = nextqual(p, tStep);
|
|
if (!errcode && p->outfile.Saveflag && *tStep == 0)
|
|
{
|
|
p->outfile.SaveQflag = TRUE;
|
|
}
|
|
if (errcode) errmsg(p, errcode);
|
|
return errcode;
|
|
}
|
|
|
|
int DLLEXPORT EN_stepQ(EN_Project p, long *timeLeft)
|
|
/*----------------------------------------------------------------
|
|
** Input: none
|
|
** Output: timeLeft = amount of simulation time remaining (sec)
|
|
** Returns: error code
|
|
** Purpose: updates water quality throughout the network over
|
|
** fixed time step
|
|
**----------------------------------------------------------------
|
|
*/
|
|
{
|
|
int errcode;
|
|
|
|
*timeLeft = 0;
|
|
if (!p->quality.OpenQflag) return 105;
|
|
errcode = stepqual(p, timeLeft);
|
|
if (!errcode && p->outfile.Saveflag && *timeLeft == 0)
|
|
{
|
|
p->outfile.SaveQflag = TRUE;
|
|
}
|
|
if (errcode) errmsg(p, errcode);
|
|
return errcode;
|
|
}
|
|
|
|
int DLLEXPORT EN_closeQ(EN_Project p)
|
|
/*----------------------------------------------------------------
|
|
** Input: none
|
|
** Output: none
|
|
** Returns: error code
|
|
** Purpose: closes a project's water quality solver
|
|
**----------------------------------------------------------------
|
|
*/
|
|
{
|
|
if (!p->Openflag) return 102;
|
|
closequal(p);
|
|
p->quality.OpenQflag = FALSE;
|
|
return 0;
|
|
}
|
|
|
|
/********************************************************************
|
|
|
|
Reporting Functions
|
|
|
|
********************************************************************/
|
|
|
|
int DLLEXPORT EN_writeline(EN_Project p, char *line)
|
|
/*----------------------------------------------------------------
|
|
** Input: line = line of text
|
|
** Output: none
|
|
** Returns: error code
|
|
** Purpose: write a line of text to a project's report file
|
|
**----------------------------------------------------------------
|
|
*/
|
|
{
|
|
if (!p->Openflag) return 102;
|
|
writeline(p, line);
|
|
return 0;
|
|
}
|
|
|
|
int DLLEXPORT EN_report(EN_Project p)
|
|
/*----------------------------------------------------------------
|
|
** Input: none
|
|
** Output: none
|
|
** Returns: error code
|
|
** Purpose: writes formatted simulation results to a project's
|
|
** report file
|
|
**----------------------------------------------------------------
|
|
*/
|
|
{
|
|
int errcode;
|
|
|
|
// Check if results have been saved to binary output file
|
|
if (!p->outfile.SaveQflag) return 106;
|
|
writewin(p->viewprog, FMT103);
|
|
|
|
// Write the formatted report
|
|
errcode = writereport(p);
|
|
if (errcode) errmsg(p, errcode);
|
|
return errcode;
|
|
}
|
|
|
|
int DLLEXPORT EN_resetreport(EN_Project p)
|
|
/*----------------------------------------------------------------
|
|
** Input: none
|
|
** Output: none
|
|
** Returns: error code
|
|
** Purpose: resets reporting options to their default values
|
|
**----------------------------------------------------------------
|
|
*/
|
|
{
|
|
int i;
|
|
|
|
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_Project p, char *format)
|
|
/*----------------------------------------------------------------
|
|
** Input: format = a report formatting command
|
|
** Output: none
|
|
** Returns: error code
|
|
** Purpose: sets a specific set of reporting options
|
|
**----------------------------------------------------------------
|
|
*/
|
|
{
|
|
char s1[MAXLINE + 1];
|
|
|
|
if (!p->Openflag) return 102;
|
|
if (strlen(format) >= MAXLINE) return 250;
|
|
strcpy(s1, format);
|
|
strcat(s1, "\n");
|
|
if (setreport(p, s1) > 0) return 250;
|
|
else return 0;
|
|
}
|
|
|
|
int DLLEXPORT EN_setstatusreport(EN_Project p, int level)
|
|
/*----------------------------------------------------------------
|
|
** Input: level = level of reporting to use (see EN_StatusReport)
|
|
** Output: none
|
|
** Returns: error code
|
|
** Purpose: sets the level of hydraulic status reporting
|
|
**----------------------------------------------------------------
|
|
*/
|
|
{
|
|
int errcode = 0;
|
|
|
|
if (level >= EN_NO_REPORT && level <= EN_FULL_REPORT)
|
|
{
|
|
p->report.Statflag = (char)level;
|
|
}
|
|
else errcode = 251;
|
|
return errcode;
|
|
}
|
|
|
|
int DLLEXPORT EN_getversion(int *version)
|
|
/*----------------------------------------------------------------
|
|
** Input: none
|
|
** Output: version = version number of the source code
|
|
** Returns: error code (should always be 0)
|
|
** Purpose: retrieves the toolkit API version number
|
|
**
|
|
** The version number is set by the constant CODEVERSION found in
|
|
** TYPES.H and is to be interpreted with implied decimals, i.e.,
|
|
** "20100" == "2(.)01(.)00".
|
|
**----------------------------------------------------------------
|
|
*/
|
|
{
|
|
*version = CODEVERSION;
|
|
return 0;
|
|
}
|
|
|
|
int DLLEXPORT EN_getcount(EN_Project p, int object, int *count)
|
|
/*----------------------------------------------------------------
|
|
** Input: object = type of object to count (see EN_CountType)
|
|
** Output: count = number of objects of the specified type
|
|
** Returns: error code
|
|
** Purpose: Retrieves number of network objects of a given type
|
|
**----------------------------------------------------------------
|
|
*/
|
|
{
|
|
Network *net = &p->network;
|
|
|
|
*count = 0;
|
|
if (!p->Openflag) return 102;
|
|
switch (object)
|
|
{
|
|
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_geterror(int errcode, char *errmsg, int maxLen)
|
|
/*----------------------------------------------------------------
|
|
** Input: errcode = an error or warnng code
|
|
** maxLen = maximum characters that errmsg can hold
|
|
** Output: errmsg = text of error/warning message
|
|
** Returns: error code
|
|
** Purpose: retrieves the text of the message associated with
|
|
** a particular error/warning code
|
|
**----------------------------------------------------------------
|
|
*/
|
|
{
|
|
char msg1[MAXMSG+1] = "";
|
|
char msg2[MAXMSG+1] = "";
|
|
|
|
switch (errcode)
|
|
{
|
|
case 1:
|
|
strncpy(errmsg, WARN1, maxLen);
|
|
break;
|
|
case 2:
|
|
strncpy(errmsg, WARN2, maxLen);
|
|
break;
|
|
case 3:
|
|
strncpy(errmsg, WARN3, maxLen);
|
|
break;
|
|
case 4:
|
|
strncpy(errmsg, WARN4, maxLen);
|
|
break;
|
|
case 5:
|
|
strncpy(errmsg, WARN5, maxLen);
|
|
break;
|
|
case 6:
|
|
strncpy(errmsg, WARN6, maxLen);
|
|
break;
|
|
default:
|
|
sprintf(msg1, "Error %d: ", errcode);
|
|
if ((errcode >= 202 && errcode <= 222) ||
|
|
(errcode >= 240 && errcode <= 261)) strcat(msg1, "function call contains ");
|
|
snprintf(errmsg, maxLen, "%s%s", msg1, geterrmsg(errcode, msg2));
|
|
}
|
|
if (strlen(errmsg) == 0)
|
|
return 251;
|
|
else
|
|
return 0;
|
|
}
|
|
|
|
int DLLEXPORT EN_getstatistic(EN_Project p, int type, double *value)
|
|
/*----------------------------------------------------------------
|
|
** Input: type = type of simulation statistic (see EN_AnalysisStatistic)
|
|
** Output: value = simulation analysis statistic value
|
|
** Returns: error code
|
|
** Purpose: retrieves the value of a simulation analysis statistic
|
|
**----------------------------------------------------------------
|
|
*/
|
|
{
|
|
switch (type)
|
|
{
|
|
case EN_ITERATIONS:
|
|
*value = (double)p->hydraul.Iterations;
|
|
break;
|
|
case EN_RELATIVEERROR:
|
|
*value = (double)p->hydraul.RelativeError;
|
|
break;
|
|
case EN_MAXHEADERROR:
|
|
*value = (double)(p->hydraul.MaxHeadError * p->Ucf[HEAD]);
|
|
break;
|
|
case EN_MAXFLOWCHANGE:
|
|
*value = (double)(p->hydraul.MaxFlowChange * p->Ucf[FLOW]);
|
|
break;
|
|
case EN_MASSBALANCE:
|
|
*value = (double)(p->quality.MassBalance.ratio);
|
|
break;
|
|
default:
|
|
*value = 0.0;
|
|
break;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int DLLEXPORT EN_gettitle(EN_Project p, char *titleline1, char *titleline2, char *titleline3)
|
|
/*----------------------------------------------------------------
|
|
** Input: None
|
|
** Output: titleline1-3 = project's title lines
|
|
** Returns: error code
|
|
** Purpose: retrieves the title lines of the project
|
|
**----------------------------------------------------------------
|
|
*/
|
|
{
|
|
if (!p->Openflag) return 102;
|
|
strcpy(titleline1, p->Title[0]);
|
|
strcpy(titleline2, p->Title[1]);
|
|
strcpy(titleline3, p->Title[2]);
|
|
return 0;
|
|
}
|
|
|
|
int DLLEXPORT EN_settitle(EN_Project p, char *titleline1, char *titleline2, char *titleline3)
|
|
/*----------------------------------------------------------------
|
|
** Input: titleline1-3 = project's title lines
|
|
** Returns: error code
|
|
** Purpose: sets the title lines of the project
|
|
**----------------------------------------------------------------
|
|
*/
|
|
{
|
|
if (!p->Openflag) return 102;
|
|
strncpy(p->Title[0], titleline1, TITLELEN);
|
|
strncpy(p->Title[1], titleline2, TITLELEN);
|
|
strncpy(p->Title[2], titleline3, TITLELEN);
|
|
return 123;
|
|
}
|
|
|
|
/********************************************************************
|
|
|
|
Analysis Options Functions
|
|
|
|
********************************************************************/
|
|
|
|
int DLLEXPORT EN_getoption(EN_Project p, int option, double *value)
|
|
/*----------------------------------------------------------------
|
|
** Input: option = an analysis option code (see EN_Option)
|
|
** Output: value = analysis option value
|
|
** Returns: error code
|
|
** Purpose: retrieves the value of an analysis option
|
|
**----------------------------------------------------------------
|
|
*/
|
|
{
|
|
Hydraul *hyd = &p->hydraul;
|
|
Quality *qual = &p->quality;
|
|
|
|
double *Ucf = p->Ucf;
|
|
double v = 0.0;
|
|
|
|
*value = 0.0;
|
|
if (!p->Openflag) return 102;
|
|
switch (option)
|
|
{
|
|
case EN_TRIALS:
|
|
v = (double)hyd->MaxIter;
|
|
break;
|
|
case EN_ACCURACY:
|
|
v = hyd->Hacc;
|
|
break;
|
|
case EN_TOLERANCE:
|
|
v = qual->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;
|
|
case EN_DEFDEMANDPAT:
|
|
v = hyd->DefPat;
|
|
break;
|
|
case EN_HEADLOSSFORM:
|
|
v = hyd->Formflag;
|
|
break;
|
|
case EN_GLOBALEFFIC:
|
|
v = hyd->Epump;
|
|
break;
|
|
case EN_GLOBALPRICE:
|
|
v = hyd->Ecost;
|
|
break;
|
|
case EN_GLOBALPATTERN:
|
|
v = hyd->Epat;
|
|
break;
|
|
case EN_DEMANDCHARGE:
|
|
v = hyd->Dcost;
|
|
break;
|
|
|
|
default:
|
|
return 251;
|
|
}
|
|
*value = (double)v;
|
|
return 0;
|
|
}
|
|
|
|
int DLLEXPORT EN_setoption(EN_Project p, int option, double value)
|
|
/*----------------------------------------------------------------
|
|
** Input: option = analysis option code (see EN_Option)
|
|
** value = analysis option value
|
|
** Output: none
|
|
** Returns: error code
|
|
** Purpose: sets the value for an analysis option
|
|
**----------------------------------------------------------------
|
|
*/
|
|
{
|
|
Network *net = &p->network;
|
|
Hydraul *hyd = &p->hydraul;
|
|
Quality *qual = &p->quality;
|
|
|
|
Snode *node;
|
|
Pdemand demand;
|
|
const int Njuncs = net->Njuncs;
|
|
double *Ucf = p->Ucf;
|
|
int i, j;
|
|
int tmpPat, pat, error;
|
|
char tmpId[MAXID + 1];
|
|
double Ke, n, ucf;
|
|
|
|
if (!p->Openflag) return 102;
|
|
switch (option)
|
|
{
|
|
case EN_TRIALS:
|
|
if (value < 1.0) return 213;
|
|
hyd->MaxIter = (int)value;
|
|
break;
|
|
|
|
case EN_ACCURACY:
|
|
if (value < 1.e-5 || value > 1.e-1) return 213;
|
|
hyd->Hacc = value;
|
|
break;
|
|
|
|
case EN_TOLERANCE:
|
|
if (value < 0.0) return 213;
|
|
qual->Ctol = value / Ucf[QUALITY];
|
|
break;
|
|
|
|
case EN_EMITEXPON:
|
|
if (value <= 0.0) return 213;
|
|
n = 1.0 / value;
|
|
ucf = pow(Ucf[FLOW], n) / Ucf[PRESSURE];
|
|
for (i = 1; i <= Njuncs; i++)
|
|
{
|
|
j = EN_getnodevalue(p, i, EN_EMITTER, &Ke);
|
|
if (j == 0 && Ke > 0.0) net->Node[i].Ke = ucf / pow(Ke, n);
|
|
}
|
|
hyd->Qexp = n;
|
|
break;
|
|
|
|
case EN_DEMANDMULT:
|
|
if (value <= 0.0) return 213;
|
|
hyd->Dmult = value;
|
|
break;
|
|
|
|
case EN_HEADERROR:
|
|
if (value < 0.0) return 213;
|
|
hyd->HeadErrorLimit = value / Ucf[HEAD];
|
|
break;
|
|
|
|
case EN_FLOWCHANGE:
|
|
if (value < 0.0) return 213;
|
|
hyd->FlowChangeLimit = value / Ucf[FLOW];
|
|
break;
|
|
|
|
case EN_DEFDEMANDPAT:
|
|
//check that the pattern exists or is set to zero to delete the default pattern
|
|
pat = ROUND(value);
|
|
if (pat < 0 || pat > net->Npats) return 205;
|
|
tmpPat = hyd->DefPat;
|
|
//get the new pattern ID
|
|
if (pat == 0)
|
|
{
|
|
strncpy(tmpId, p->parser.DefPatID, MAXID);
|
|
}
|
|
else
|
|
{
|
|
error = EN_getpatternid(p, pat, tmpId);
|
|
if (error != 0) return error;
|
|
}
|
|
// replace node patterns with default pattern
|
|
for (i = 1; i <= net->Nnodes; i++)
|
|
{
|
|
node = &net->Node[i];
|
|
for (demand = node->D; demand != NULL; demand = demand->next)
|
|
{
|
|
if (demand->Pat == tmpPat)
|
|
{
|
|
demand->Pat = pat;
|
|
strcpy(demand->Name, "");
|
|
}
|
|
}
|
|
}
|
|
strncpy(p->parser.DefPatID, tmpId, MAXID);
|
|
hyd->DefPat = pat;
|
|
break;
|
|
|
|
case EN_GLOBALEFFIC:
|
|
if (value <= 0.0 || value > 100.0) return 213;
|
|
hyd->Epump = value;
|
|
break;
|
|
|
|
case EN_GLOBALPRICE:
|
|
if (value < 0.0) return 213;
|
|
hyd->Ecost = value;
|
|
break;
|
|
|
|
case EN_GLOBALPATTERN:
|
|
pat = ROUND(value);
|
|
if (pat < 0 || pat > net->Npats) return 205;
|
|
hyd->Epat = pat;
|
|
break;
|
|
|
|
case EN_DEMANDCHARGE:
|
|
if (value < 0.0) return 213;
|
|
hyd->Dcost = value;
|
|
break;
|
|
|
|
default:
|
|
return 251;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int DLLEXPORT EN_getflowunits(EN_Project p, int *units)
|
|
/*----------------------------------------------------------------
|
|
** Input: none
|
|
** Output: units = flow units code (see EN_FlowUnits)
|
|
** Returns: error code
|
|
** Purpose: retrieves the flow units used by a project
|
|
**----------------------------------------------------------------
|
|
*/
|
|
{
|
|
*units = -1;
|
|
if (!p->Openflag) return 102;
|
|
*units = p->parser.Flowflag;
|
|
return 0;
|
|
}
|
|
|
|
int DLLEXPORT EN_setflowunits(EN_Project p, int units)
|
|
/*----------------------------------------------------------------
|
|
** Input: units = flow units code (see EN_FlowUnits)
|
|
** Output: none
|
|
** Returns: error code
|
|
** Purpose: sets the flow units used by a project
|
|
**----------------------------------------------------------------
|
|
*/
|
|
{
|
|
Network *net = &p->network;
|
|
|
|
int i, j;
|
|
double qfactor, vfactor, hfactor, efactor, xfactor, yfactor;
|
|
double *Ucf = p->Ucf;
|
|
|
|
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 = units;
|
|
switch (units)
|
|
{
|
|
case LPS:
|
|
case LPM:
|
|
case MLD:
|
|
case CMH:
|
|
case CMD:
|
|
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 VOLUME_CURVE:
|
|
xfactor = efactor / Ucf[ELEV];
|
|
yfactor = vfactor / Ucf[VOLUME];
|
|
break;
|
|
case HLOSS_CURVE:
|
|
case PUMP_CURVE:
|
|
xfactor = qfactor / Ucf[FLOW];
|
|
yfactor = hfactor / Ucf[HEAD];
|
|
break;
|
|
case EFFIC_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_gettimeparam(EN_Project p, int param, long *value)
|
|
/*----------------------------------------------------------------
|
|
** Input: param = time parameter code (see EN_TimeParameter)
|
|
** Output: value = time parameter value
|
|
** Returns: error code
|
|
** Purpose: retrieves the value of a time parameter
|
|
**----------------------------------------------------------------
|
|
*/
|
|
{
|
|
Report *rpt = &p->report;
|
|
Times *time = &p->times;
|
|
|
|
int i;
|
|
|
|
*value = 0;
|
|
if (!p->Openflag) return 102;
|
|
if (param < EN_DURATION || param > EN_NEXTEVENTTANK) return 251;
|
|
switch (param)
|
|
{
|
|
case EN_DURATION:
|
|
*value = time->Dur;
|
|
break;
|
|
case EN_HYDSTEP:
|
|
*value = time->Hstep;
|
|
break;
|
|
case EN_QUALSTEP:
|
|
*value = time->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 = rpt->Tstatflag;
|
|
break;
|
|
case EN_RULESTEP:
|
|
*value = time->Rulestep;
|
|
break;
|
|
case EN_PERIODS:
|
|
*value = rpt->Nperiods;
|
|
break;
|
|
case EN_STARTTIME:
|
|
*value = time->Tstart;
|
|
break;
|
|
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 full/empty tank
|
|
tanktimestep(p, value);
|
|
break;
|
|
case EN_NEXTEVENTTANK:
|
|
*value = time->Hstep;
|
|
i = tanktimestep(p, value);
|
|
*value = i;
|
|
break;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int DLLEXPORT EN_settimeparam(EN_Project p, int param, long value)
|
|
/*----------------------------------------------------------------
|
|
** Input: param = time parameter code (see EN_TimeParameter)
|
|
** value = time parameter value
|
|
** Output: none
|
|
** Returns: error code
|
|
** Purpose: sets the value of a time parameter
|
|
**----------------------------------------------------------------
|
|
*/
|
|
{
|
|
Report *rpt = &p->report;
|
|
Times *time = &p->times;
|
|
|
|
if (!p->Openflag) return 102;
|
|
if (value < 0) return 213;
|
|
switch (param)
|
|
{
|
|
case EN_DURATION:
|
|
time->Dur = value;
|
|
if (time->Rstart > time->Dur) time->Rstart = 0;
|
|
break;
|
|
|
|
case EN_HYDSTEP:
|
|
if (value == 0) return 213;
|
|
time->Hstep = value;
|
|
time->Hstep = MIN(time->Pstep, time->Hstep);
|
|
time->Hstep = MIN(time->Rstep, time->Hstep);
|
|
time->Qstep = MIN(time->Qstep, time->Hstep);
|
|
break;
|
|
|
|
case EN_QUALSTEP:
|
|
if (value == 0) return 213;
|
|
time->Qstep = value;
|
|
time->Qstep = MIN(time->Qstep, time->Hstep);
|
|
break;
|
|
|
|
case EN_PATTERNSTEP:
|
|
if (value == 0) return 213;
|
|
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 213;
|
|
time->Rstep = value;
|
|
if (time->Hstep > time->Rstep) time->Hstep = time->Rstep;
|
|
break;
|
|
|
|
case EN_REPORTSTART:
|
|
if (time->Rstart > time->Dur) return 213;
|
|
time->Rstart = value;
|
|
break;
|
|
|
|
case EN_RULESTEP:
|
|
if (value == 0) return 213;
|
|
time->Rulestep = value;
|
|
time->Rulestep = MIN(time->Rulestep, time->Hstep);
|
|
break;
|
|
|
|
case EN_STATISTIC:
|
|
if (value > RANGE) return 213;
|
|
rpt->Tstatflag = (char)value;
|
|
break;
|
|
|
|
case EN_HTIME:
|
|
time->Htime = value;
|
|
break;
|
|
|
|
case EN_QTIME:
|
|
time->Qtime = value;
|
|
break;
|
|
|
|
default:
|
|
return 251;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int DLLEXPORT EN_getqualinfo(EN_Project p, int *qualType, char *chemName,
|
|
char *chemUnits, int *traceNode)
|
|
/*----------------------------------------------------------------
|
|
** Input: none
|
|
** Output: qualType = type of quality analysis to run (see EN_QualityType)
|
|
** chemName = name of chemical constituent
|
|
** chemUnits = concentration units of constituent
|
|
** traceNode = index of node being traced (if applicable)
|
|
** Returns: error code
|
|
** Purpose: retrieves water quality analysis options
|
|
**----------------------------------------------------------------
|
|
*/
|
|
{
|
|
EN_getqualtype(p, qualType, traceNode);
|
|
if (p->quality.Qualflag == CHEM)
|
|
{
|
|
strncpy(chemName, p->quality.ChemName, MAXID);
|
|
strncpy(chemUnits, p->quality.ChemUnits, MAXID);
|
|
}
|
|
else if (p->quality.Qualflag == TRACE)
|
|
{
|
|
strncpy(chemName, w_TRACE, MAXID);
|
|
strncpy(chemUnits, u_PERCENT, MAXID);
|
|
}
|
|
else if (p->quality.Qualflag == AGE)
|
|
{
|
|
strncpy(chemName, w_AGE, MAXID);
|
|
strncpy(chemUnits, u_HOURS, MAXID);
|
|
}
|
|
else
|
|
{
|
|
strncpy(chemName, "", MAXID);
|
|
strncpy(chemUnits, "", MAXID);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int DLLEXPORT EN_getqualtype(EN_Project p, int *qualType, int *traceNode)
|
|
/*----------------------------------------------------------------
|
|
** Input: none
|
|
** Output: qualType = type of quality analysis to run (see EN_QualityType)
|
|
** traceNode = index of node being traced (for qualType = EN_TRACE)
|
|
** Output: none
|
|
** Returns: error code
|
|
** Purpose: retrieves type of quality analysis being made
|
|
**----------------------------------------------------------------
|
|
*/
|
|
{
|
|
*traceNode = 0;
|
|
if (!p->Openflag) return 102;
|
|
*qualType = p->quality.Qualflag;
|
|
if (p->quality.Qualflag == TRACE) *traceNode = p->quality.TraceNode;
|
|
return 0;
|
|
}
|
|
|
|
int DLLEXPORT EN_setqualtype(EN_Project p, int qualType, char *chemName,
|
|
char *chemUnits, char *traceNode)
|
|
/*----------------------------------------------------------------
|
|
** Input: qualType = type of quality analysis to run (see EN_QualityType)
|
|
** chemname = name of chemical constituent
|
|
** chemunits = concentration units of constituent
|
|
** tracenode = ID name of node being traced (if applicable)
|
|
** Output: none
|
|
** Returns: error code
|
|
** Purpose: sets water quality analysis options
|
|
**----------------------------------------------------------------
|
|
*/
|
|
{
|
|
Network *net = &p->network;
|
|
Report *rpt = &p->report;
|
|
Quality *qual = &p->quality;
|
|
|
|
double *Ucf = p->Ucf;
|
|
int i;
|
|
double ccf = 1.0;
|
|
|
|
if (!p->Openflag) return 102;
|
|
if (qualType < EN_NONE || qualType > EN_TRACE) return 251;
|
|
qual->Qualflag = (char)qualType;
|
|
qual->Ctol *= Ucf[QUALITY];
|
|
if (qual->Qualflag == CHEM) // Chemical analysis
|
|
{
|
|
strncpy(qual->ChemName, chemName, MAXID);
|
|
strncpy(qual->ChemUnits, chemUnits, MAXID);
|
|
strncpy(rpt->Field[QUALITY].Units, qual->ChemUnits, MAXID);
|
|
strncpy(rpt->Field[REACTRATE].Units, qual->ChemUnits, MAXID);
|
|
strcat(rpt->Field[REACTRATE].Units, t_PERDAY);
|
|
ccf = 1.0 / LperFT3;
|
|
}
|
|
if (qual->Qualflag == TRACE) // Source trace analysis
|
|
{
|
|
qual->TraceNode = findnode(net, traceNode);
|
|
if (qual->TraceNode == 0) return 212;
|
|
strncpy(qual->ChemName, w_TRACE, MAXID);
|
|
strncpy(qual->ChemUnits, u_PERCENT, MAXID);
|
|
strcpy(rpt->Field[QUALITY].Units, u_PERCENT);
|
|
}
|
|
if (qual->Qualflag == AGE) // Water age analysis
|
|
{
|
|
strncpy(qual->ChemName, w_AGE, MAXID);
|
|
strncpy(qual->ChemUnits, u_HOURS, MAXID);
|
|
strcpy(rpt->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 ((qual->Qualflag == AGE || qual->Qualflag == TRACE) & (Ucf[QUALITY] != 1))
|
|
{
|
|
for (i = 1; i <= p->network.Nnodes; i++)
|
|
{
|
|
p->network.Node[i].C0 *= Ucf[QUALITY];
|
|
}
|
|
}
|
|
|
|
Ucf[QUALITY] = ccf;
|
|
Ucf[LINKQUAL] = ccf;
|
|
Ucf[REACTRATE] = ccf;
|
|
qual->Ctol /= Ucf[QUALITY];
|
|
return 0;
|
|
}
|
|
|
|
/********************************************************************
|
|
|
|
Node Functions
|
|
|
|
********************************************************************/
|
|
|
|
int DLLEXPORT EN_addnode(EN_Project p, char *id, int nodeType)
|
|
/*----------------------------------------------------------------
|
|
** Input: id = node ID name
|
|
** nodeType = type of node (see EN_NodeType)
|
|
** Output: none
|
|
** Returns: error code
|
|
** Purpose: adds a new node to a project
|
|
**----------------------------------------------------------------
|
|
*/
|
|
{
|
|
Network *net = &p->network;
|
|
Hydraul *hyd = &p->hydraul;
|
|
Quality *qual = &p->quality;
|
|
|
|
int i, nIdx;
|
|
int index;
|
|
int size;
|
|
struct Sdemand *demand;
|
|
Stank *tank;
|
|
Snode *node;
|
|
Scontrol *control;
|
|
|
|
// Cannot modify network structure while solvers are active
|
|
if (!p->Openflag) return 102;
|
|
if (hyd->OpenHflag || qual->OpenQflag) return 262;
|
|
|
|
// Check if a node with same id already exists
|
|
if (EN_getnodeindex(p, id, &i) == 0) return 215;
|
|
|
|
// Check that id name is not too long
|
|
if (strlen(id) > MAXID) return 250;
|
|
|
|
// Grow node-related arrays to accomodate the new node
|
|
size = (net->Nnodes + 2) * sizeof(Snode);
|
|
net->Node = (Snode *)realloc(net->Node, size);
|
|
size = (net->Nnodes + 2) * sizeof(double);
|
|
hyd->NodeDemand = (double *)realloc(hyd->NodeDemand, size);
|
|
qual->NodeQual = (double *)realloc(qual->NodeQual, size);
|
|
hyd->NodeHead = (double *)realloc(hyd->NodeHead, size);
|
|
|
|
// Actions taken when a new Junction is added
|
|
if (nodeType == EN_JUNCTION)
|
|
{
|
|
net->Njuncs++;
|
|
nIdx = net->Njuncs;
|
|
node = &net->Node[nIdx];
|
|
|
|
demand = (struct Sdemand *)malloc(sizeof(struct Sdemand));
|
|
demand->Base = 0.0;
|
|
demand->Pat = hyd->DefPat; // Use default pattern
|
|
strcpy(demand->Name, "");
|
|
demand->next = NULL;
|
|
node->D = demand;
|
|
|
|
// shift rest of Node array
|
|
for (index = net->Nnodes; index >= net->Njuncs; index--)
|
|
{
|
|
hashtable_update(net->NodeHashTable, net->Node[index].ID, index + 1);
|
|
net->Node[index + 1] = net->Node[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;
|
|
}
|
|
|
|
// shift indices of tanks/reservoir nodes in controls
|
|
for (index = 1; index <= net->Ncontrols; ++index)
|
|
{
|
|
control = &net->Control[index];
|
|
if (control->Node > net->Njuncs - 1) control->Node += 1;
|
|
}
|
|
|
|
// adjust indices of tanks/reservoirs in Rule premises (see RULES.C)
|
|
adjusttankrules(p);
|
|
}
|
|
|
|
// Actions taken when a new Tank/Reservoir is added
|
|
else
|
|
{
|
|
nIdx = net->Nnodes + 1;
|
|
node = &net->Node[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++;
|
|
strncpy(node->ID, id, MAXID);
|
|
|
|
// set default values for new node
|
|
node->El = 0;
|
|
node->S = NULL;
|
|
node->C0 = 0;
|
|
node->Ke = 0;
|
|
node->Rpt = 0;
|
|
node->X = MISSING;
|
|
node->Y = MISSING;
|
|
strcpy(node->Comment, "");
|
|
|
|
// Insert new node into hash table
|
|
hashtable_insert(net->NodeHashTable, node->ID, nIdx);
|
|
return 0;
|
|
}
|
|
|
|
int DLLEXPORT EN_deletenode(EN_Project p, int index, int actionCode)
|
|
/*----------------------------------------------------------------
|
|
** Input: index = index of the node to delete
|
|
** actionCode = how to treat controls that contain the link
|
|
** or its incident links:
|
|
** EN_UNCONDITIONAL deletes all such controls plus the node,
|
|
** EN_CONDITIONAL does not delete the node if it or any of
|
|
** its links appear in a control and returns an error code
|
|
** Output: none
|
|
** Returns: error code
|
|
** Purpose: deletes a node from a project
|
|
**----------------------------------------------------------------
|
|
*/
|
|
{
|
|
Network *net = &p->network;
|
|
|
|
int i, nodeType, tankindex;
|
|
Snode *node;
|
|
Pdemand demand, nextdemand;
|
|
Psource source;
|
|
|
|
// Cannot modify network structure while solvers are active
|
|
if (!p->Openflag) return 102;
|
|
if (p->hydraul.OpenHflag || p->quality.OpenQflag) return 262;
|
|
|
|
// Check that node exists
|
|
if (index <= 0 || index > net->Nnodes) return 203;
|
|
if (actionCode < EN_UNCONDITIONAL || actionCode > EN_CONDITIONAL) return 251;
|
|
|
|
// Can't delete a water quality trace node
|
|
if (index == p->quality.TraceNode) return 260;
|
|
|
|
// Count number of simple & rule-based controls that contain the node
|
|
if (actionCode == EN_CONDITIONAL)
|
|
{
|
|
actionCode = incontrols(p, NODE, index);
|
|
for (i = 1; i <= net->Nlinks; i++)
|
|
{
|
|
if (net->Link[i].N1 == index ||
|
|
net->Link[i].N2 == index) actionCode += incontrols(p, LINK, i);
|
|
}
|
|
if (actionCode > 0) return 261;
|
|
}
|
|
|
|
// Get a reference to the node & its type
|
|
node = &net->Node[index];
|
|
EN_getnodetype(p, index, &nodeType);
|
|
|
|
// Remove node from its hash table
|
|
hashtable_delete(net->NodeHashTable, node->ID);
|
|
|
|
// Free memory allocated to node's demands & WQ source
|
|
demand = node->D;
|
|
while (demand != NULL)
|
|
{
|
|
nextdemand = demand->next;
|
|
free(demand);
|
|
demand = nextdemand;
|
|
}
|
|
source = node->S;
|
|
if (source != NULL) free(source);
|
|
|
|
// Shift position of higher entries in Node & Coord arrays down one
|
|
for (i = index; i <= net->Nnodes - 1; i++)
|
|
{
|
|
net->Node[i] = net->Node[i + 1];
|
|
// ... update node's entry in the hash table
|
|
hashtable_update(net->NodeHashTable, net->Node[i].ID, i);
|
|
}
|
|
|
|
// Remove references to demands & source in last (inactive) Node array entry
|
|
net->Node[net->Nnodes].D = NULL;
|
|
net->Node[net->Nnodes].S = NULL;
|
|
|
|
// If deleted node is a tank, remove it from the Tank array
|
|
if (nodeType != EN_JUNCTION)
|
|
{
|
|
tankindex = findtank(net, index);
|
|
for (i = tankindex; i <= net->Ntanks - 1; i++)
|
|
{
|
|
net->Tank[i] = net->Tank[i + 1];
|
|
}
|
|
}
|
|
|
|
// Shift higher node indices in Tank array down one
|
|
for (i = 1; i <= net->Ntanks; i++)
|
|
{
|
|
if (net->Tank[i].Node > index) net->Tank[i].Node -= 1;
|
|
}
|
|
|
|
// Delete any links connected to the deleted node
|
|
// (Process links in reverse order to maintain their indexing)
|
|
for (i = net->Nlinks; i >= 1; i--)
|
|
{
|
|
if (net->Link[i].N1 == index ||
|
|
net->Link[i].N2 == index) EN_deletelink(p, i, EN_UNCONDITIONAL);
|
|
}
|
|
|
|
// Adjust indices of all link end nodes
|
|
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;
|
|
}
|
|
|
|
// Delete any control containing the node
|
|
for (i = net->Ncontrols; i >= 1; i--)
|
|
{
|
|
if (net->Control[i].Node == index) EN_deletecontrol(p, i);
|
|
}
|
|
|
|
// Adjust higher numbered link indices in remaining controls
|
|
for (i = 1; i <= net->Ncontrols; i++)
|
|
{
|
|
if (net->Control[i].Node > index) net->Control[i].Node--;
|
|
}
|
|
|
|
// Make necessary adjustments to rule-based controls
|
|
adjustrules(p, EN_R_NODE, index);
|
|
|
|
// Reduce counts of node types
|
|
if (nodeType == EN_JUNCTION) net->Njuncs--;
|
|
else net->Ntanks--;
|
|
net->Nnodes--;
|
|
return 0;
|
|
}
|
|
|
|
int DLLEXPORT EN_getnodeindex(EN_Project p, char *id, int *index)
|
|
/*----------------------------------------------------------------
|
|
** Input: id = node ID name
|
|
** Output: index = node index
|
|
** Returns: error code
|
|
** Purpose: retrieves the index of a node
|
|
**----------------------------------------------------------------
|
|
*/
|
|
{
|
|
*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_Project p, int index, char *id)
|
|
/*----------------------------------------------------------------
|
|
** Input: index = node index
|
|
** Output: id = node ID name
|
|
** Returns: error code
|
|
** Purpose: retrieves the name of a node
|
|
**----------------------------------------------------------------
|
|
*/
|
|
{
|
|
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_setnodeid(EN_Project p, int index, char *newid)
|
|
/*----------------------------------------------------------------
|
|
** Input: index = node index
|
|
** newid = new node ID name
|
|
** Output: none
|
|
** Returns: error code
|
|
** Purpose: sets the ID name of a node
|
|
**----------------------------------------------------------------
|
|
*/
|
|
{
|
|
Network *net = &p->network;
|
|
size_t n;
|
|
|
|
// Check for valid arguments
|
|
if (index <= 0 || index > net->Nnodes) return 203;
|
|
n = strlen(newid);
|
|
if (n < 1 || n > MAXID) return 209;
|
|
if (strcspn(newid, " ;") < n) return 209;
|
|
|
|
// Check if another node with same name exists
|
|
if (hashtable_find(net->NodeHashTable, newid) > 0) return 215;
|
|
|
|
// Replace the existing node ID with the new value
|
|
hashtable_delete(net->NodeHashTable, net->Node[index].ID);
|
|
strncpy(net->Node[index].ID, newid, MAXID);
|
|
hashtable_insert(net->NodeHashTable, net->Node[index].ID, index);
|
|
return 0;
|
|
}
|
|
|
|
int DLLEXPORT EN_getnodetype(EN_Project p, int index, int *nodeType)
|
|
/*----------------------------------------------------------------
|
|
** Input: index = node index
|
|
** Output: nodeType = node type (see EN_NodeType)
|
|
** Returns: error code
|
|
** Purpose: retrieves the type of a node
|
|
**----------------------------------------------------------------
|
|
*/
|
|
{
|
|
*nodeType = -1;
|
|
if (!p->Openflag) return 102;
|
|
if (index < 1 || index > p->network.Nnodes) return 203;
|
|
if (index <= p->network.Njuncs) *nodeType = EN_JUNCTION;
|
|
else
|
|
{
|
|
if (p->network.Tank[index - p->network.Njuncs].A == 0.0)
|
|
{
|
|
*nodeType = EN_RESERVOIR;
|
|
}
|
|
else *nodeType = EN_TANK;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int DLLEXPORT EN_getnodevalue(EN_Project p, int index, int property, double *value)
|
|
/*----------------------------------------------------------------
|
|
** Input: index = node index
|
|
** property = node property code (see EN_NodeProperty)
|
|
** Output: value = node property value
|
|
** Returns: error code
|
|
** Purpose: retrieves a property value for a node
|
|
**----------------------------------------------------------------
|
|
*/
|
|
{
|
|
Network *net = &p->network;
|
|
Hydraul *hyd = &p->hydraul;
|
|
Quality *qual = &p->quality;
|
|
|
|
double v = 0.0;
|
|
Pdemand demand;
|
|
Psource source;
|
|
|
|
Snode *Node = net->Node;
|
|
Stank *Tank = net->Tank;
|
|
|
|
int nJuncs = net->Njuncs;
|
|
|
|
double *Ucf = p->Ucf;
|
|
double *NodeDemand = hyd->NodeDemand;
|
|
double *NodeHead = hyd->NodeHead;
|
|
double *NodeQual = qual->NodeQual;
|
|
|
|
// Check for valid arguments
|
|
*value = 0.0;
|
|
if (!p->Openflag) return 102;
|
|
if (index <= 0 || index > net->Nnodes) return 203;
|
|
|
|
// Retrieve requested property
|
|
switch (property)
|
|
{
|
|
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;
|
|
|
|
case EN_SOURCEQUAL:
|
|
case EN_SOURCETYPE:
|
|
case EN_SOURCEMASS:
|
|
case EN_SOURCEPAT:
|
|
source = Node[index].S;
|
|
if (source == NULL) return 240;
|
|
if (property == EN_SOURCEQUAL) v = source->C0;
|
|
else if (property == EN_SOURCEMASS) v = source->Smass * 60.0;
|
|
else if (property == EN_SOURCEPAT) v = source->Pat;
|
|
else v = source->Type;
|
|
break;
|
|
|
|
case EN_TANKLEVEL:
|
|
if (index <= nJuncs) return 0;
|
|
v = (Tank[index - nJuncs].H0 - Node[index].El) * Ucf[ELEV];
|
|
break;
|
|
|
|
case EN_INITVOLUME:
|
|
v = 0.0;
|
|
if (index > nJuncs) v = Tank[index - nJuncs].V0 * Ucf[VOLUME];
|
|
break;
|
|
|
|
case EN_MIXMODEL:
|
|
v = MIX1;
|
|
if (index > nJuncs) v = Tank[index - nJuncs].MixModel;
|
|
break;
|
|
|
|
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 = NodeHead[index] * Ucf[HEAD];
|
|
break;
|
|
|
|
case EN_PRESSURE:
|
|
v = (NodeHead[index] - Node[index].El) * Ucf[PRESSURE];
|
|
break;
|
|
|
|
case EN_QUALITY:
|
|
v = NodeQual[index] * Ucf[QUALITY];
|
|
break;
|
|
|
|
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:
|
|
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;
|
|
|
|
case EN_TANKVOLUME:
|
|
if (index <= nJuncs) return 0;
|
|
v = tankvolume(p, index - nJuncs, NodeHead[index]) * Ucf[VOLUME];
|
|
break;
|
|
|
|
default:
|
|
return 251;
|
|
}
|
|
*value = v;
|
|
return 0;
|
|
}
|
|
|
|
int DLLEXPORT EN_setnodevalue(EN_Project p, int index, int property, double value)
|
|
/*----------------------------------------------------------------
|
|
** Input: index = node index
|
|
** property = node property code (see EN_NodeProperty)
|
|
** value = node property value
|
|
** Output: none
|
|
** Returns: error code
|
|
** Purpose: sets a property value for a node
|
|
**----------------------------------------------------------------
|
|
*/
|
|
{
|
|
Network *net = &p->network;
|
|
Hydraul *hyd = &p->hydraul;
|
|
Quality *qual = &p->quality;
|
|
|
|
Snode *Node = net->Node;
|
|
Stank *Tank = net->Tank;
|
|
Scurve *curve;
|
|
|
|
const int nNodes = net->Nnodes;
|
|
const int nJuncs = net->Njuncs;
|
|
const int nPats = net->Npats;
|
|
|
|
double *Ucf = p->Ucf;
|
|
|
|
int i, j, n;
|
|
Pdemand demand;
|
|
Psource source;
|
|
double hTmp;
|
|
|
|
if (!p->Openflag) return 102;
|
|
if (index <= 0 || index > nNodes) return 203;
|
|
switch (property)
|
|
{
|
|
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 0;
|
|
if (value < 0.0) return 209;
|
|
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 209;
|
|
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 209;
|
|
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 (property == EN_SOURCEQUAL) source->C0 = value;
|
|
else if (property == EN_SOURCEPAT)
|
|
{
|
|
j = ROUND(value);
|
|
if (j < 0 || j > nPats) return 205;
|
|
source->Pat = j;
|
|
}
|
|
else // property == EN_SOURCETYPE
|
|
{
|
|
j = ROUND(value);
|
|
if (j < CONCEN || j > FLOWPACED) return 251;
|
|
else source->Type = (char)j;
|
|
}
|
|
break;
|
|
|
|
case EN_TANKLEVEL:
|
|
if (index <= nJuncs) return 0;
|
|
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 225;
|
|
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;
|
|
|
|
case EN_TANKDIAM:
|
|
if (value <= 0.0) return 209;
|
|
if (index <= nJuncs) return 0;
|
|
j = index - nJuncs;
|
|
if (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);
|
|
Tank[j].Vcurve = 0;
|
|
}
|
|
break;
|
|
|
|
case EN_MINVOLUME:
|
|
if (value < 0.0) return 209;
|
|
if (index <= nJuncs) return 0;
|
|
j = index - nJuncs;
|
|
if (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);
|
|
}
|
|
break;
|
|
|
|
case EN_VOLCURVE:
|
|
if (index < nJuncs) return 0;
|
|
i = ROUND(value);
|
|
if (i < 0 || i > net->Ncurves) return 205;
|
|
curve = &net->Curve[i];
|
|
j = index - nJuncs;
|
|
if (Tank[j].A == 0.0) return 0;
|
|
n = curve->Npts - 1;
|
|
if (Tank[j].Vmin * Ucf[VOLUME] < curve->Y[0] ||
|
|
Tank[j].Vmax * Ucf[VOLUME] > curve->Y[n]) return 225;
|
|
Tank[j].Vcurve = i;
|
|
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);
|
|
Tank[j].A = (curve->Y[n] - curve->Y[0]) / (curve->X[n] - curve->X[0]);
|
|
break;
|
|
|
|
case EN_MINLEVEL:
|
|
if (value < 0.0) return 209;
|
|
if (index <= nJuncs) return 0; // not a tank or reservoir
|
|
j = index - nJuncs;
|
|
if (Tank[j].A == 0.0) return 0; // 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 0;
|
|
Tank[j].Hmin = hTmp;
|
|
Tank[j].Vmin = (Tank[j].Hmin - Node[index].El) * Tank[j].A;
|
|
}
|
|
else return 225;
|
|
break;
|
|
|
|
case EN_MAXLEVEL:
|
|
if (value < 0.0) return 209;
|
|
if (index <= nJuncs) return 0; // not a tank or reservoir
|
|
j = index - nJuncs;
|
|
if (Tank[j].A == 0.0) return 0; // 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 0;
|
|
Tank[j].Hmax = hTmp;
|
|
Tank[j].Vmax = tankvolume(p, j, Tank[j].Hmax);
|
|
}
|
|
else return 225;
|
|
break;
|
|
|
|
case EN_MIXMODEL:
|
|
j = ROUND(value);
|
|
if (index <= nJuncs) return 0;
|
|
if (j < MIX1 || j > LIFO) return 251;
|
|
if (Tank[index - nJuncs].A > 0.0)
|
|
{
|
|
Tank[index - nJuncs].MixModel = (char)j;
|
|
}
|
|
break;
|
|
|
|
case EN_MIXFRACTION:
|
|
if (index <= nJuncs) return 0;
|
|
if (value < 0.0 || value > 1.0) return 209;
|
|
j = index - nJuncs;
|
|
if (Tank[j].A > 0.0)
|
|
{
|
|
Tank[j].V1max = value * Tank[j].Vmax;
|
|
}
|
|
break;
|
|
|
|
case EN_TANK_KBULK:
|
|
if (index <= nJuncs) return 0;
|
|
j = index - nJuncs;
|
|
if (Tank[j].A > 0.0)
|
|
{
|
|
Tank[j].Kb = value / SECperDAY;
|
|
qual->Reactflag = 1;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
return 251;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int DLLEXPORT EN_setjuncdata(EN_Project p, int index, double elev,
|
|
double dmnd, char *dmndpat)
|
|
/*----------------------------------------------------------------
|
|
** Input: index = junction node index
|
|
** elev = junction elevation
|
|
** dmnd = junction primary base demand
|
|
** dmndpat = name of primary demand time pattern
|
|
** Output: none
|
|
** Returns: error code
|
|
** Purpose: sets several properties for a junction node
|
|
**----------------------------------------------------------------
|
|
*/
|
|
{
|
|
Network *net = &p->network;
|
|
|
|
int i, patIndex = 0;
|
|
Snode *Node = net->Node;
|
|
Pdemand demand;
|
|
|
|
// Check that junction exists
|
|
if (!p->Openflag) return 102;
|
|
if (index <= 0 || index > net->Njuncs) return 203;
|
|
|
|
// Check that demand pattern exists
|
|
if (strlen(dmndpat) > 0)
|
|
{
|
|
for (i = 1; i <= net->Npats; i++)
|
|
{
|
|
if (strcmp(dmndpat, net->Pattern[i].ID) == 0)
|
|
{
|
|
patIndex = i;
|
|
break;
|
|
}
|
|
}
|
|
if (patIndex == 0) return 205;
|
|
}
|
|
|
|
// Assign values to junction's parameters
|
|
Node[index].El = elev / p->Ucf[ELEV];
|
|
for (demand = Node[index].D; demand != NULL; demand = demand->next)
|
|
{
|
|
if (demand->next == NULL)
|
|
{
|
|
demand->Base = dmnd / p->Ucf[FLOW];
|
|
demand->Pat = patIndex;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int DLLEXPORT EN_settankdata(EN_Project p, int index, double elev,
|
|
double initlvl, double minlvl,
|
|
double maxlvl, double diam,
|
|
double minvol, char *volcurve)
|
|
/*----------------------------------------------------------------
|
|
** Input: index = tank node index
|
|
** elev = tank bottom elevation
|
|
** initlvl = initial water depth
|
|
** minlvl = minimum water depth
|
|
** maxlvl = maximum water depth
|
|
** diam = tank diameter
|
|
** minvol = tank volume at minimum level
|
|
** volCurve = name of curve for volume v. level
|
|
** Output: none
|
|
** Returns: error code
|
|
** Purpose: sets several properties for a tank node
|
|
**----------------------------------------------------------------
|
|
*/
|
|
{
|
|
Network *net = &p->network;
|
|
|
|
int i, j, n, curveIndex = 0;
|
|
double area, elevation = elev;
|
|
double *Ucf = p->Ucf;
|
|
Stank *Tank = net->Tank;
|
|
Scurve *curve;
|
|
|
|
// Check that tank exists
|
|
if (!p->Openflag) return 102;
|
|
if (index <= net->Njuncs || index > net->Nnodes) return 203;
|
|
j = index - net->Njuncs;
|
|
if (Tank[j].A == 0) return 0; // Tank is a Reservoir
|
|
|
|
// Check for valid parameter values
|
|
if (initlvl < 0.0 || minlvl < 0.0 || maxlvl < 0.0) return 209;
|
|
if (minlvl > initlvl || minlvl > maxlvl || initlvl > maxlvl) return 225;
|
|
if (diam < 0.0 || minvol < 0.0) return 209;
|
|
|
|
// volume curve supplied
|
|
if (strlen(volcurve) > 0)
|
|
{
|
|
for (i = 1; i <= net->Ncurves; i++)
|
|
{
|
|
if (strcmp(volcurve, net->Curve[i].ID) == 0)
|
|
{
|
|
curveIndex = i;
|
|
break;
|
|
}
|
|
}
|
|
if (curveIndex == 0) return 206;
|
|
curve = &net->Curve[curveIndex];
|
|
n = curve->Npts - 1;
|
|
if (minlvl < curve->X[0] || maxlvl > curve->X[n]) return 225;
|
|
area = (curve->Y[n] - curve->Y[0]) / (curve->X[n] - curve->X[0]);
|
|
}
|
|
|
|
// Tank diameter supplied
|
|
else area = PI * diam * diam / 4.0;
|
|
|
|
// Assign parameters to tank object
|
|
net->Node[Tank[j].Node].El = elevation;
|
|
Tank[j].A = area / Ucf[ELEV] / Ucf[ELEV];
|
|
Tank[j].H0 = elevation + initlvl / Ucf[ELEV];
|
|
Tank[j].Hmin = elevation + minlvl / Ucf[ELEV];
|
|
Tank[j].Hmax = elevation + maxlvl / Ucf[ELEV];
|
|
Tank[j].Vcurve = curveIndex;
|
|
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);
|
|
return 0;
|
|
}
|
|
|
|
|
|
int DLLEXPORT EN_getcoord(EN_Project p, int index, double *x, double *y)
|
|
/*----------------------------------------------------------------
|
|
** Input: index = node index
|
|
** Output: x = node x-coordinate
|
|
** y = node y-coordinate
|
|
** Returns: error code
|
|
** Purpose: retrieves the coordinates of a node
|
|
**----------------------------------------------------------------
|
|
*/
|
|
{
|
|
Network *net = &p->network;
|
|
Snode *node;
|
|
|
|
if (!p->Openflag) return 102;
|
|
if (index < 1 || index > p->network.Nnodes) return 203;
|
|
|
|
// check if node has coords
|
|
node = &net->Node[index];
|
|
if (node->X == MISSING ||
|
|
node->Y == MISSING) return 254;
|
|
|
|
*x = (double)(node->X);
|
|
*y = (double)(node->Y);
|
|
return 0;
|
|
}
|
|
|
|
int DLLEXPORT EN_setcoord(EN_Project p, int index, double x, double y)
|
|
/*----------------------------------------------------------------
|
|
** Input: index = node index
|
|
** x = node x-coordinate
|
|
** y = node y-coordinate
|
|
** Output: none
|
|
** Returns: error code
|
|
** Purpose: sets the coordinates of a node
|
|
**----------------------------------------------------------------
|
|
*/
|
|
{
|
|
Network *net = &p->network;
|
|
Snode *node;
|
|
|
|
if (!p->Openflag) return 102;
|
|
if (index < 1 || index > p->network.Nnodes) return 203;
|
|
node = &net->Node[index];
|
|
node->X = x;
|
|
node->Y = y;
|
|
return 0;
|
|
}
|
|
|
|
/********************************************************************
|
|
|
|
Nodal Demand Functions
|
|
|
|
********************************************************************/
|
|
|
|
int DLLEXPORT EN_getdemandmodel(EN_Project p, int *model, double *pmin,
|
|
double *preq, double *pexp)
|
|
/*----------------------------------------------------------------
|
|
** Input: none
|
|
** Output: model = type of demand model (see EN_DemandModel)
|
|
** pmin = minimum pressure for any demand
|
|
** preq = required pressure for full demand
|
|
** pexp = exponent in pressure dependent demand formula
|
|
** Returns: error code
|
|
** Purpose: retrieves the parameters of a project's demand model
|
|
**----------------------------------------------------------------
|
|
*/
|
|
{
|
|
*model = p->hydraul.DemandModel;
|
|
*pmin = (double)(p->hydraul.Pmin * p->Ucf[PRESSURE]);
|
|
*preq = (double)(p->hydraul.Preq * p->Ucf[PRESSURE]);
|
|
*pexp = (double)(p->hydraul.Pexp);
|
|
return 0;
|
|
}
|
|
|
|
int DLLEXPORT EN_setdemandmodel(EN_Project p, int model, double pmin,
|
|
double preq, double pexp)
|
|
/*----------------------------------------------------------------
|
|
** Input: model = type of demand model (see EN_DemandModel)
|
|
** pmin = minimum pressure for any demand
|
|
** preq = required pressure for full demand
|
|
** pexp = exponent in pressure dependent demand formula
|
|
** Output: none
|
|
** Returns: error code
|
|
** Purpose: sets the parameters of a project's demand model
|
|
**----------------------------------------------------------------
|
|
*/
|
|
{
|
|
if (model < 0 || model > EN_PDA) return 251;
|
|
if (pmin > preq || pexp <= 0.0) return 209;
|
|
p->hydraul.DemandModel = model;
|
|
p->hydraul.Pmin = pmin / p->Ucf[PRESSURE];
|
|
p->hydraul.Preq = preq / p->Ucf[PRESSURE];
|
|
p->hydraul.Pexp = pexp;
|
|
return 0;
|
|
}
|
|
|
|
int DLLEXPORT EN_getnumdemands(EN_Project p, int nodeIndex, int *numDemands)
|
|
/*----------------------------------------------------------------
|
|
** Input: nodeIndex = node index
|
|
** Output: numDemands = number of demand categories
|
|
** Returns: error code
|
|
** Purpose: retrieves the number of demand categories for a node
|
|
**----------------------------------------------------------------
|
|
*/
|
|
{
|
|
Pdemand d;
|
|
int n = 0;
|
|
|
|
// Check for valid arguments
|
|
if (!p->Openflag) return 102;
|
|
if (nodeIndex <= 0 || nodeIndex > p->network.Nnodes) return 203;
|
|
|
|
// Count the number of demand categories
|
|
for (d = p->network.Node[nodeIndex].D; d != NULL; d = d->next) n++;
|
|
*numDemands = n;
|
|
return 0;
|
|
}
|
|
|
|
int DLLEXPORT EN_getbasedemand(EN_Project p, int nodeIndex, int demandIndex,
|
|
double *baseDemand)
|
|
/*----------------------------------------------------------------
|
|
** Input: nodeIndex = node index
|
|
** demandIndex = demand category index
|
|
** Output: baseDemand = baseline demand value
|
|
** Returns: error code
|
|
** Purpose: retrieves the baseline value for a node's demand category
|
|
**----------------------------------------------------------------
|
|
*/
|
|
{
|
|
Pdemand d;
|
|
int n = 1;
|
|
|
|
// Check for valid arguments
|
|
if (!p->Openflag) return 102;
|
|
if (nodeIndex <= 0 || nodeIndex > p->network.Nnodes) return 203;
|
|
|
|
// Retrieve demand for specified category
|
|
if (nodeIndex <= p->network.Njuncs)
|
|
{
|
|
for (d = p->network.Node[nodeIndex].D; n < demandIndex && d->next != NULL;
|
|
d = d->next) n++;
|
|
if (n != demandIndex) return 253;
|
|
*baseDemand = (double)(d->Base * p->Ucf[FLOW]);
|
|
}
|
|
else *baseDemand = (double)(0.0);
|
|
return 0;
|
|
}
|
|
|
|
int DLLEXPORT EN_setbasedemand(EN_Project p, int nodeIndex, int demandIndex,
|
|
double baseDemand)
|
|
/*----------------------------------------------------------------
|
|
** Input: nodeIndex = node index
|
|
** demandIndex = demand category index
|
|
** baseDemand = baseline demand value
|
|
** Output: none
|
|
** Returns: error code
|
|
** Purpose: sets the baseline value for a node's demand category
|
|
**----------------------------------------------------------------
|
|
*/
|
|
{
|
|
Pdemand d;
|
|
int n = 1;
|
|
|
|
// Check for valid arguments
|
|
if (!p->Openflag) return 102;
|
|
if (nodeIndex <= 0 || nodeIndex > p->network.Nnodes) return 203;
|
|
|
|
// Set baseline demand for specified category
|
|
if (nodeIndex <= p->network.Njuncs)
|
|
{
|
|
for (d = p->network.Node[nodeIndex].D; n < demandIndex && d->next != NULL;
|
|
d = d->next) n++;
|
|
if (n != demandIndex) return 253;
|
|
d->Base = baseDemand / p->Ucf[FLOW];
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int DLLEXPORT EN_getdemandname(EN_Project p, int nodeIndex, int demandIndex,
|
|
char *demandName)
|
|
/*----------------------------------------------------------------
|
|
** Input: nodeIndex = node index
|
|
** demandIndex = demand category index
|
|
** Output: demandname = demand category name
|
|
** Returns: error code
|
|
** Purpose: retrieves the name assigned to a node's demand category
|
|
**----------------------------------------------------------------
|
|
*/
|
|
{
|
|
Pdemand d;
|
|
int n = 1;
|
|
|
|
strcpy(demandName, "");
|
|
|
|
// Check for valid arguments
|
|
if (!p->Openflag) return 102;
|
|
if (nodeIndex <= 0 || nodeIndex > p->network.Njuncs) return 203;
|
|
|
|
// Locate demand category record and retrieve its name
|
|
for (d = p->network.Node[nodeIndex].D;
|
|
n < demandIndex && d->next != NULL; d = d->next) n++;
|
|
if (n != demandIndex) return 253;
|
|
strcpy(demandName, d->Name);
|
|
return 0;
|
|
}
|
|
|
|
int DLLEXPORT EN_setdemandname(EN_Project p, int nodeIndex, int demandIndex,
|
|
char *demandName)
|
|
/*----------------------------------------------------------------
|
|
** Input: nodeIndex = node index
|
|
** demandIndex = demand category index
|
|
** demandName = name of demand category
|
|
** Output: none
|
|
** Returns: error code
|
|
** Purpose: assigns a name to a node's demand category
|
|
**----------------------------------------------------------------
|
|
*/
|
|
{
|
|
Pdemand d;
|
|
int n = 1;
|
|
|
|
// Check for valid arguments
|
|
if (!p->Openflag) return 102;
|
|
if (nodeIndex <= 0 || nodeIndex > p->network.Njuncs) return 203;
|
|
|
|
// Locate demand category record and assign demandName to it
|
|
for (d = p->network.Node[nodeIndex].D;
|
|
n < demandIndex && d->next != NULL; d = d->next) n++;
|
|
if (n != demandIndex) return 253;
|
|
strncpy(d->Name, demandName, MAXMSG);
|
|
return 0;
|
|
}
|
|
|
|
int DLLEXPORT EN_getdemandpattern(EN_Project p, int nodeIndex, int demandIndex,
|
|
int *patIndex)
|
|
/*----------------------------------------------------------------
|
|
** Input: nodeIndex = node index
|
|
** demandIndex = demand category index
|
|
** Output: patIndex = time pattern index
|
|
** Returns: error code
|
|
** Purpose: retrieves the time pattern assigned to a node's
|
|
** demand category
|
|
**----------------------------------------------------------------
|
|
*/
|
|
{
|
|
Pdemand d;
|
|
int n = 1;
|
|
|
|
// 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;
|
|
n < demandIndex && d->next != NULL; d = d->next) n++;
|
|
if (n != demandIndex) return 253;
|
|
*patIndex = d->Pat;
|
|
return 0;
|
|
}
|
|
|
|
int DLLEXPORT EN_setdemandpattern(EN_Project p, int nodeIndex, int demandIndex,
|
|
int patIndex)
|
|
/*----------------------------------------------------------------
|
|
** Input: nodeIndex = node index
|
|
** demandIndex = demand category index
|
|
** patIndex = time pattern index
|
|
** Output: none
|
|
** Returns: error code
|
|
** Purpose: assigns a time pattern to a node's demand category
|
|
**----------------------------------------------------------------
|
|
*/
|
|
{
|
|
Network *net = &p->network;
|
|
|
|
Pdemand d;
|
|
int n = 1;
|
|
|
|
// Check for valid arguments
|
|
if (!p->Openflag) return 102;
|
|
if (nodeIndex <= 0 || nodeIndex > net->Nnodes) return 203;
|
|
if (patIndex <= 0 || patIndex > net->Npats) return 205;
|
|
|
|
// Locate demand category record and assign time pattern to it
|
|
if (nodeIndex <= net->Njuncs)
|
|
{
|
|
for (d = net->Node[nodeIndex].D;
|
|
n < demandIndex && d->next != NULL; d = d->next) n++;
|
|
if (n != demandIndex) return 253;
|
|
d->Pat = patIndex;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/********************************************************************
|
|
|
|
Link Functions
|
|
|
|
********************************************************************/
|
|
|
|
int DLLEXPORT EN_addlink(EN_Project p, char *id, int linkType,
|
|
char *fromNode, char *toNode)
|
|
/*----------------------------------------------------------------
|
|
** Input: id = link ID name
|
|
** type = link type (see EN_LinkType)
|
|
** fromNode = name of link's starting node
|
|
** toNode = name of link's ending node
|
|
** Output: none
|
|
** Returns: error code
|
|
** Purpose: adds a new link to a project
|
|
**----------------------------------------------------------------
|
|
*/
|
|
{
|
|
Network *net = &p->network;
|
|
Hydraul *hyd = &p->hydraul;
|
|
|
|
int i, n, size, errcode;
|
|
int n1, n2;
|
|
Slink *link;
|
|
Spump *pump;
|
|
|
|
// Cannot modify network structure while solvers are active
|
|
if (!p->Openflag) return 102;
|
|
if (p->hydraul.OpenHflag || p->quality.OpenQflag) return 262;
|
|
|
|
// Check if a link with same id already exists
|
|
if (EN_getlinkindex(p, id, &i) == 0) return 215;
|
|
|
|
// Check for valid link type
|
|
if (linkType < CVPIPE || linkType > GPV) return 251;
|
|
|
|
// Lookup the link's from and to nodes
|
|
n1 = hashtable_find(net->NodeHashTable, fromNode);
|
|
n2 = hashtable_find(net->NodeHashTable, toNode);
|
|
if (n1 == 0 || n2 == 0) return 203;
|
|
|
|
// Check that id name is not too long
|
|
if (strlen(id) > MAXID) return 250;
|
|
|
|
// Check that valve link has legal connections
|
|
if (linkType > PUMP)
|
|
{
|
|
errcode = valvecheck(p, linkType, n1, n2);
|
|
if (errcode) return errcode;
|
|
}
|
|
|
|
// Grow link-related arrays to accomodate the new link
|
|
net->Nlinks++;
|
|
n = net->Nlinks;
|
|
size = (n + 1) * sizeof(Slink);
|
|
net->Link = (Slink *)realloc(net->Link, size);
|
|
size = (n + 1) * sizeof(double);
|
|
hyd->LinkFlow = (double *)realloc(hyd->LinkFlow, size);
|
|
hyd->LinkSetting = (double *)realloc(hyd->LinkSetting, size);
|
|
size = (n + 1) * sizeof(StatusType);
|
|
hyd->LinkStatus = (StatusType *)realloc(hyd->LinkStatus, size);
|
|
|
|
// Set properties for the new link
|
|
link = &net->Link[n];
|
|
strncpy(link->ID, id, MAXID);
|
|
|
|
if (linkType <= PIPE) net->Npipes++;
|
|
else if (linkType == PUMP)
|
|
{
|
|
// Grow pump array to accomodate the new link
|
|
net->Npumps++;
|
|
size = (net->Npumps + 1) * sizeof(Spump);
|
|
net->Pump = (Spump *)realloc(net->Pump, size);
|
|
pump = &net->Pump[net->Npumps];
|
|
pump->Link = n;
|
|
pump->Ptype = NOCURVE;
|
|
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.TotalCost = MISSING;
|
|
}
|
|
else
|
|
{
|
|
// Grow valve array to accomodate the new link
|
|
net->Nvalves++;
|
|
size = (net->Nvalves + 1) * sizeof(Svalve);
|
|
net->Valve = (Svalve *)realloc(net->Valve, size);
|
|
net->Valve[net->Nvalves].Link = n;
|
|
}
|
|
|
|
link->Type = linkType;
|
|
link->N1 = n1;
|
|
link->N2 = n2;
|
|
link->Status = OPEN;
|
|
|
|
if (linkType == PUMP)
|
|
{
|
|
link->Kc = 1.0; // Speed factor
|
|
link->Km = 0.0; // Horsepower
|
|
link->Len = 0.0;
|
|
}
|
|
else if (linkType <= 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->Status = ACTIVE;
|
|
}
|
|
link->Kb = 0;
|
|
link->Kw = 0;
|
|
link->R = 0;
|
|
link->Rc = 0;
|
|
link->Rpt = 0;
|
|
strcpy(link->Comment, "");
|
|
|
|
hashtable_insert(net->LinkHashTable, link->ID, n);
|
|
return 0;
|
|
}
|
|
|
|
int DLLEXPORT EN_deletelink(EN_Project p, int index, int actionCode)
|
|
/*----------------------------------------------------------------
|
|
** Input: index = index of the link to delete
|
|
** actionCode = how to treat controls that contain the link:
|
|
** EN_UNCONDITIONAL deletes all such controls plus the link,
|
|
** EN_CONDITIONAL does not delete the link if it appears
|
|
** in a control and returns an error code
|
|
** Output: none
|
|
** Returns: error code
|
|
** Purpose: deletes a link from a project
|
|
**----------------------------------------------------------------
|
|
*/
|
|
{
|
|
Network *net = &p->network;
|
|
|
|
int i;
|
|
int pumpindex;
|
|
int valveindex;
|
|
int linkType;
|
|
Slink *link;
|
|
|
|
// Cannot modify network structure while solvers are active
|
|
if (!p->Openflag) return 102;
|
|
if (p->hydraul.OpenHflag || p->quality.OpenQflag) return 262;
|
|
|
|
// Check that link exists
|
|
if (index <= 0 || index > net->Nlinks) return 204;
|
|
if (actionCode < EN_UNCONDITIONAL || actionCode > EN_CONDITIONAL) return 251;
|
|
|
|
// Deletion will be cancelled if link appears in any controls
|
|
if (actionCode == EN_CONDITIONAL)
|
|
{
|
|
actionCode = incontrols(p, LINK, index);
|
|
if (actionCode > 0) return 261;
|
|
}
|
|
|
|
// Get references to the link and its type
|
|
link = &net->Link[index];
|
|
EN_getlinktype(p, index, &linkType);
|
|
|
|
// Remove link from its hash table
|
|
hashtable_delete(net->LinkHashTable, link->ID);
|
|
|
|
// Shift position of higher entries in Link array down one
|
|
for (i = index; i <= net->Nlinks - 1; i++)
|
|
{
|
|
net->Link[i] = net->Link[i + 1];
|
|
// ... update link's entry in the hash table
|
|
hashtable_update(net->LinkHashTable, net->Link[i].ID, i);
|
|
}
|
|
|
|
// Adjust references to higher numbered links for pumps & valves
|
|
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;
|
|
}
|
|
|
|
// Delete any pump associated with the deleted link
|
|
if (linkType == PUMP)
|
|
{
|
|
pumpindex = findpump(net, index);
|
|
for (i = pumpindex; i <= net->Npumps - 1; i++)
|
|
{
|
|
net->Pump[i] = net->Pump[i + 1];
|
|
}
|
|
net->Npumps--;
|
|
}
|
|
|
|
// Delete any valve (linkType > PUMP) associated with the deleted link
|
|
if (linkType > PUMP)
|
|
{
|
|
valveindex = findvalve(net, index);
|
|
for (i = valveindex; i <= net->Nvalves - 1; i++)
|
|
{
|
|
net->Valve[i] = net->Valve[i + 1];
|
|
}
|
|
net->Nvalves--;
|
|
}
|
|
|
|
// Delete any control containing the link
|
|
for (i = net->Ncontrols; i >= 1; i--)
|
|
{
|
|
if (net->Control[i].Link == index) EN_deletecontrol(p, i);
|
|
}
|
|
|
|
// Adjust higher numbered link indices in remaining controls
|
|
for (i = 1; i <= net->Ncontrols; i++)
|
|
{
|
|
if (net->Control[i].Link > index) net->Control[i].Link--;
|
|
}
|
|
|
|
// Make necessary adjustments to rule-based controls
|
|
adjustrules(p, EN_R_LINK, index); // see RULES.C
|
|
|
|
// Reduce link count by one
|
|
net->Nlinks--;
|
|
return 0;
|
|
}
|
|
|
|
int DLLEXPORT EN_getlinkindex(EN_Project p, char *id, int *index)
|
|
/*----------------------------------------------------------------
|
|
** Input: id = link ID name
|
|
** Output: index = link index
|
|
** Returns: error code
|
|
** Purpose: retrieves the index of a link
|
|
**----------------------------------------------------------------
|
|
*/
|
|
{
|
|
*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_Project p, int index, char *id)
|
|
/*----------------------------------------------------------------
|
|
** Input: index = link index
|
|
** Output: id = link ID name
|
|
** Returns: error code
|
|
** Purpose: retrieves the ID name of a link
|
|
**----------------------------------------------------------------
|
|
*/
|
|
{
|
|
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_setlinkid(EN_Project p, int index, char *newid)
|
|
/*----------------------------------------------------------------
|
|
** Input: index = link index
|
|
** id = link ID name
|
|
** Output: none
|
|
** Returns: error code
|
|
** Purpose: sets the ID name of a link
|
|
**----------------------------------------------------------------
|
|
*/
|
|
{
|
|
Network *net = &p->network;
|
|
size_t n;
|
|
|
|
// Check for valid arguments
|
|
if (index <= 0 || index > net->Nlinks) return 204;
|
|
n = strlen(newid);
|
|
if (n < 1 || n > MAXID) return 211;
|
|
if (strcspn(newid, " ;") < n) return 211;
|
|
|
|
// Check if another link with same name exists
|
|
if (hashtable_find(net->LinkHashTable, newid) > 0) return 215;
|
|
|
|
// Replace the existing link ID with the new value
|
|
hashtable_delete(net->LinkHashTable, net->Link[index].ID);
|
|
strncpy(net->Link[index].ID, newid, MAXID);
|
|
hashtable_insert(net->LinkHashTable, net->Link[index].ID, index);
|
|
return 0;
|
|
}
|
|
|
|
int DLLEXPORT EN_getlinktype(EN_Project p, int index, int *linkType)
|
|
/*----------------------------------------------------------------
|
|
** Input: index = link index
|
|
** Output: linkType = link type (see EN_LinkType)
|
|
** Returns: error code
|
|
** Purpose: retrieves the type code of a link
|
|
**----------------------------------------------------------------
|
|
*/
|
|
{
|
|
*linkType = -1;
|
|
if (!p->Openflag) return 102;
|
|
if (index < 1 || index > p->network.Nlinks) return 204;
|
|
*linkType = p->network.Link[index].Type;
|
|
return 0;
|
|
}
|
|
|
|
int DLLEXPORT EN_setlinktype(EN_Project p, int *index, int linkType, int actionCode)
|
|
/*----------------------------------------------------------------
|
|
** Input: index = link index
|
|
** linkType = new link type (see EN_LinkType)
|
|
** actionCode = how to treat controls that contain the link:
|
|
** EN_UNCONDITIONAL deletes all such controls,
|
|
** EN_CONDITIONAL cancels the type change if the link appears
|
|
** in a control and returns an error code
|
|
** Output: none
|
|
** Returns: error code
|
|
** Purpose: changes the type of a particular link (e.g. pipe to pump)
|
|
**----------------------------------------------------------------
|
|
*/
|
|
{
|
|
Network *net = &p->network;
|
|
|
|
int i = *index, n1, n2;
|
|
char id[MAXID + 1];
|
|
char id1[MAXID + 1];
|
|
char id2[MAXID + 1];
|
|
int errcode;
|
|
int oldType;
|
|
|
|
// Cannot modify network structure while solvers are active
|
|
if (!p->Openflag) return 102;
|
|
if (p->hydraul.OpenHflag || p->quality.OpenQflag) return 262;
|
|
|
|
// Check for valid input parameters
|
|
if (linkType < 0 || linkType > GPV || actionCode < EN_UNCONDITIONAL ||
|
|
actionCode > EN_CONDITIONAL)
|
|
{
|
|
return 251;
|
|
}
|
|
|
|
// Check for valid link index
|
|
if (i <= 0 || i > net->Nlinks) return 204;
|
|
|
|
// Check if current link type equals new type
|
|
EN_getlinktype(p, i, &oldType);
|
|
if (oldType == linkType) return 0;
|
|
|
|
// Type change will be cancelled if link appears in any controls
|
|
if (actionCode == EN_CONDITIONAL)
|
|
{
|
|
actionCode = incontrols(p, LINK, i);
|
|
if (actionCode > 0) return 261;
|
|
}
|
|
|
|
// Pipe changing from or to having a check valve
|
|
if (oldType <= PIPE && linkType <= PIPE)
|
|
{
|
|
net->Link[i].Type = linkType;
|
|
if (linkType == CVPIPE) net->Link[i].Status = OPEN;
|
|
return 0;
|
|
}
|
|
|
|
// Get ID's of link & its end nodes
|
|
EN_getlinkid(p, i, id);
|
|
EN_getlinknodes(p, i, &n1, &n2);
|
|
EN_getnodeid(p, n1, id1);
|
|
EN_getnodeid(p, n2, id2);
|
|
|
|
// Check for illegal valve connections
|
|
errcode = valvecheck(p, linkType, n1, n2);
|
|
if (errcode) return errcode;
|
|
|
|
// Delete the original link (and any controls containing it)
|
|
EN_deletelink(p, i, actionCode);
|
|
|
|
// Create a new link of new type and old id
|
|
errcode = EN_addlink(p, id, linkType, id1, id2);
|
|
|
|
// Find the index of this new link
|
|
EN_getlinkindex(p, id, index);
|
|
return errcode;
|
|
}
|
|
|
|
int DLLEXPORT EN_getlinknodes(EN_Project p, int index, int *node1, int *node2)
|
|
/*----------------------------------------------------------------
|
|
** Input: index = link index
|
|
** Output: node1 = index of link's starting node
|
|
** node2 = index of link's ending node
|
|
** Returns: error code
|
|
** Purpose: retrieves the start and end nodes of a link
|
|
**----------------------------------------------------------------
|
|
*/
|
|
{
|
|
*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_setlinknodes(EN_Project p, int index, int node1, int node2)
|
|
/*----------------------------------------------------------------
|
|
** Input: index = link index
|
|
** node1 = index of link's new starting node
|
|
** node2 = index of link's new ending node
|
|
** Returns: error code
|
|
** Purpose: sets the start and end nodes of a link
|
|
**----------------------------------------------------------------
|
|
*/
|
|
{
|
|
Network *net = &p->network;
|
|
int type, errcode;
|
|
|
|
// Cannot modify network structure while solvers are active
|
|
if (p->hydraul.OpenHflag || p->quality.OpenQflag) return 262;
|
|
|
|
// Check that nodes exist
|
|
if (node1 < 0 || node1 > net->Nnodes) return 203;
|
|
if (node2 < 0 || node2 > net->Nnodes) return 203;
|
|
|
|
// Check that nodes are not the same
|
|
if (node1 == node2) return 222;
|
|
|
|
// Check for illegal valve connection
|
|
type = net->Link[index].Type;
|
|
if (type > PUMP)
|
|
{
|
|
errcode = valvecheck(p, type, node1, node2);
|
|
if (errcode) return errcode;
|
|
}
|
|
|
|
// Assign new end nodes to link
|
|
net->Link[index].N1 = node1;
|
|
net->Link[index].N2 = node2;
|
|
return 0;
|
|
}
|
|
|
|
int DLLEXPORT EN_getlinkvalue(EN_Project p, int index, int property, double *value)
|
|
/*----------------------------------------------------------------
|
|
** Input: index = link index
|
|
** property = link property code (see EN_LinkProperty)
|
|
** Output: value = link property value
|
|
** Returns: error code
|
|
** Purpose: retrieves a property value for a link
|
|
**----------------------------------------------------------------
|
|
*/
|
|
{
|
|
Network *net = &p->network;
|
|
Hydraul *hyd = &p->hydraul;
|
|
|
|
double a, h, q, v = 0.0;
|
|
int pmp;
|
|
Slink *Link = net->Link;
|
|
Spump *Pump = net->Pump;
|
|
double *Ucf = p->Ucf;
|
|
double *LinkFlow = hyd->LinkFlow;
|
|
double *LinkSetting = hyd->LinkSetting;
|
|
|
|
// Check for valid arguments
|
|
*value = 0.0;
|
|
if (!p->Openflag) return 102;
|
|
if (index <= 0 || index > net->Nlinks) return 204;
|
|
|
|
// Retrieve called-for property
|
|
switch (property)
|
|
{
|
|
case EN_DIAMETER:
|
|
if (Link[index].Type == 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 <= 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 != 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].Status <= CLOSED) v = 0.0;
|
|
else v = 1.0;
|
|
break;
|
|
|
|
case EN_INITSETTING:
|
|
if (Link[index].Type == PIPE || Link[index].Type == CVPIPE)
|
|
{
|
|
return EN_getlinkvalue(p, index, EN_ROUGHNESS, value);
|
|
}
|
|
v = Link[index].Kc;
|
|
switch (Link[index].Type)
|
|
{
|
|
case PRV:
|
|
case PSV:
|
|
case PBV:
|
|
v *= Ucf[PRESSURE];
|
|
break;
|
|
case 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:
|
|
if (hyd->LinkStatus[index] <= CLOSED) v = 0.0;
|
|
else v = LinkFlow[index] * Ucf[FLOW];
|
|
break;
|
|
|
|
case EN_VELOCITY:
|
|
if (Link[index].Type == PUMP) v = 0.0;
|
|
else if (hyd->LinkStatus[index] <= CLOSED) v = 0.0;
|
|
else
|
|
{
|
|
q = ABS(LinkFlow[index]);
|
|
a = PI * SQR(Link[index].Diam) / 4.0;
|
|
v = q / a * Ucf[VELOCITY];
|
|
}
|
|
break;
|
|
|
|
case EN_HEADLOSS:
|
|
if (hyd->LinkStatus[index] <= CLOSED) v = 0.0;
|
|
else
|
|
{
|
|
h = hyd->NodeHead[Link[index].N1] - hyd->NodeHead[Link[index].N2];
|
|
if (Link[index].Type != 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 == PIPE || Link[index].Type == CVPIPE)
|
|
{
|
|
return EN_getlinkvalue(p, index, EN_ROUGHNESS, value);
|
|
}
|
|
if (LinkSetting[index] == MISSING) v = 0.0;
|
|
else v = LinkSetting[index];
|
|
switch (Link[index].Type)
|
|
{
|
|
case PRV:
|
|
case PSV:
|
|
case PBV:
|
|
v *= Ucf[PRESSURE];
|
|
break;
|
|
case 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 == PUMP)
|
|
{
|
|
v = (double)Pump[findpump(&p->network, index)].Upat;
|
|
}
|
|
break;
|
|
|
|
case EN_PUMP_STATE:
|
|
v = hyd->LinkStatus[index];
|
|
|
|
if (Link[index].Type == PUMP)
|
|
{
|
|
pmp = findpump(net, index);
|
|
if (hyd->LinkStatus[index] >= OPEN)
|
|
{
|
|
if (hyd->LinkFlow[index] > hyd->LinkSetting[index] * Pump[pmp].Qmax)
|
|
{
|
|
v = XFLOW;
|
|
}
|
|
if (hyd->LinkFlow[index] < 0.0) v = XHEAD;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case EN_PUMP_EFFIC:
|
|
getenergy(p, index, &a, &v);
|
|
break;
|
|
|
|
case EN_PUMP_POWER:
|
|
v = 0;
|
|
if (Link[index].Type == PUMP)
|
|
{
|
|
pmp = findpump(net, index);
|
|
if (Pump[pmp].Ptype == CONST_HP) v = Link[index].Km; // Power in HP or KW
|
|
}
|
|
break;
|
|
|
|
case EN_PUMP_HCURVE:
|
|
if (Link[index].Type == PUMP)
|
|
{
|
|
v = (double)Pump[findpump(&p->network, index)].Hcurve;
|
|
}
|
|
break;
|
|
|
|
case EN_PUMP_ECURVE:
|
|
if (Link[index].Type == PUMP)
|
|
{
|
|
v = (double)Pump[findpump(&p->network, index)].Ecurve;
|
|
}
|
|
break;
|
|
|
|
case EN_PUMP_ECOST:
|
|
if (Link[index].Type == PUMP)
|
|
{
|
|
v = (double)Pump[findpump(&p->network, index)].Ecost;
|
|
}
|
|
break;
|
|
|
|
case EN_PUMP_EPAT:
|
|
if (Link[index].Type == PUMP)
|
|
{
|
|
v = (double)Pump[findpump(&p->network, index)].Epat;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
return 251;
|
|
}
|
|
*value = (double)v;
|
|
return 0;
|
|
}
|
|
|
|
int DLLEXPORT EN_setlinkvalue(EN_Project p, int index, int property, double value)
|
|
/*----------------------------------------------------------------
|
|
** Input: index = link index
|
|
** property = link property code (see EN_LinkProperty)
|
|
** value = property value
|
|
** Output: none
|
|
** Returns: error code
|
|
** Purpose: sets a property value for a link
|
|
**----------------------------------------------------------------
|
|
*/
|
|
{
|
|
Network *net = &p->network;
|
|
Hydraul *hyd = &p->hydraul;
|
|
Quality *qual = &p->quality;
|
|
|
|
Slink *Link = net->Link;
|
|
double *Ucf = p->Ucf;
|
|
double *LinkSetting = hyd->LinkSetting;
|
|
char s;
|
|
double r;
|
|
int pumpIndex, patIndex, curveIndex;
|
|
|
|
if (!p->Openflag) return 102;
|
|
if (index <= 0 || index > net->Nlinks) return 204;
|
|
switch (property)
|
|
{
|
|
case EN_DIAMETER:
|
|
if (Link[index].Type != PUMP)
|
|
{
|
|
if (value <= 0.0) return 211;
|
|
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 <= PIPE)
|
|
{
|
|
if (value <= 0.0) return 211;
|
|
Link[index].Len = value / Ucf[ELEV];
|
|
resistcoeff(p, index);
|
|
}
|
|
break;
|
|
|
|
case EN_ROUGHNESS:
|
|
if (Link[index].Type <= PIPE)
|
|
{
|
|
if (value <= 0.0) return 211;
|
|
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 != PUMP)
|
|
{
|
|
if (value <= 0.0) return 211;
|
|
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 == CVPIPE) return 207;
|
|
s = (char)ROUND(value);
|
|
if (s < 0 || s > 1) return 211;
|
|
if (property == EN_INITSTATUS)
|
|
{
|
|
setlinkstatus(p, index, s, &Link[index].Status, &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 211;
|
|
if (Link[index].Type == PIPE || Link[index].Type == CVPIPE)
|
|
{
|
|
return EN_setlinkvalue(p, index, EN_ROUGHNESS, value);
|
|
}
|
|
else
|
|
{
|
|
switch (Link[index].Type)
|
|
{
|
|
case PUMP:
|
|
break;
|
|
case PRV:
|
|
case PSV:
|
|
case PBV:
|
|
value /= Ucf[PRESSURE];
|
|
break;
|
|
case FCV:
|
|
value /= Ucf[FLOW];
|
|
break;
|
|
case TCV:
|
|
break;
|
|
case GPV:
|
|
return 207; // Cannot modify setting for GPV
|
|
default:
|
|
return 0;
|
|
}
|
|
if (property == EN_INITSETTING)
|
|
{
|
|
setlinksetting(p, index, value, &Link[index].Status, &Link[index].Kc);
|
|
}
|
|
else
|
|
{
|
|
setlinksetting(p, index, value, &hyd->LinkStatus[index],
|
|
&LinkSetting[index]);
|
|
}
|
|
}
|
|
break;
|
|
|
|
case EN_KBULK:
|
|
if (Link[index].Type <= PIPE)
|
|
{
|
|
Link[index].Kb = value / SECperDAY;
|
|
qual->Reactflag = 1;
|
|
}
|
|
break;
|
|
|
|
case EN_KWALL:
|
|
if (Link[index].Type <= PIPE)
|
|
{
|
|
Link[index].Kw = value / SECperDAY;
|
|
qual->Reactflag = 1;
|
|
}
|
|
break;
|
|
|
|
case EN_LINKPATTERN:
|
|
if (Link[index].Type == PUMP)
|
|
{
|
|
patIndex = ROUND(value);
|
|
if (patIndex <= 0 || patIndex > net->Npats) return 205;
|
|
pumpIndex = findpump(&p->network, index);
|
|
net->Pump[pumpIndex].Upat = patIndex;
|
|
}
|
|
break;
|
|
|
|
case EN_PUMP_POWER:
|
|
if (Link[index].Type == PUMP)
|
|
{
|
|
if (value <= 0.0) return 211;
|
|
pumpIndex = findpump(&p->network, index);
|
|
net->Pump[pumpIndex].Ptype = CONST_HP;
|
|
net->Pump[pumpIndex].Hcurve = 0;
|
|
net->Link[index].Km = value;
|
|
updatepumpparams(p, pumpIndex);
|
|
net->Pump[pumpIndex].R /= Ucf[POWER];
|
|
net->Pump[pumpIndex].Q0 /= Ucf[FLOW];
|
|
net->Pump[pumpIndex].Qmax /= Ucf[FLOW];
|
|
net->Pump[pumpIndex].Hmax /= Ucf[HEAD];
|
|
}
|
|
break;
|
|
|
|
case EN_PUMP_HCURVE:
|
|
if (Link[index].Type == PUMP)
|
|
{
|
|
return EN_setheadcurveindex(p, index, ROUND(value));
|
|
}
|
|
break;
|
|
|
|
case EN_PUMP_ECURVE:
|
|
if (Link[index].Type == PUMP)
|
|
{
|
|
curveIndex = ROUND(value);
|
|
if (curveIndex <= 0 || curveIndex > net->Ncurves) return 205;
|
|
pumpIndex = findpump(&p->network, index);
|
|
net->Pump[pumpIndex].Ecurve = curveIndex;
|
|
}
|
|
break;
|
|
|
|
case EN_PUMP_ECOST:
|
|
if (Link[index].Type == PUMP)
|
|
{
|
|
if (value < 0.0) return 211;
|
|
pumpIndex = findpump(&p->network, index);
|
|
net->Pump[pumpIndex].Ecost = value;
|
|
}
|
|
break;
|
|
|
|
case EN_PUMP_EPAT:
|
|
if (Link[index].Type == PUMP)
|
|
{
|
|
patIndex = ROUND(value);
|
|
if (patIndex <= 0 || patIndex > net->Npats) return 205;
|
|
pumpIndex = findpump(&p->network, index);
|
|
net->Pump[pumpIndex].Epat = patIndex;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
return 251;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int DLLEXPORT EN_setpipedata(EN_Project p, int index, double length,
|
|
double diam, double rough, double mloss)
|
|
/*----------------------------------------------------------------
|
|
** Input: index = pipe link index
|
|
** length = pipe length
|
|
** diam = pipe diameter
|
|
** rough = pipe roughness coefficient
|
|
** mloss = minor loss coefficient
|
|
** Output: none
|
|
** Returns: error code
|
|
** Purpose: sets several properties for a pipe link
|
|
**----------------------------------------------------------------
|
|
*/
|
|
{
|
|
Network *net = &p->network;
|
|
|
|
Slink *Link = net->Link;
|
|
double *Ucf = p->Ucf;
|
|
double diameter = diam;
|
|
|
|
// Check that pipe exists
|
|
if (!p->Openflag) return 102;
|
|
if (index <= 0 || index > net->Nlinks) return 204;
|
|
if (Link[index].Type > PIPE) return 0;
|
|
|
|
// Check for valid parameters
|
|
if (length <= 0.0 || diam <= 0.0 || rough <= 0.0 || mloss < 0.0) return 211;
|
|
|
|
// Assign parameters to pipe
|
|
Link[index].Len = length / Ucf[ELEV];
|
|
diameter /= Ucf[DIAM];
|
|
Link[index].Diam = diameter;
|
|
Link[index].Kc = rough;
|
|
if (p->hydraul.Formflag == DW) Link[index].Kc /= (1000.0 * Ucf[ELEV]);
|
|
|
|
// Update minor loss factor & pipe flow resistance
|
|
Link[index].Km = 0.02517 * mloss / SQR(Link[index].Diam) / SQR(Link[index].Diam);
|
|
resistcoeff(p, index);
|
|
return 0;
|
|
}
|
|
|
|
|
|
/********************************************************************
|
|
|
|
Pump Functions
|
|
|
|
********************************************************************/
|
|
|
|
int DLLEXPORT EN_getpumptype(EN_Project p, int linkIndex, int *pumpType)
|
|
/*----------------------------------------------------------------
|
|
** Input: linkIndex = index of a pump link
|
|
** Output: pumpType = type of pump head curve (see EN_PumpType)
|
|
** Returns: error code
|
|
** Purpose: retrieves the type of head curve used by a pump
|
|
**----------------------------------------------------------------
|
|
*/
|
|
{
|
|
Network *net = &p->network;
|
|
|
|
Slink *Link = net->Link;
|
|
Spump *Pump = net->Pump;
|
|
const int Nlinks = net->Nlinks;
|
|
|
|
*pumpType = -1;
|
|
if (!p->Openflag) return 102;
|
|
if (linkIndex < 1 || linkIndex > Nlinks) return 204;
|
|
if (PUMP != Link[linkIndex].Type) return 216;
|
|
*pumpType = Pump[findpump(&p->network, linkIndex)].Ptype;
|
|
return 0;
|
|
}
|
|
|
|
int DLLEXPORT EN_getheadcurveindex(EN_Project p, int linkIndex, int *curveIndex)
|
|
/*----------------------------------------------------------------
|
|
** Input: linkIndex = index of a pump link
|
|
** Output: curveIndex = index of a pump's head curve
|
|
** Returns: error code
|
|
** Purpose: retrieves the index of a pump's head curve
|
|
**----------------------------------------------------------------
|
|
*/
|
|
{
|
|
Network *net = &p->network;
|
|
|
|
Slink *Link = net->Link;
|
|
Spump *Pump = net->Pump;
|
|
const int Nlinks = net->Nlinks;
|
|
|
|
*curveIndex = 0;
|
|
if (!p->Openflag) return 102;
|
|
if (linkIndex < 1 || linkIndex > Nlinks) return 204;
|
|
if (PUMP != Link[linkIndex].Type) return 216;
|
|
*curveIndex = Pump[findpump(net, linkIndex)].Hcurve;
|
|
return 0;
|
|
}
|
|
|
|
int DLLEXPORT EN_setheadcurveindex(EN_Project p, int linkIndex, int curveIndex)
|
|
/*----------------------------------------------------------------
|
|
** Input: linkIndex = index of a pump link
|
|
** curveIndex = index of a curve
|
|
** Output: none
|
|
** Returns: error code
|
|
** Purpose: assigns a new head curve to a pump
|
|
**----------------------------------------------------------------
|
|
*/
|
|
{
|
|
Network *net = &p->network;
|
|
|
|
double *Ucf = p->Ucf;
|
|
int pumpIndex;
|
|
Spump *pump;
|
|
|
|
// Check for valid parameters
|
|
if (!p->Openflag) return 102;
|
|
if (linkIndex < 1 || linkIndex > net->Nlinks) return 204;
|
|
if (PUMP != net->Link[linkIndex].Type) return 0;
|
|
if (curveIndex <= 0 || curveIndex > net->Ncurves) return 206;
|
|
|
|
// Assign the new curve to the pump
|
|
pumpIndex = findpump(net, linkIndex);
|
|
pump = &p->network.Pump[pumpIndex];
|
|
pump->Ptype = NOCURVE;
|
|
pump->Hcurve = curveIndex;
|
|
|
|
// Update the pump curve's parameters and convert their units
|
|
updatepumpparams(p, pumpIndex);
|
|
if (pump->Ptype == POWER_FUNC)
|
|
{
|
|
pump->H0 /= Ucf[HEAD];
|
|
pump->R *= (pow(Ucf[FLOW], pump->N) / Ucf[HEAD]);
|
|
}
|
|
pump->Q0 /= Ucf[FLOW];
|
|
pump->Qmax /= Ucf[FLOW];
|
|
pump->Hmax /= Ucf[HEAD];
|
|
|
|
// Designate the newly assigned curve as being a Pump Curve
|
|
p->network.Curve[curveIndex].Type = PUMP_CURVE;
|
|
return 0;
|
|
}
|
|
|
|
/********************************************************************
|
|
|
|
Time Pattern Functions
|
|
|
|
********************************************************************/
|
|
|
|
int DLLEXPORT EN_addpattern(EN_Project p, char *id)
|
|
/*----------------------------------------------------------------
|
|
** Input: id = time pattern ID name
|
|
** Output: none
|
|
** Returns: error code
|
|
** Purpose: adds a new time pattern to a project
|
|
**----------------------------------------------------------------
|
|
*/
|
|
{
|
|
Network *net = &p->network;
|
|
Parser *parser = &p->parser;
|
|
Hydraul *hyd = &p->hydraul;
|
|
|
|
int i, n, err = 0;
|
|
Spattern *pat;
|
|
|
|
// Check if a pattern with same id already exists
|
|
if (!p->Openflag) return 102;
|
|
if (EN_getpatternindex(p, id, &i) == 0) return 215;
|
|
|
|
// Check that id name is not too long
|
|
if (strlen(id) > MAXID) return 250;
|
|
|
|
// Expand the project's array of patterns
|
|
n = net->Npats + 1;
|
|
net->Pattern = (Spattern *)realloc(net->Pattern, (n + 1) * sizeof(Spattern));
|
|
|
|
// Assign properties to the new pattern
|
|
pat = &net->Pattern[n];
|
|
strcpy(pat->ID, id);
|
|
pat->Length = 1;
|
|
pat->F = (double *)calloc(1, sizeof(double));
|
|
if (pat->F == NULL) err = 1;
|
|
else pat->F[0] = 1.0;
|
|
|
|
// Abort if memory allocation error
|
|
if (err)
|
|
{
|
|
free(pat->F);
|
|
return 101;
|
|
}
|
|
|
|
// Update the number of patterns
|
|
net->Npats = n;
|
|
parser->MaxPats = n;
|
|
|
|
// Make new pattern be default demand pattern if name matches
|
|
if (strcmp(id, parser->DefPatID) == 0) hyd->DefPat = n;
|
|
return 0;
|
|
}
|
|
|
|
int DLLEXPORT EN_getpatternindex(EN_Project p, char *id, int *index)
|
|
/*----------------------------------------------------------------
|
|
** Input: id = time pattern name
|
|
** Output: index = time pattern index
|
|
** Returns: error code
|
|
** Purpose: retrieves the index of a time pattern
|
|
**----------------------------------------------------------------
|
|
*/
|
|
{
|
|
int i;
|
|
|
|
*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_Project p, int index, char *id)
|
|
/*----------------------------------------------------------------
|
|
** Input: index = time pattern index
|
|
** Output: id = time pattern ID name
|
|
** Returns: error code
|
|
** Purpose: retrieves the ID name of a time pattern
|
|
**----------------------------------------------------------------
|
|
*/
|
|
{
|
|
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_Project p, int index, int *len)
|
|
/*----------------------------------------------------------------
|
|
** Input: index = time pattern index
|
|
** Output: len = number of periods in a time pattern
|
|
** Returns: error code
|
|
** Purpose: retrieves the number of time periods in a time pattern
|
|
**----------------------------------------------------------------
|
|
*/
|
|
{
|
|
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_Project p, int index, int period, double *value)
|
|
/*----------------------------------------------------------------
|
|
** Input: index = time pattern index
|
|
** period = time pattern period
|
|
** Output: value = pattern factor for a particular time period
|
|
** Returns: error code
|
|
** Purpose: retrieves the pattern factor for a specific time period
|
|
** in a time pattern
|
|
**----------------------------------------------------------------
|
|
*/
|
|
{
|
|
*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 = (double)p->network.Pattern[index].F[period - 1];
|
|
return 0;
|
|
}
|
|
|
|
int DLLEXPORT EN_setpatternvalue(EN_Project p, int index, int period, double value)
|
|
/*----------------------------------------------------------------
|
|
** Input: index = time pattern index
|
|
** period = time pattern period
|
|
** value = pattern factor for a particular time period
|
|
** Output: none
|
|
** Returns: error code
|
|
** Purpose: sets the pattern factor for a specific time period
|
|
** in a time pattern
|
|
**----------------------------------------------------------------
|
|
*/
|
|
{
|
|
Network *net = &p->network;
|
|
Spattern *Pattern = net->Pattern;
|
|
|
|
if (!p->Openflag) return 102;
|
|
if (index <= 0 || index > net->Npats) return 205;
|
|
if (period <= 0 || period > Pattern[index].Length) return 251;
|
|
Pattern[index].F[period - 1] = value;
|
|
return 0;
|
|
}
|
|
|
|
int DLLEXPORT EN_getaveragepatternvalue(EN_Project p, int index, double *value)
|
|
/*----------------------------------------------------------------
|
|
** Input: index = time pattern index
|
|
** Output: value = average of a time pattern's factors
|
|
** Returns: error code
|
|
** Purpose: retrieves the average of all pattern factors for a time pattern
|
|
**----------------------------------------------------------------
|
|
*/
|
|
{
|
|
Network *net = &p->network;
|
|
|
|
Spattern *Pattern = net->Pattern;
|
|
int i;
|
|
|
|
*value = 0.0;
|
|
if (!p->Openflag) return 102;
|
|
if (index < 1 || index > net->Npats) return 205;
|
|
for (i = 0; i < Pattern[index].Length; i++)
|
|
{
|
|
*value += (double)Pattern[index].F[i];
|
|
}
|
|
*value /= (double)Pattern[index].Length;
|
|
return 0;
|
|
}
|
|
|
|
int DLLEXPORT EN_setpattern(EN_Project p, int index, double *values, int len)
|
|
/*----------------------------------------------------------------
|
|
** Input: index = time pattern index
|
|
** values = an array of pattern factor values
|
|
** len = number of time periods contained in f
|
|
** Output: none
|
|
** Returns: error code
|
|
** Purpose: replaces the pattern factors in a time pattern
|
|
**----------------------------------------------------------------
|
|
*/
|
|
{
|
|
Network *net = &p->network;
|
|
|
|
int j;
|
|
Spattern *Pattern = net->Pattern;
|
|
|
|
// Check for valid arguments
|
|
if (!p->Openflag) return 102;
|
|
if (index <= 0 || index > net->Npats) return 205;
|
|
if (len <= 0) return 251;
|
|
|
|
// Re-set number of time periods & reallocate memory for multipliers
|
|
Pattern[index].Length = len;
|
|
Pattern[index].F = (double *)realloc(Pattern[index].F, len * sizeof(double));
|
|
if (Pattern[index].F == NULL) return 101;
|
|
|
|
// Load multipliers into pattern
|
|
for (j = 0; j < len; j++) Pattern[index].F[j] = values[j];
|
|
return 0;
|
|
}
|
|
|
|
/********************************************************************
|
|
|
|
Data Curve Functions
|
|
|
|
********************************************************************/
|
|
|
|
int DLLEXPORT EN_addcurve(EN_Project p, char *id)
|
|
/*----------------------------------------------------------------
|
|
** Input: id = data curve ID name
|
|
** Output: none
|
|
** Returns: error code
|
|
** Purpose: adds a new data curve to a project
|
|
**----------------------------------------------------------------
|
|
*/
|
|
{
|
|
Network *net = &p->network;
|
|
|
|
int i, n, err = 0;
|
|
Scurve *curve;
|
|
|
|
// Check if a curve with same id already exists
|
|
if (!p->Openflag) return 102;
|
|
if (EN_getcurveindex(p, id, &i) == 0) return 215;
|
|
|
|
// Check that id name is not too long
|
|
if (strlen(id) > MAXID) return 250;
|
|
|
|
// Expand the array of curves
|
|
n = net->Ncurves + 1;
|
|
net->Curve = (Scurve *) realloc(net->Curve, (n + 1) * sizeof(Scurve));
|
|
|
|
// Set the properties of the new curve
|
|
curve = &net->Curve[n];
|
|
strcpy(curve->ID, id);
|
|
curve->Npts = 1;
|
|
curve->Type = GENERIC_CURVE;
|
|
curve->X = (double *)calloc(1, sizeof(double));
|
|
curve->Y = (double *)calloc(1, sizeof(double));
|
|
if (curve->X == NULL) err = 1;
|
|
else if (curve->Y == NULL) err = 1;
|
|
else
|
|
{
|
|
curve->X[0] = 1.0;
|
|
curve->Y[0] = 1.0;
|
|
}
|
|
|
|
// Abort if memory allocation error
|
|
if (err)
|
|
{
|
|
free(curve->X);
|
|
free(curve->Y);
|
|
return 101;
|
|
}
|
|
|
|
// Update the number of curves
|
|
net->Ncurves = n;
|
|
p->parser.MaxCurves = n;
|
|
return 0;
|
|
}
|
|
|
|
int DLLEXPORT EN_getcurveindex(EN_Project p, char *id, int *index)
|
|
/*----------------------------------------------------------------
|
|
** Input: id = data curve name
|
|
** Output: index = data curve index
|
|
** Returns: error code
|
|
** Purpose: retrieves the index of a data curve
|
|
**----------------------------------------------------------------
|
|
*/
|
|
{
|
|
int i;
|
|
|
|
*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_Project p, int index, char *id)
|
|
/*----------------------------------------------------------------
|
|
** Input: index = data curve index
|
|
** Output: id = data curve ID name
|
|
** Returns: error code
|
|
** Purpose: retrieves the name of a data curve
|
|
**----------------------------------------------------------------
|
|
*/
|
|
{
|
|
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_Project p, int index, int *len)
|
|
/*----------------------------------------------------------------
|
|
** Input: index = data curve index
|
|
** Output: len = number of points in a data curve
|
|
** Returns: error code
|
|
** Purpose: retrieves the number of points in a data curve
|
|
**----------------------------------------------------------------
|
|
*/
|
|
{
|
|
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_getcurvetype(EN_Project p, int index, int *type)
|
|
/*----------------------------------------------------------------
|
|
** Input: index = data curve index
|
|
** Output: type = type of data curve (see EN_CurveType)
|
|
** Returns: error code
|
|
** Purpose: retrieves the type assigned to a data curve
|
|
**----------------------------------------------------------------
|
|
*/
|
|
{
|
|
Network *net = &p->network;
|
|
if (!p->Openflag) return 102;
|
|
if (index < 1 || index > net->Ncurves) return 206;
|
|
*type = net->Curve[index].Type;
|
|
return 0;
|
|
}
|
|
|
|
int DLLEXPORT EN_getcurvevalue(EN_Project p, int curveIndex, int pointIndex,
|
|
double *x, double *y)
|
|
/*----------------------------------------------------------------
|
|
** Input: curveIndex = data curve index
|
|
** pointIndex = index of a data point on the curve
|
|
** Output: x = x-value of the point on the curve
|
|
** y = y-value of the point on the curve
|
|
** Returns: error code
|
|
** Purpose: retrieves the value of a specific point on a data curve
|
|
**----------------------------------------------------------------
|
|
*/
|
|
{
|
|
*x = 0.0;
|
|
*y = 0.0;
|
|
if (!p->Openflag) return 102;
|
|
if (curveIndex < 1 || curveIndex > p->network.Ncurves) return 206;
|
|
if (pointIndex < 1 || pointIndex > p->network.Curve[curveIndex].Npts) return 251;
|
|
*x = (double)p->network.Curve[curveIndex].X[pointIndex - 1];
|
|
*y = (double)p->network.Curve[curveIndex].Y[pointIndex - 1];
|
|
return 0;
|
|
}
|
|
|
|
int DLLEXPORT EN_setcurvevalue(EN_Project p, int curveIndex, int pointIndex,
|
|
double x, double y)
|
|
/*----------------------------------------------------------------
|
|
** Input: curveIndex = data curve index
|
|
** pointIndex = index of a data point on the curve
|
|
** x = new x-value for the point on the curve
|
|
** y = new y-value for the point on the curve
|
|
** Output: none
|
|
** Returns: error code
|
|
** Purpose: sets the value of a specific point on a data curve
|
|
**----------------------------------------------------------------
|
|
*/
|
|
{
|
|
Network *net = &p->network;
|
|
Scurve *curve;
|
|
|
|
if (!p->Openflag) return 102;
|
|
if (curveIndex <= 0 || curveIndex > net->Ncurves) return 206;
|
|
curve = &net->Curve[curveIndex];
|
|
if (pointIndex <= 0 || pointIndex > curve->Npts) return 251;
|
|
curve->X[pointIndex - 1] = x;
|
|
curve->Y[pointIndex - 1] = y;
|
|
return 0;
|
|
}
|
|
|
|
int DLLEXPORT EN_getcurve(EN_Project p, int index, char *id, int *nPoints,
|
|
double **xValues, double **yValues)
|
|
/*----------------------------------------------------------------
|
|
** Input: index = data curve index
|
|
** Output: id = ID name of data curve
|
|
** nPoints = number of data points on the curve
|
|
** xValues = array of x-values for each data point
|
|
** yValues = array of y-values for each data point
|
|
** Returns: error code
|
|
** Purpose: retrieves the data associated with a data curve
|
|
**
|
|
** The calling program is responsible for making xValues and
|
|
** yValues large enough to hold nPoints data points.
|
|
**----------------------------------------------------------------
|
|
*/
|
|
{
|
|
int i;
|
|
Scurve *curve;
|
|
|
|
if (!p->Openflag) return 102;
|
|
if (index <= 0 || index > p->network.Ncurves) return 206;
|
|
curve = &p->network.Curve[index];
|
|
strncpy(id, curve->ID, MAXID);
|
|
*nPoints = curve->Npts;
|
|
for (i = 0; i < curve->Npts; i++)
|
|
{
|
|
*xValues[i] = curve->X[i];
|
|
*yValues[i] = curve->Y[i];
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int DLLEXPORT EN_setcurve(EN_Project p, int index, double *xValues,
|
|
double *yValues, int nPoints)
|
|
/*----------------------------------------------------------------
|
|
** Input: index = data curve index
|
|
** xValues = array of x-values
|
|
** yValues = array of y-values
|
|
** nPoints = number of data points in the x and y arrays
|
|
** Returns: error code
|
|
** Purpose: replaces a curve's set of data points
|
|
**----------------------------------------------------------------
|
|
*/
|
|
{
|
|
Network *net = &p->network;
|
|
Scurve *curve;
|
|
int j;
|
|
|
|
// Check for valid arguments
|
|
if (!p->Openflag) return 102;
|
|
if (index <= 0 || index > net->Ncurves) return 206;
|
|
if (nPoints <= 0) return 202;
|
|
|
|
// Check that x values are increasing
|
|
for (j = 1; j < nPoints; j++) if (xValues[j-1] >= xValues[j]) return 230;
|
|
|
|
// Re-set number of points & reallocate memory for values
|
|
curve = &net->Curve[index];
|
|
curve->Npts = nPoints;
|
|
curve->X = (double *)realloc(curve->X, nPoints * sizeof(double));
|
|
curve->Y = (double *)realloc(curve->Y, nPoints * sizeof(double));
|
|
if (curve->X == NULL) return 101;
|
|
if (curve->Y == NULL) return 101;
|
|
|
|
// Load values into curve
|
|
for (j = 0; j < nPoints; j++)
|
|
{
|
|
curve->X[j] = xValues[j];
|
|
curve->Y[j] = yValues[j];
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/********************************************************************
|
|
|
|
Simple Controls Functions
|
|
|
|
********************************************************************/
|
|
|
|
int DLLEXPORT EN_addcontrol(EN_Project p, int type, int linkIndex, double setting,
|
|
int nodeIndex, double level, int *index)
|
|
/*----------------------------------------------------------------
|
|
** Input: type = type of control (see EN_ControlType)
|
|
** linkIndex = index of link being controlled
|
|
** setting = link control setting (e.g., pump speed)
|
|
** nodeIndex = index of node controlling a link (for level controls)
|
|
** level = control activation level (pressure for junction nodes,
|
|
** water level for tank nodes or time value for time-based
|
|
** control)
|
|
** Output: index = the index of the new control
|
|
** Returns: error code
|
|
** Purpose: adds a new simple control to a project
|
|
**----------------------------------------------------------------
|
|
*/
|
|
{
|
|
Network *net = &p->network;
|
|
Parser *parser = &p->parser;
|
|
|
|
char status = ACTIVE;
|
|
int n;
|
|
long t = 0;
|
|
double s = setting, lvl = level;
|
|
double *Ucf = p->Ucf;
|
|
Scontrol *control;
|
|
|
|
|
|
// Check that project exists
|
|
if (!p->Openflag) return 102;
|
|
|
|
// Check that controlled link exists
|
|
if (linkIndex <= 0 || linkIndex > net->Nlinks) return 204;
|
|
|
|
// Cannot control check valve
|
|
if (net->Link[linkIndex].Type == CVPIPE) return 207;
|
|
|
|
// Check for valid parameters
|
|
if (type < 0 || type > EN_TIMEOFDAY) return 251;
|
|
if (type == EN_LOWLEVEL || type == EN_HILEVEL)
|
|
{
|
|
if (nodeIndex < 1 || nodeIndex > net->Nnodes) return 203;
|
|
}
|
|
else nodeIndex = 0;
|
|
if (s < 0.0 || lvl < 0.0) return 202;
|
|
|
|
// Adjust units of control parameters
|
|
switch (net->Link[linkIndex].Type)
|
|
{
|
|
case PRV:
|
|
case PSV:
|
|
case PBV:
|
|
s /= Ucf[PRESSURE];
|
|
break;
|
|
case FCV:
|
|
s /= Ucf[FLOW];
|
|
break;
|
|
case GPV:
|
|
if (s == 0.0) status = CLOSED;
|
|
else if (s == 1.0) status = OPEN;
|
|
else return 202;
|
|
s = net->Link[linkIndex].Kc;
|
|
break;
|
|
case PIPE:
|
|
case PUMP:
|
|
status = OPEN;
|
|
if (s == 0.0) status = CLOSED;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
if (type == LOWLEVEL || type == HILEVEL)
|
|
{
|
|
if (nodeIndex > net->Njuncs) lvl = net->Node[nodeIndex].El + level / Ucf[ELEV];
|
|
else lvl = net->Node[nodeIndex].El + level / Ucf[PRESSURE];
|
|
}
|
|
if (type == TIMER) t = (long)ROUND(lvl);
|
|
if (type == TIMEOFDAY) t = (long)ROUND(lvl) % SECperDAY;
|
|
|
|
// Expand project's array of controls
|
|
n = net->Ncontrols + 1;
|
|
net->Control = (Scontrol *)realloc(net->Control, (n + 1) * sizeof(Scontrol));
|
|
|
|
// Set properties of the new control
|
|
control = &net->Control[n];
|
|
control->Type = (char)type;
|
|
control->Link = linkIndex;
|
|
control->Node = nodeIndex;
|
|
control->Status = status;
|
|
control->Setting = s;
|
|
control->Grade = lvl;
|
|
control->Time = t;
|
|
|
|
// Update number of controls
|
|
net->Ncontrols = n;
|
|
parser->MaxControls = n;
|
|
|
|
// Replace the control's index
|
|
*index = n;
|
|
return 0;
|
|
}
|
|
|
|
int DLLEXPORT EN_deletecontrol(EN_Project p, int index)
|
|
/*----------------------------------------------------------------
|
|
** Input: index = index of the control
|
|
** Output: none
|
|
** Returns: error code
|
|
** Purpose: deletes a simple control from a project
|
|
**----------------------------------------------------------------
|
|
*/
|
|
{
|
|
Network *net = &p->network;
|
|
int i;
|
|
|
|
if (index <= 0 || index > net->Ncontrols) return 241;
|
|
for (i = index; i <= net->Ncontrols - 1; i++)
|
|
{
|
|
net->Control[i] = net->Control[i + 1];
|
|
}
|
|
net->Ncontrols--;
|
|
return 0;
|
|
}
|
|
|
|
int DLLEXPORT EN_getcontrol(EN_Project p, int index, int *type, int *linkIndex,
|
|
double *setting, int *nodeIndex, double *level)
|
|
/*----------------------------------------------------------------
|
|
** Input: index = index of the control
|
|
** Output: type = type of control (see EN_ControlType)
|
|
** linkIndex = index of link being controlled
|
|
** setting = link control setting (e.g., pump speed)
|
|
** nodeIndex = index of node that triggers a level control
|
|
** level = trigger level that activates the control (pressure for junction nodes,
|
|
** water level for tank nodes or time value for time-based control)
|
|
** Returns: error code
|
|
** Purpose: Retrieves the properties of a simple control
|
|
**----------------------------------------------------------------
|
|
*/
|
|
{
|
|
Network *net = &p->network;
|
|
|
|
double s, lvl;
|
|
double *Ucf = p->Ucf;
|
|
Scontrol *control;
|
|
Snode *node;
|
|
|
|
// Set default return values
|
|
s = 0.0;
|
|
lvl = 0.0;
|
|
*type = 0;
|
|
*linkIndex = 0;
|
|
*nodeIndex = 0;
|
|
|
|
// Check for valid arguments
|
|
if (!p->Openflag) return 102;
|
|
if (index <= 0 || index > net->Ncontrols) return 241;
|
|
|
|
// Retrieve control's type and link index
|
|
control = &net->Control[index];
|
|
*type = control->Type;
|
|
*linkIndex = control->Link;
|
|
|
|
// Retrieve control's setting
|
|
s = control->Setting;
|
|
if (control->Setting != MISSING)
|
|
{
|
|
switch (net->Link[*linkIndex].Type)
|
|
{
|
|
case PRV:
|
|
case PSV:
|
|
case PBV:
|
|
s *= Ucf[PRESSURE];
|
|
break;
|
|
case FCV:
|
|
s *= Ucf[FLOW];
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
else if (control->Status == OPEN) s = 1.0;
|
|
else s = 0.0;
|
|
|
|
// Retrieve level value for a node level control
|
|
*nodeIndex = control->Node;
|
|
if (*nodeIndex > 0)
|
|
{
|
|
node = &net->Node[*nodeIndex];
|
|
if (*nodeIndex > net->Njuncs) // Node is a tank
|
|
{
|
|
lvl = (control->Grade - node->El) * Ucf[ELEV];
|
|
}
|
|
else // Node is a junction
|
|
{
|
|
lvl = (control->Grade - node->El) * Ucf[PRESSURE];
|
|
}
|
|
}
|
|
|
|
// Retrieve level value for a time-based control
|
|
else
|
|
{
|
|
lvl = (double)control->Time;
|
|
}
|
|
*setting = (double)s;
|
|
*level = (double)lvl;
|
|
return 0;
|
|
}
|
|
|
|
int DLLEXPORT EN_setcontrol(EN_Project p, int index, int type, int linkIndex,
|
|
double setting, int nodeIndex, double level)
|
|
/*----------------------------------------------------------------
|
|
** Input: index = index of the control
|
|
** type = type of control (see EN_ControlType)
|
|
** linkIndex = index of link being controlled
|
|
** setting = link control setting (e.g., pump speed)
|
|
** nodeIndex = index of node that triggers the control (for level controls)
|
|
** level = trigger level that activates the control (pressure for junction nodes,
|
|
** water level for tank nodes or time value for time-based control)
|
|
** Output: none
|
|
** Returns: error code
|
|
** Purpose: Sets the properties of a simple control
|
|
**----------------------------------------------------------------
|
|
*/
|
|
{
|
|
Network *net = &p->network;
|
|
|
|
char status = ACTIVE;
|
|
long t = 0;
|
|
double s = setting, lvl = level;
|
|
double *Ucf = p->Ucf;
|
|
Slink *link;
|
|
Scontrol *control;
|
|
|
|
// Check that project exists
|
|
if (!p->Openflag) return 102;
|
|
|
|
// Check that control exists
|
|
if (index <= 0 || index > net->Ncontrols) return 241;
|
|
control = &net->Control[index];
|
|
|
|
// Check that controlled link exists (0 index de-activates the control)
|
|
if (linkIndex == 0)
|
|
{
|
|
control->Link = 0;
|
|
return 0;
|
|
}
|
|
if (linkIndex < 0 || linkIndex > net->Nlinks) return 204;
|
|
|
|
// Cannot control check valve
|
|
if (net->Link[linkIndex].Type == CVPIPE) return 207;
|
|
|
|
// Check for valid control properties
|
|
if (type < 0 || type > EN_TIMEOFDAY) return 251;
|
|
if (type == EN_LOWLEVEL || type == EN_HILEVEL)
|
|
{
|
|
if (nodeIndex < 1 || nodeIndex > net->Nnodes) return 203;
|
|
}
|
|
else nodeIndex = 0;
|
|
if (s < 0.0 || lvl < 0.0) return 202;
|
|
|
|
// Adjust units of control's properties
|
|
link = &net->Link[linkIndex];
|
|
switch (link->Type)
|
|
{
|
|
case PRV:
|
|
case PSV:
|
|
case PBV:
|
|
s /= Ucf[PRESSURE];
|
|
break;
|
|
case FCV:
|
|
s /= Ucf[FLOW];
|
|
break;
|
|
case GPV:
|
|
if (s == 0.0) status = CLOSED;
|
|
else if (s == 1.0) status = OPEN;
|
|
else return 202;
|
|
s = link->Kc;
|
|
break;
|
|
case PIPE:
|
|
case PUMP:
|
|
status = OPEN;
|
|
if (s == 0.0) status = CLOSED;
|
|
default:
|
|
break;
|
|
}
|
|
if (type == LOWLEVEL || type == HILEVEL)
|
|
{
|
|
if (nodeIndex > net->Njuncs) lvl = net->Node[nodeIndex].El + level / Ucf[ELEV];
|
|
else lvl = net->Node[nodeIndex].El + level / Ucf[PRESSURE];
|
|
}
|
|
if (type == TIMER) t = (long)ROUND(lvl);
|
|
if (type == TIMEOFDAY) t = (long)ROUND(lvl) % SECperDAY;
|
|
|
|
/* Reset control's parameters */
|
|
control->Type = (char)type;
|
|
control->Link = linkIndex;
|
|
control->Node = nodeIndex;
|
|
control->Status = status;
|
|
control->Setting = s;
|
|
control->Grade = lvl;
|
|
control->Time = t;
|
|
return 0;
|
|
}
|
|
|
|
/********************************************************************
|
|
|
|
Rule-Based Controls Functions
|
|
|
|
********************************************************************/
|
|
|
|
int DLLEXPORT EN_addrule(EN_Project p, char *rule)
|
|
/*----------------------------------------------------------------
|
|
** Input: rule = text of rule being added in the format
|
|
** used for the [RULES] section of an EPANET input file
|
|
** Output: none
|
|
** Returns: error code
|
|
** Purpose: adds a new rule to a project
|
|
**----------------------------------------------------------------
|
|
*/
|
|
{
|
|
Network *net = &p->network;
|
|
Parser *parser = &p->parser;
|
|
Rules *rules = &p->rules;
|
|
|
|
char *line;
|
|
char *nextline;
|
|
char line2[MAXLINE+1];
|
|
|
|
// Resize rules array
|
|
net->Rule = (Srule *)realloc(net->Rule, (net->Nrules + 2)*sizeof(Srule));
|
|
rules->Errcode = 0;
|
|
rules->RuleState = 6; // = r_PRIORITY
|
|
|
|
// Extract each line of the rule statement
|
|
line = rule;
|
|
while (line)
|
|
{
|
|
// Find where current line ends and next one begins
|
|
nextline = strchr(line, '\n');
|
|
if (nextline) *nextline = '\0';
|
|
|
|
// Copy and tokenize the current line
|
|
strcpy(line2, line);
|
|
strcat(line2, "\n"); // Tokenizer won't work without this
|
|
parser->Ntokens = gettokens(line2, parser->Tok, MAXTOKS, parser->Comment);
|
|
|
|
// Process the line to build up the rule's contents
|
|
if (parser->Ntokens > 0 && *parser->Tok[0] != ';')
|
|
{
|
|
ruledata(p); // Nrules gets updated in ruledata()
|
|
if (rules->Errcode) break;
|
|
}
|
|
|
|
// Extract next line from the rule statement
|
|
if (nextline) *nextline = '\n';
|
|
line = nextline ? (nextline + 1) : NULL;
|
|
}
|
|
|
|
// Delete new rule entry if there was an error
|
|
if (rules->Errcode) deleterule(p, net->Nrules);
|
|
|
|
// Re-assign error code 201 (syntax error) to 250 (invalid format)
|
|
if (rules->Errcode == 201) rules->Errcode = 250;
|
|
return rules->Errcode;
|
|
}
|
|
|
|
int DLLEXPORT EN_deleterule(EN_Project p, int index)
|
|
/*----------------------------------------------------------------
|
|
** Input: index = rule index
|
|
** Output: none
|
|
** Returns: error code
|
|
** Purpose: deletes a rule from a project
|
|
**----------------------------------------------------------------
|
|
*/
|
|
{
|
|
if (index < 1 || index > p->network.Nrules) return 257;
|
|
deleterule(p, index);
|
|
return 0;
|
|
}
|
|
|
|
int DLLEXPORT EN_getrule(EN_Project p, int index, int *nPremises,
|
|
int *nThenActions, int *nElseActions,
|
|
double *priority)
|
|
/*----------------------------------------------------------------
|
|
** Input: index = rule index
|
|
** Output: nPremises = number of premises conditions (IF AND OR)
|
|
** nThenActions = number of actions in THEN portion of rule
|
|
** nElseActions = number of actions in ELSE portion of rule
|
|
** priority = rule priority
|
|
** Returns: error code
|
|
** Purpose: gets information about a particular rule
|
|
**----------------------------------------------------------------
|
|
*/
|
|
{
|
|
Network *net = &p->network;
|
|
|
|
int count;
|
|
Spremise *premise;
|
|
Saction *action;
|
|
|
|
if (index < 1 || index > net->Nrules) return 257;
|
|
*priority = (double)p->network.Rule[index].priority;
|
|
|
|
count = 1;
|
|
premise = net->Rule[index].Premises;
|
|
while (premise->next != NULL)
|
|
{
|
|
count++;
|
|
premise = premise->next;
|
|
}
|
|
*nPremises = count;
|
|
|
|
count = 1;
|
|
action = net->Rule[index].ThenActions;
|
|
while (action->next != NULL)
|
|
{
|
|
count++;
|
|
action = action->next;
|
|
}
|
|
*nThenActions = count;
|
|
|
|
action = net->Rule[index].ElseActions;
|
|
count = 0;
|
|
if (action != NULL)
|
|
{
|
|
count = 1;
|
|
while (action->next != NULL)
|
|
{
|
|
count++;
|
|
action = action->next;
|
|
}
|
|
}
|
|
*nElseActions = count;
|
|
return 0;
|
|
}
|
|
|
|
int DLLEXPORT EN_getruleID(EN_Project p, int index, char *id)
|
|
/*----------------------------------------------------------------
|
|
** Input: index = rule index
|
|
** Output: id = rule ID label
|
|
** Returns: error code
|
|
** Purpose: retrieves the ID label of a rule
|
|
**----------------------------------------------------------------
|
|
*/
|
|
{
|
|
strcpy(id, "");
|
|
if (!p->Openflag) return 102;
|
|
if (index < 1 || index > p->network.Nrules) return 257;
|
|
strcpy(id, p->network.Rule[index].label);
|
|
return 0;
|
|
}
|
|
|
|
int DLLEXPORT EN_getpremise(EN_Project p, int ruleIndex, int premiseIndex,
|
|
int *logop, int *object, int *objIndex, int *variable,
|
|
int *relop, int *status, double *value)
|
|
/*----------------------------------------------------------------
|
|
** Input: ruleIndex = rule index
|
|
** premiseIndex = premise index
|
|
** Output: logop = logical operator (IF = 1, AND = 2, OR = 3)
|
|
** object = type of object appearing in the premise (see EN_RuleObject)
|
|
** objIndex = object's index in Node or Link array
|
|
** variable = object variable being tested (see EN_RuleVariable)
|
|
** relop = relational operator (see EN_RuleOperator)
|
|
** status = object status being tested against (see EN_RuleStatus))
|
|
** value = variable value being tested against
|
|
** Returns: error code
|
|
** Purpose: retrieves the properties of a rule's premise
|
|
**----------------------------------------------------------------
|
|
*/
|
|
{
|
|
Spremise *premises;
|
|
Spremise *premise;
|
|
|
|
if (ruleIndex < 1 || ruleIndex > p->network.Nrules) return 257;
|
|
|
|
premises = p->network.Rule[ruleIndex].Premises;
|
|
premise = getpremise(premises, premiseIndex);
|
|
if (premise == NULL) return 258;
|
|
|
|
*logop = premise->logop;
|
|
*object = premise->object;
|
|
*objIndex = premise->index;
|
|
*variable = premise->variable;
|
|
*relop = premise->relop;
|
|
*status = premise->status;
|
|
*value = (double)premise->value;
|
|
return 0;
|
|
}
|
|
|
|
|
|
int DLLEXPORT EN_setpremise(EN_Project p, int ruleIndex, int premiseIndex,
|
|
int logop, int object, int objIndex, int variable,
|
|
int relop, int status, double value)
|
|
/*----------------------------------------------------------------
|
|
** Input: ruleIndex = rule index
|
|
** premiseIndex = premise index
|
|
** logop = logical operator (IF = 1, AND = 2, OR = 3)
|
|
** object = type of object appearing in the premise (see EN_RuleObject)
|
|
** objIndex = object's index in Node or Link array
|
|
** variable = object variable being tested (see EN_RuleVariable)
|
|
** relop = relational operator (see EN_RuleOperator)
|
|
** status = object status being tested against (see EN_RuleStatus))
|
|
** value = variable value being tested against
|
|
** Output: none
|
|
** Returns: error code
|
|
** Purpose: sets the properties of a rule's premise
|
|
**----------------------------------------------------------------
|
|
*/
|
|
{
|
|
Spremise *premises;
|
|
Spremise *premise;
|
|
|
|
if (ruleIndex < 1 || ruleIndex > p->network.Nrules) return 257;
|
|
|
|
premises = p->network.Rule[ruleIndex].Premises;
|
|
premise = getpremise(premises, premiseIndex);
|
|
if (premise == NULL) return 258;
|
|
|
|
premise->logop = logop;
|
|
premise->object = object;
|
|
premise->index = objIndex;
|
|
premise->variable = variable;
|
|
premise->relop = relop;
|
|
premise->status = status;
|
|
premise->value = value;
|
|
return 0;
|
|
}
|
|
|
|
int DLLEXPORT EN_setpremiseindex(EN_Project p, int ruleIndex, int premiseIndex,
|
|
int objIndex)
|
|
/*----------------------------------------------------------------
|
|
** Input: ruleIndex = rule index
|
|
** premiseIndex = premise index
|
|
** objIndex = object's index in Node or Link array
|
|
** Output: none
|
|
** Returns: error code
|
|
** Purpose: sets the index of an object referred to in a rule's premise
|
|
**----------------------------------------------------------------
|
|
*/
|
|
{
|
|
Spremise *premises;
|
|
Spremise *premise;
|
|
|
|
if (ruleIndex < 1 || ruleIndex > p->network.Nrules) return 257;
|
|
|
|
premises = p->network.Rule[ruleIndex].Premises;
|
|
premise = getpremise(premises, premiseIndex);
|
|
if (premise == NULL) return 258;
|
|
|
|
premise->index = objIndex;
|
|
return 0;
|
|
}
|
|
|
|
int DLLEXPORT EN_setpremisestatus(EN_Project p, int ruleIndex, int premiseIndex, int status)
|
|
/*----------------------------------------------------------------
|
|
** Input: ruleIndex = rule index
|
|
** premiseIndex = premise index
|
|
** status = object status being tested against
|
|
** (see EN_RuleStatus))
|
|
** Output: none
|
|
** Returns: error code
|
|
** Purpose: sets the status of an object being tested against
|
|
** in a rule's premise
|
|
**----------------------------------------------------------------
|
|
*/
|
|
{
|
|
Spremise *premises;
|
|
Spremise *premise;
|
|
|
|
if (ruleIndex < 1 || ruleIndex > p->network.Nrules) return 257;
|
|
|
|
premises = p->network.Rule[ruleIndex].Premises;
|
|
premise = getpremise(premises, premiseIndex);
|
|
if (premise == NULL) return 258;
|
|
|
|
premise->status = status;
|
|
return 0;
|
|
}
|
|
|
|
int DLLEXPORT EN_setpremisevalue(EN_Project p, int ruleIndex, int premiseIndex, double value)
|
|
/*----------------------------------------------------------------
|
|
** Input: ruleIndex = rule index
|
|
** premiseIndex = premise index
|
|
** value = value of object variable being tested against
|
|
** Output: none
|
|
** Returns: error code
|
|
** Purpose: sets the value of object's variable being tested against
|
|
** in a rule's premise
|
|
**----------------------------------------------------------------
|
|
*/
|
|
{
|
|
Spremise *premises;
|
|
Spremise *premise;
|
|
|
|
if (ruleIndex < 1 || ruleIndex > p->network.Nrules) return 257;
|
|
|
|
premises = p->network.Rule[ruleIndex].Premises;
|
|
premise = getpremise(premises, premiseIndex);
|
|
if (premise == NULL) return 258;
|
|
|
|
premise->value = value;
|
|
return 0;
|
|
}
|
|
|
|
int DLLEXPORT EN_getthenaction(EN_Project p, int ruleIndex, int actionIndex,
|
|
int *linkIndex, int *status, double *setting)
|
|
/*----------------------------------------------------------------
|
|
** Input: ruleIndex = rule index
|
|
** actionIndex = index of a rule's THEN actions
|
|
** Output: linkIndex = index of link appearing in the action
|
|
** status = status assigned to the link (see EN_RuleStatus))
|
|
** setting = setting assigned to the link
|
|
** Returns: error code
|
|
** Purpose: retrieves the properties of a rule's THEN action
|
|
**----------------------------------------------------------------
|
|
*/
|
|
{
|
|
Saction *actions;
|
|
Saction *action;
|
|
|
|
if (ruleIndex < 1 || ruleIndex > p->network.Nrules) return 257;
|
|
|
|
actions = p->network.Rule[ruleIndex].ThenActions;
|
|
action = getaction(actions, actionIndex);
|
|
if (action == NULL) return 258;
|
|
|
|
*linkIndex = action->link;
|
|
*status = action->status;
|
|
*setting = (double)action->setting;
|
|
return 0;
|
|
}
|
|
|
|
int DLLEXPORT EN_setthenaction(EN_Project p, int ruleIndex, int actionIndex,
|
|
int linkIndex, int status, double setting)
|
|
/*----------------------------------------------------------------
|
|
** Input: ruleIndex = rule index
|
|
** actionIndex = index of a rule's THEN actions
|
|
** linkIndex = index of link appearing in the action
|
|
** status = status assigned to the link (see EN_RuleStatus))
|
|
** setting = setting assigned to the link
|
|
** Returns: error code
|
|
** Purpose: sets the properties of a rule's THEN action
|
|
**----------------------------------------------------------------
|
|
*/
|
|
{
|
|
Saction *actions;
|
|
Saction *action;
|
|
|
|
if (ruleIndex < 1 || ruleIndex > p->network.Nrules) return 257;
|
|
|
|
actions = p->network.Rule[ruleIndex].ThenActions;
|
|
action = getaction(actions, actionIndex);
|
|
if (action == NULL) return 258;
|
|
|
|
action->link = linkIndex;
|
|
action->status = status;
|
|
action->setting = setting;
|
|
return 0;
|
|
}
|
|
|
|
int DLLEXPORT EN_getelseaction(EN_Project p, int ruleIndex, int actionIndex,
|
|
int *linkIndex, int *status, double *setting)
|
|
/*----------------------------------------------------------------
|
|
** Input: ruleIndex = rule index
|
|
** actionIndex = index of a rule's ELSE actions
|
|
** Output: linkIndex = index of link appearing in the action
|
|
** status = status assigned to the link (see EN_RuleStatus))
|
|
** setting = setting assigned to the link
|
|
** Returns: error code
|
|
** Purpose: retrieves the properties of a rule's ELSE action
|
|
**----------------------------------------------------------------
|
|
*/
|
|
{
|
|
|
|
Saction *actions;
|
|
Saction *action;
|
|
|
|
if (ruleIndex < 1 || ruleIndex > p->network.Nrules) return 257;
|
|
|
|
actions = p->network.Rule[ruleIndex].ThenActions;
|
|
action = getaction(actions, actionIndex);
|
|
if (action == NULL) return 258;
|
|
|
|
*linkIndex = action->link;
|
|
*status = action->status;
|
|
*setting = (double)action->setting;
|
|
return 0;
|
|
}
|
|
|
|
int DLLEXPORT EN_setelseaction(EN_Project p, int ruleIndex, int actionIndex,
|
|
int linkIndex, int status, double setting)
|
|
/*----------------------------------------------------------------
|
|
** Input: ruleIndex = rule index
|
|
** actionIndex = index of a rule's ELSE actions
|
|
** linkIndex = index of link appearing in the action
|
|
** status = status assigned to the link (see EN_RuleStatus))
|
|
** setting = setting assigned to the link
|
|
** Returns: error code
|
|
** Purpose: sets the properties of a rule's ELSE action
|
|
**----------------------------------------------------------------
|
|
*/
|
|
{
|
|
Saction *actions;
|
|
Saction *action;
|
|
|
|
if (ruleIndex < 1 || ruleIndex > p->network.Nrules) return 257;
|
|
|
|
actions = p->network.Rule[ruleIndex].ThenActions;
|
|
action = getaction(actions, actionIndex);
|
|
if (action == NULL) return 258;
|
|
|
|
action->link = linkIndex;
|
|
action->status = status;
|
|
action->setting = setting;
|
|
return 0;
|
|
}
|
|
|
|
int DLLEXPORT EN_setrulepriority(EN_Project p, int index, double priority)
|
|
/*-----------------------------------------------------------------------------
|
|
** Input: index = rule index
|
|
** priority = rule priority level
|
|
** Output: none
|
|
** Returns: error code
|
|
** Purpose: sets the priority level for a rule
|
|
**-----------------------------------------------------------------------------
|
|
*/
|
|
{
|
|
if (index <= 0 || index > p->network.Nrules) return 257;
|
|
p->network.Rule[index].priority = priority;
|
|
return 0;
|
|
}
|