diff --git a/CMakeLists.txt b/CMakeLists.txt index 59e1356..466e964 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -40,6 +40,7 @@ cmake_minimum_required (VERSION 2.8.8) project(EPANET) +add_subdirectory(run) add_subdirectory(tools/epanet-output) IF (BUILD_TESTS) @@ -51,6 +52,7 @@ set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin) set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib) set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib) + # Sets the position independent code property for all targets SET(CMAKE_POSITION_INDEPENDENT_CODE ON) @@ -66,40 +68,25 @@ IF (MSVC) ENDIF (MSVC) -#include_directories(include src) - - # configure file groups -file(GLOB EPANET_SOURCES src/*.c) -#set(EPANET_API_HEADER include/epanet2.h) -set(EPANET_CLI_SOURCES run/main.c) - -file(GLOB EPANET_LIB_ALL src/*) +file(GLOB EPANET_SOURCES src/*.c src/util/*.c) +file(GLOB EPANET_LIB_ALL src/* src/util/*) source_group("Library" FILES ${EPANET_LIB_ALL}) -source_group("CLI" REGULAR_EXPRESSION "run/.*") # the shared library add_library(epanet SHARED ${EPANET_SOURCES}) #${EPANET_API_HEADER}) target_include_directories(epanet PUBLIC ${PROJECT_SOURCE_DIR}/include) + # create export lib so we can link against dll using Visual Studio -#include(GenerateExportHeader) -#GENERATE_EXPORT_HEADER(epanet -# BASE_NAME epanet -# EXPORT_MACRO_NAME DLLEXPORT -# EXPORT_FILE_NAME epanet_export.h -# STATIC_DEFINE SHARED_EXPORTS_BUILT_AS_STATIC) -# -#file(COPY ${CMAKE_CURRENT_BINARY_DIR}/epanet_export.h -# DESTINATION ${CMAKE_CURRENT_SOURCE_DIR}/include) +add_definitions(-D WITH_GENX) +include(GenerateExportHeader) +GENERATE_EXPORT_HEADER(epanet + BASE_NAME epanet + EXPORT_MACRO_NAME DLLEXPORT + EXPORT_FILE_NAME epanet_export.h + STATIC_DEFINE SHARED_EXPORTS_BUILT_AS_STATIC) - -# the standalone executable -add_executable(runepanet ${EPANET_CLI_SOURCES}) - -if(NOT MSVC) - target_link_libraries(runepanet LINK_PUBLIC epanet m) -else(NOT MSVC) - target_link_libraries(runepanet LINK_PUBLIC epanet) -endif(NOT MSVC) +file(COPY ${CMAKE_CURRENT_BINARY_DIR}/epanet_export.h + DESTINATION ${CMAKE_CURRENT_SOURCE_DIR}/include) diff --git a/README.md b/README.md index dd6f5d7..0c1c01b 100755 --- a/README.md +++ b/README.md @@ -13,8 +13,9 @@ The EPANET Library is a pressurized pipe network hydraulic and water quality ana Please see the [`version 2.1` Release Notes](https://github.com/OpenWaterAnalytics/EPANET/blob/master/ReleaseNotes2_1.md) for information relevant to users of the previous official version (2.00.12). If you would like to contribute by addressing any of the outstanding [Issues](https://github.com/OpenWaterAnalytics/EPANET/issues), then please comment on the Issue, then Fork this repo to your own account and base your commits on the [`dev` branch](https://github.com/OpenWaterAnalytics/EPANET/tree/dev). Once you are finished, you can open a Pull Request to test the code and discuss merging your changes back into the community respository. -A step-by-step tutorial on how to contribute to EPANET using GitHub is also [available] (http://www.slideshare.net/demetriseliades/contributing-to-epanet-using-github-in-windows). +A step-by-step tutorial on how to contribute to EPANET using GitHub is also [available](http://www.slideshare.net/demetriseliades/contributing-to-epanet-using-github-in-windows). __Note:__ This repository is not affiliated with, or endorsed by, the USEPA. For the last "official" release of EPANET (2.00.12 UI and Toolkit) please go to the [EPA's GitHub repo](https://github.com/USEPA/Water-Distribution-Network-Model) or [the USEPA website](http://www2.epa.gov/water-research/epanet). It is also not the graphical user interface version. This is the hydraulic and water quality solver engine. However, if you are interested in extending EPANET for academic, personal, or commercial use, then you've come to the right place. For community discussion, FAQ, and roadmapping of the project, go to the [Community Forum](http://community.wateranalytics.org/category/epanet). + diff --git a/ReleaseNotes2_2.md b/ReleaseNotes2_2.md new file mode 100644 index 0000000..d09fbf8 --- /dev/null +++ b/ReleaseNotes2_2.md @@ -0,0 +1,96 @@ + + + +Release Notes for EPANET 2.2 (Draft) +============================ + +This document describes the changes and updates that have been made to version 2.2 of EPANET. + +## Thread-Safe API Functions + +A duplicate set of the version 2.1 API functions has been provided that allow multiple EPANET projects to be analyzed concurrently in a thread-safe manner. These functions maintain the same name as the original but use a `EN_` prefix instead of `EN`. In addition, the first argument to each of these functions is a pointer to an `EN_Project` structure that encapsulates the network data for the particular project being analyzed. For example, instead of writing: + +`ENgetnodevalue(nodeIndex, EN_ELEVATION, &elev)` + +one would use: + +`EN_getnodevalue(pr, nodeIndex, EN_ELEVATION, &elev)` + +where `pr` is the pointer to an `EN_Project`. + +Two new functions have been added to the API to manage the creation and deletion of project pointers. `EN_createproject` creates a new project along with a pointer to it, while `EN_deleteproject` deletes a project. An example of using the thread-safe version of the API is shown below: +``` +#include "epanet2.h" +int runEpanet(char *finp, char *frpt) +{ + EN_Project *pr = NULL; + int err; + err = EN_createproject(&pr); + if (err) return err; + err = EN_open(pr, finp, frpt, ""); + if (!err) err = EN_solveH(pr); + if (!err) err = EN_report(pr); + EN_close(pr); + EN_deleteproject(pr); + return err; +} +``` + +## Additional Convergence Parameters + +Two new analysis options have been added to provide more rigorous convergence criteria for EPANET's hydraulic solver. In the API they are named `EN_HEADERROR` and `EN_FLOWCHANGE` while in the `[OPTIONS]` section of an EPANET input file they are named `HEADERROR` and `FLOWCHANGE`, respectively. + +`EN_HEADERROR` is the maximum head loss error that any network link can have for hydraulic convergence to occur. A link's head loss error is the difference between the head loss found as a function of computed flow in the link (such as by the Hazen-Williams equation for a pipe) and the difference in computed heads for the link's end nodes. The units of this parameter are feet (or meters for SI units). The default value of 0 indicates that no head error limit applies. + +`EN_FLOWCHANGE` is the largest change in flow that any network element (link, emitter, or pressure-dependent demand) can have for hydraulic convergence to occur. It is specified in whatever flow units the project is using. The default value of 0 indicates that no flow change limit applies. + +These new parameters augment the current `EN_ACCURACY` option which always remains in effect. In addition, both `EN_HEADERROR` and `EN_FLOWCHANGE` can be used as parameters in the `ENgetstatistic` (or `EN_getstatistic`) function to retrieve their computed values (even when their option values are 0) after a hydraulic solution has been completed. + +## Improved Linear Solver Routine +EPANET's hydraulic solver requires solving a system of linear equations over a series of iterations until a set of convergence criteria are met. The coefficient matrix of this linear system is square and symmetric. It has a row for each network node and a non-zero off-diagonal coefficient for each link. The numerical effort needed to solve the linear system can be reduced if the nodes are re-ordered so that the non-zero coefficients cluster more tightly around the diagonal. + +EPANET's original node re-ordering scheme has been replaced by the more powerful **Multiple Minimum Degree (MMD)** algorithm. On a series of eight networks ranging in size from 7,700 to 100,000 nodes **MMD** reduced the solution time for a single period (steady state) hydraulic analysis by an average of more than 50%. + +## Pressure Dependent Demands +EPANET has always employed a Demand Driven Analysis (**DDA**) when modeling network hydraulics. Under this approach nodal demands at a given point in time are fixed values that must be delivered no matter what nodal heads and link flows are produced by a hydraulic solution. This can result in situations where required demands are satisfied at nodes that have negative pressures - a physical impossibility. + +To address this issue EPANET has been extended to use a Pressure Driven Analysis (**PDA**) if so desired. Under **PDA**, the demand *D* delivered at a node depends on the node's available pressure *P* according to: +$$D =D_f\left(\frac{P-P_{min}}{P_{req}-P_{min}}\right)^{P_{exp}} for P_{0}<=P<=P_{req}$$where *Df* is the full demand required, *Pmin* is the pressure below which demand is zero, *Preq* is the pressure required to deliver the full required demand and *Pexp* is an exponent. When *P < Pmin* demand is 0 and when *P > Preq* demand equals *Df*. + +To implement pressure dependent analysis four new parameters have been added to the [OPTIONS] section of the EPANET input file: + + +| Parameter | Description | Default | +|--|--|--| +| DEMAND MODEL | either DDA or PDA | DDA | +| MINIMUM PRESSURE | value for *Pmin* | 0 +| REQUIRED PRESSURE | value for *Preq* | 0 +| PRESSURE EXPONENT | value for *Pexp* | 0.5 | + +These parameters can also be set and retrieved in code using the following API functions +``` +int ENsetdemandmodel(int modelType, double pMin, double pReq, double pExp); +int ENgetdemandmodel(int *modelType, double *pMin, double *pReq, double *pExp); +``` +for the legacy API and +``` +int EN_setdemandmodel(EN_Project *pr, int modelType, double pMin, double pReq, double pExp); +int EN_getdemandmodel(EN_Project *pr, int *modelType, double *pMin, double *pReq, double *pExp); +``` +for the thread-safe API. Some additional points regarding the new **PDA** option are: + + - If no DEMAND MODEL and its parameters are specified then the analysis defaults to being demand driven (**DDA**). + - This implementation of **PDA** assumes that the same parameters apply to all nodes in the network. Extending the framework to allow different parameters for specific nodes is straightforward to do but is left as a future feature to implement. + - *P0* is allowed to equal to *Preq*. This condition can be used to find a solution that results in the smallest amount of demand reductions needed to insure that no node delivers positive demand at a pressure below *Pmin*. + +## Code Changes + + - The header file `vars.h` containing global variables has been eliminated. Instead a number of new structures incorporating these variables has been added to `types.h`. These structures have been incorporated into the new `EN_Project` structure, also defined in `types.h`, which gets passed into each of the thread-safe API functions as a pointer. + - Each of the legacy API functions now simply calls its thread-safe counterpart passing in a pointer to a default global`EN_Project` variable that is declared in `types.h`. + - Throughout all code modules, global variables that were previously accessed through `vars.h` are now accessed using the `EN_Project` pointer that is passed into the functions where the variables appear. + - The exceedingly long `hydraul.c` file has been split into four separate files: + - `hydraul.c` now contains just the code needed to initialize a hydraulic analysis, set demands and control actions at each time step, and determine the length of the next time step to take. + - `hydsolver.c` implements EPANET's hydraulic solver at a single point in time. + - `hydcoeffs.c` computes values of the matrix coefficients (derived from link head losses and their gradients) used by the hydraulic solver. + - `hydstatus.c` checks for status changes in valves and pumps as requested by the hydraulic solver. + - The Multiple Minimum Degree re-ordering algorithm appears in a new file named `genmmd.c`. This is 1990's legacy code that is readily available on the web and can be found in several linear equation solver libraries. diff --git a/include/epanet2.bas b/include/epanet2.bas index d782c6d..0ded96c 100644 --- a/include/epanet2.bas +++ b/include/epanet2.bas @@ -78,6 +78,8 @@ Public Const EN_NEXTEVENT = 14 Public Const EN_ITERATIONS = 0 Public Const EN_RELATIVEERROR = 1 +Public Const EN_MAXHEADERROR = 2 +Public Const EN_MAXFLOWCHANGE = 3 Public Const EN_NODECOUNT = 0 'Component counts Public Const EN_TANKCOUNT = 1 @@ -126,6 +128,9 @@ Public Const EN_MLD = 7 Public Const EN_CMH = 8 Public Const EN_CMD = 9 +Public Const EN_DDA = 0 ' Demand driven analysis +Public Const EN_PDA = 1 ' Pressure driven analysis + Public Const EN_TRIALS = 0 ' Misc. options Public Const EN_ACCURACY = 1 Public Const EN_TOLERANCE = 2 @@ -233,6 +238,9 @@ Public Const EN_G_CURVE = 4 ' General\default curve Declare Function ENgetversion Lib "epanet2.dll" (value As Long) As Long + Declare Function ENgetdemandmodel Lib "epanet2.dll" (type as long, pmin as Single, preq as Single, pexp as Single) As Long + Declare Function ENsetdemandmodel Lib "epanet2.dll" (ByVal type as long, ByVal pmin as Single, ByVal preq as Single, ByVal pexp as Single) As Long + Declare Function ENsetflowunits Lib "epanet2.dll" (ByVal code As Long) As Long Declare Function ENsetcontrol Lib "epanet2.dll" (ByVal Cindex As Long, ByVal Ctype As Long, ByVal Lindex As Long, ByVal setting As Single, ByVal Nindex As Long, ByVal Level As Single) As Long Declare Function ENsetnodevalue Lib "epanet2.dll" (ByVal index As Long, ByVal code As Long, ByVal value As Single) As Long diff --git a/include/epanet2.h b/include/epanet2.h index e070f37..e6d91c6 100644 --- a/include/epanet2.h +++ b/include/epanet2.h @@ -30,37 +30,40 @@ #define EN_API_FLOAT_TYPE float #endif -// --- define WINDOWS -#undef WINDOWS -#ifdef _WIN32 - #define WINDOWS -#endif -#ifdef __WIN32__ - #define WINDOWS -#endif +#ifdef WITH_GENX + #include "epanet_export.h" +#else + // --- define WINDOWS + #undef WINDOWS + #ifdef _WIN32 + #define WINDOWS + #endif + #ifdef __WIN32__ + #define WINDOWS + #endif -// --- define DLLEXPORT -#ifndef DLLEXPORT - #ifdef WINDOWS - #ifdef __cplusplus - #define DLLEXPORT extern "C" __declspec(dllexport) - #else - #define DLLEXPORT __declspec(dllexport) __stdcall - #endif // __cplusplus - #elif defined(CYGWIN) - #define DLLEXPORT __stdcall - #elif defined(__APPLE__) - #ifdef __cplusplus - #define DLLEXPORT + // --- define DLLEXPORT + #ifndef DLLEXPORT + #ifdef WINDOWS + #ifdef __cplusplus + #define DLLEXPORT __declspec(dllexport) + #else + #define DLLEXPORT __declspec(dllexport) __stdcall + #endif // __cplusplus + #elif defined(CYGWIN) + #define DLLEXPORT __stdcall + #elif defined(__APPLE__) + #ifdef __cplusplus + #define DLLEXPORT + #else + #define DLLEXPORT + #endif #else #define DLLEXPORT #endif - #else - #define DLLEXPORT #endif #endif -//#include "epanet_export.h" // --- Define the EPANET toolkit constants @@ -141,10 +144,11 @@ typedef enum { EN_NEXTEVENTIDX = 15 } EN_TimeProperty; - typedef enum { EN_ITERATIONS = 0, - EN_RELATIVEERROR = 1 + EN_RELATIVEERROR = 1, + EN_MAXHEADERROR = 2, + EN_MAXFLOWCHANGE = 3 } EN_AnalysisStatistic; typedef enum { @@ -153,7 +157,7 @@ typedef enum { EN_LINKCOUNT = 2, /**< Number of Links (Pipes + Pumps + Valves) */ EN_PATCOUNT = 3, /**< Number of Time Patterns */ EN_CURVECOUNT = 4, /**< Number of Curves */ - EN_CONTROLCOUNT = 5, /**< Number of Control Statements */ + EN_CONTROLCOUNT = 5, /**< Number of Control Statements */ EN_RULECOUNT = 6 /**< Number of Rule-based Control Statements */ } EN_CountType; @@ -208,6 +212,10 @@ typedef enum { EN_CMD = 9 } EN_FlowUnits; +typedef enum { /* Demand model types. */ + EN_DDA = 0, /**< Demand driven analysis */ + EN_PDA = 1 /**< Pressure driven analysis */ +} EN_DemandModel; /// Simulation Option codes typedef enum { @@ -271,7 +279,8 @@ extern "C" { /** @brief The EPANET Project wrapper object */ - typedef struct EN_Project EN_Project; + typedef void *EN_ProjectHandle; + typedef struct EN_Pattern EN_Pattern; typedef struct EN_Curve EN_Curve; @@ -288,7 +297,8 @@ extern "C" { as it carries out its computations. If this feature is not needed then the argument should be NULL. */ - int DLLEXPORT ENepanet(char *inpFile, char *rptFile, char *binOutFile, void (*callback) (char *)); + int DLLEXPORT ENepanet(const char *inpFile, const char *rptFile, + const char *binOutFile, void (*callback) (char *)); /** @brief Initializes an EPANET session @@ -524,7 +534,29 @@ extern "C" { @return Error code */ int DLLEXPORT ENsetflowunits(int code); - + + /** + @brief Retrieves the type of demand model in use and its parameters + @param[out] type Type of demand model (EN_DDA or EN_PDA) + @param[out] pmin Pressure below which there is no demand + @param[out] preq Pressure required to deliver full demand + @param[out] pexp Pressure exponent in demand function + @return Error code + */ + int DLLEXPORT ENgetdemandmodel(int *type, EN_API_FLOAT_TYPE *pmin, + EN_API_FLOAT_TYPE *preq, EN_API_FLOAT_TYPE *pexp); + + /** + @brief Sets the type of demand model to use and its parameters + @param type Type of demand model (EN_DDA or EN_PDA) + @param pmin Pressure below which there is no demand + @param preq Pressure required to deliver full demand + @param pexp Pressure exponent in demand function + @return Error code + */ + int DLLEXPORT ENsetdemandmodel(int type, EN_API_FLOAT_TYPE pmin, + EN_API_FLOAT_TYPE preq, EN_API_FLOAT_TYPE pexp); + /** @brief Retrieves the index of the time pattern with specified ID @param id String ID of the time pattern @@ -806,7 +838,7 @@ extern "C" { int DLLEXPORT ENsetnodevalue(int index, int code, EN_API_FLOAT_TYPE v); /** - @brief Set a proprty value for a link. + @brief Set a property value for a link. @param index The index of a link. First link is index 1. @param code The code for the property to set. @param v The value to set for this link and property. @@ -1131,115 +1163,131 @@ extern "C" { int DLLEXPORT ENdeletelink(int linkIndex); - - /*************************************************** Threadsafe versions of all epanet functions ***************************************************/ - int DLLEXPORT EN_alloc(EN_Project **p); - int DLLEXPORT EN_free(EN_Project *p); - int DLLEXPORT EN_epanet(char *inpFile, char *rptFile, char *binOutFile, void (*callback) (char *)); - int DLLEXPORT EN_init(EN_Project *p, char *rptFile, char *binOutFile, EN_FlowUnits UnitsType, EN_FormType HeadlossFormula); - int DLLEXPORT EN_open(EN_Project *p, char *inpFile, char *rptFile, char *binOutFile); - int DLLEXPORT EN_saveinpfile(EN_Project *p, char *filename); - int DLLEXPORT EN_close(EN_Project *p); - int DLLEXPORT EN_solveH(EN_Project *p); - int DLLEXPORT EN_saveH(EN_Project *p); - int DLLEXPORT EN_openH(EN_Project *p); - int DLLEXPORT EN_initH(EN_Project *p, int EN_SaveOption); - int DLLEXPORT EN_runH(EN_Project *p, long *currentTime); - int DLLEXPORT EN_nextH(EN_Project *p, long *tStep); - int DLLEXPORT EN_closeH(EN_Project *p); - int DLLEXPORT EN_savehydfile(EN_Project *p, char *filename); - int DLLEXPORT EN_usehydfile(EN_Project *p, char *filename); - int DLLEXPORT EN_solveQ(EN_Project *p); - int DLLEXPORT EN_openQ(EN_Project *p); - int DLLEXPORT EN_initQ(EN_Project *p, int saveFlag); - int DLLEXPORT EN_runQ(EN_Project *p, long *currentTime); - int DLLEXPORT EN_nextQ(EN_Project *p, long *tStep); - int DLLEXPORT EN_stepQ(EN_Project *p, long *timeLeft); - int DLLEXPORT EN_closeQ(EN_Project *p); - int DLLEXPORT EN_writeline(EN_Project *p, char *line); - int DLLEXPORT EN_report(EN_Project *p); - int DLLEXPORT EN_resetreport(EN_Project *p); - int DLLEXPORT EN_setreport(EN_Project *p, char *reportFormat); - int DLLEXPORT EN_getcontrol(EN_Project *p, int controlIndex, int *controlType, int *linkIndex, EN_API_FLOAT_TYPE *setting, int *nodeIndex, EN_API_FLOAT_TYPE *level); - int DLLEXPORT EN_getcount(EN_Project *p, EN_CountType code, int *count); - int DLLEXPORT EN_getoption(EN_Project *p, EN_Option opt, EN_API_FLOAT_TYPE *value); - int DLLEXPORT EN_gettimeparam(EN_Project *p, int code, long *value); - int DLLEXPORT EN_getflowunits(EN_Project *p, int *code); - int DLLEXPORT EN_setflowunits(EN_Project *p, int code); - int DLLEXPORT EN_getpatternindex(EN_Project *p, char *id, int *index); - int DLLEXPORT EN_getpatternid(EN_Project *p, int index, char *id); - int DLLEXPORT EN_getpatternlen(EN_Project *p, int index, int *len); - int DLLEXPORT EN_getpatternvalue(EN_Project *p, int index, int period, EN_API_FLOAT_TYPE *value); - int DLLEXPORT EN_getaveragepatternvalue(EN_Project *p, int index, EN_API_FLOAT_TYPE *value); - int DLLEXPORT EN_getqualtype(EN_Project *p, int *qualcode, int *tracenode); + int DLLEXPORT EN_createproject(EN_ProjectHandle *ph); + int DLLEXPORT EN_deleteproject(EN_ProjectHandle *ph); + + void DLLEXPORT EN_clearError(EN_ProjectHandle ph); + int DLLEXPORT EN_checkError(EN_ProjectHandle ph, char** msg_buffer); + + //int DLLEXPORT EN_epanet(EN_ProjectHandle ph, const char *f1, const char *f2, + // const char *f3, void(*pviewprog)(char *)); + int DLLEXPORT EN_init(EN_ProjectHandle *ph, char *rptFile, char *binOutFile, + EN_FlowUnits UnitsType, EN_FormType HeadlossFormula); + + int DLLEXPORT EN_open(EN_ProjectHandle ph, const char *inpFile, + const char *rptFile, const char *binOutFile); + + int DLLEXPORT EN_saveinpfile(EN_ProjectHandle ph, char *filename); + + int DLLEXPORT EN_close(EN_ProjectHandle ph); + int DLLEXPORT EN_solveH(EN_ProjectHandle ph); + + int DLLEXPORT EN_saveH(EN_ProjectHandle ph); + int DLLEXPORT EN_openH(EN_ProjectHandle ph); + int DLLEXPORT EN_initH(EN_ProjectHandle ph, int EN_SaveOption); + int DLLEXPORT EN_runH(EN_ProjectHandle ph, long *currentTime); + int DLLEXPORT EN_nextH(EN_ProjectHandle ph, long *tStep); + int DLLEXPORT EN_closeH(EN_ProjectHandle ph); + int DLLEXPORT EN_savehydfile(EN_ProjectHandle ph, char *filename); + int DLLEXPORT EN_usehydfile(EN_ProjectHandle ph, char *filename); + + int DLLEXPORT EN_solveQ(EN_ProjectHandle ph); + int DLLEXPORT EN_openQ(EN_ProjectHandle ph); + int DLLEXPORT EN_initQ(EN_ProjectHandle ph, int saveFlag); + int DLLEXPORT EN_runQ(EN_ProjectHandle ph, long *currentTime); + int DLLEXPORT EN_nextQ(EN_ProjectHandle ph, long *tStep); + int DLLEXPORT EN_stepQ(EN_ProjectHandle ph, long *timeLeft); + int DLLEXPORT EN_closeQ(EN_ProjectHandle ph); + int DLLEXPORT EN_writeline(EN_ProjectHandle ph, char *line); + + int DLLEXPORT EN_report(EN_ProjectHandle ph); + int DLLEXPORT EN_resetreport(EN_ProjectHandle ph); + int DLLEXPORT EN_setreport(EN_ProjectHandle ph, char *reportFormat); + + int DLLEXPORT EN_getcontrol(EN_ProjectHandle ph, int controlIndex, int *controlType, int *linkIndex, EN_API_FLOAT_TYPE *setting, int *nodeIndex, EN_API_FLOAT_TYPE *level); + int DLLEXPORT EN_getcount(EN_ProjectHandle ph, EN_CountType code, int *count); + int DLLEXPORT EN_getoption(EN_ProjectHandle ph, EN_Option opt, EN_API_FLOAT_TYPE *value); + int DLLEXPORT EN_gettimeparam(EN_ProjectHandle ph, int code, long *value); + int DLLEXPORT EN_getflowunits(EN_ProjectHandle ph, int *code); + int DLLEXPORT EN_setflowunits(EN_ProjectHandle ph, int code); + int DLLEXPORT EN_getpatternindex(EN_ProjectHandle ph, char *id, int *index); + int DLLEXPORT EN_getpatternid(EN_ProjectHandle ph, int index, char *id); + int DLLEXPORT EN_getpatternlen(EN_ProjectHandle ph, int index, int *len); + int DLLEXPORT EN_getpatternvalue(EN_ProjectHandle ph, int index, int period, EN_API_FLOAT_TYPE *value); + int DLLEXPORT EN_getaveragepatternvalue(EN_ProjectHandle ph, int index, EN_API_FLOAT_TYPE *value); + int DLLEXPORT EN_getqualtype(EN_ProjectHandle ph, int *qualcode, int *tracenode); int DLLEXPORT EN_geterror(int errcode, char *errmsg, int maxLen); - int DLLEXPORT EN_getstatistic(EN_Project *p, int code, EN_API_FLOAT_TYPE* value); - int DLLEXPORT EN_getnodeindex(EN_Project *p, char *id, int *index); - int DLLEXPORT EN_getnodeid(EN_Project *p, int index, char *id); - int DLLEXPORT EN_getnodetype(EN_Project *p, int index, int *code); - int DLLEXPORT EN_getnodevalue(EN_Project *p, int index, int code, EN_API_FLOAT_TYPE *value); - int DLLEXPORT EN_getcoord(EN_Project *p, int index, EN_API_FLOAT_TYPE *x, EN_API_FLOAT_TYPE *y); - int DLLEXPORT EN_setcoord(EN_Project *p, int index, EN_API_FLOAT_TYPE x, EN_API_FLOAT_TYPE y); - int DLLEXPORT EN_getnumdemands(EN_Project *p, int nodeIndex, int *numDemands); - int DLLEXPORT EN_getbasedemand(EN_Project *p, int nodeIndex, int demandIndex, EN_API_FLOAT_TYPE *baseDemand); - int DLLEXPORT EN_getdemandpattern(EN_Project *p, int nodeIndex, int demandIndex, int *pattIndex); - int DLLEXPORT EN_getlinkindex(EN_Project *p, char *id, int *index); - int DLLEXPORT EN_getlinkid(EN_Project *p, int index, char *id); - int DLLEXPORT EN_getlinktype(EN_Project *p, int index, EN_LinkType *code); - int DLLEXPORT EN_setlinktype(EN_Project *p, char *id, EN_LinkType type); - int DLLEXPORT EN_getlinknodes(EN_Project *p, int index, int *node1, int *node2); - int DLLEXPORT EN_getlinkvalue(EN_Project *p, int index, EN_LinkProperty code, EN_API_FLOAT_TYPE *value); - int DLLEXPORT EN_getcurve(EN_Project *p, int curveIndex, char* id, int *nValues, EN_API_FLOAT_TYPE **xValues, EN_API_FLOAT_TYPE **yValues); - int DLLEXPORT EN_getheadcurveindex(EN_Project *p, int pumpIndex, int *curveIndex); - int DLLEXPORT EN_setheadcurveindex(EN_Project *p, int pumpIndex, int curveIndex); - int DLLEXPORT EN_getpumptype(EN_Project *p, int linkIndex, int *outType); - int DLLEXPORT EN_getcurvetype(EN_Project *p, int curveIndex, int *outType); + + int DLLEXPORT EN_getstatistic(EN_ProjectHandle ph, int code, EN_API_FLOAT_TYPE* value); + int DLLEXPORT EN_getnodeindex(EN_ProjectHandle ph, char *id, int *index); + int DLLEXPORT EN_getnodeid(EN_ProjectHandle ph, int index, char *id); + int DLLEXPORT EN_getnodetype(EN_ProjectHandle ph, int index, int *code); + int DLLEXPORT EN_getnodevalue(EN_ProjectHandle ph, int index, int code, EN_API_FLOAT_TYPE *value); + int DLLEXPORT EN_getcoord(EN_ProjectHandle ph, int index, EN_API_FLOAT_TYPE *x, EN_API_FLOAT_TYPE *y); + int DLLEXPORT EN_setcoord(EN_ProjectHandle ph, int index, EN_API_FLOAT_TYPE x, EN_API_FLOAT_TYPE y); + int DLLEXPORT EN_getnumdemands(EN_ProjectHandle ph, int nodeIndex, int *numDemands); + int DLLEXPORT EN_getbasedemand(EN_ProjectHandle ph, int nodeIndex, int demandIndex, EN_API_FLOAT_TYPE *baseDemand); + int DLLEXPORT EN_getdemandpattern(EN_ProjectHandle ph, int nodeIndex, int demandIndex, int *pattIndex); + int DLLEXPORT EN_getlinkindex(EN_ProjectHandle ph, char *id, int *index); + int DLLEXPORT EN_getlinkid(EN_ProjectHandle ph, int index, char *id); + int DLLEXPORT EN_getlinktype(EN_ProjectHandle ph, int index, EN_LinkType *code); + int DLLEXPORT EN_setlinktype(EN_ProjectHandle ph, char *id, EN_LinkType type); + int DLLEXPORT EN_getlinknodes(EN_ProjectHandle ph, int index, int *node1, int *node2); + int DLLEXPORT EN_getlinkvalue(EN_ProjectHandle ph, int index, EN_LinkProperty code, EN_API_FLOAT_TYPE *value); + int DLLEXPORT EN_getcurve(EN_ProjectHandle ph, int curveIndex, char* id, int *nValues, EN_API_FLOAT_TYPE **xValues, EN_API_FLOAT_TYPE **yValues); + int DLLEXPORT EN_getheadcurveindex(EN_ProjectHandle ph, int pumpIndex, int *curveIndex); + int DLLEXPORT EN_setheadcurveindex(EN_ProjectHandle ph, int pumpIndex, int curveIndex); + int DLLEXPORT EN_getpumptype(EN_ProjectHandle ph, int linkIndex, int *outType); + int DLLEXPORT EN_getcurvetype(EN_ProjectHandle ph, int curveIndex, int *outType); + int DLLEXPORT EN_getversion(int *version); - int DLLEXPORT EN_setcontrol(EN_Project *p, int cindex, int ctype, int lindex, EN_API_FLOAT_TYPE setting, int nindex, EN_API_FLOAT_TYPE level); - int DLLEXPORT EN_setnodevalue(EN_Project *p, int index, int code, EN_API_FLOAT_TYPE v); - int DLLEXPORT EN_setlinkvalue(EN_Project *p, int index, int code, EN_API_FLOAT_TYPE v); - int DLLEXPORT EN_addpattern(EN_Project *p, char *id); - int DLLEXPORT EN_setpattern(EN_Project *p, int index, EN_API_FLOAT_TYPE *f, int len); - int DLLEXPORT EN_setpatternvalue(EN_Project *p, int index, int period, EN_API_FLOAT_TYPE value); - int DLLEXPORT EN_settimeparam(EN_Project *p, int code, long value); - int DLLEXPORT EN_setoption(EN_Project *p, int code, EN_API_FLOAT_TYPE v); - int DLLEXPORT EN_setstatusreport(EN_Project *p, int code); - int DLLEXPORT EN_setqualtype(EN_Project *p, int qualcode, char *chemname, char *chemunits, char *tracenode); - int DLLEXPORT EN_getqualinfo(EN_Project *p, int *qualcode, char *chemname, char *chemunits, int *tracenode); - int DLLEXPORT EN_setbasedemand(EN_Project *p, int nodeIndex, int demandIdx, EN_API_FLOAT_TYPE baseDemand); - int DLLEXPORT EN_getcurveindex(EN_Project *p, char *id, int *index); - int DLLEXPORT EN_getcurveid(EN_Project *p, int index, char *id); - int DLLEXPORT EN_getcurvelen(EN_Project *p, int index, int *len); - int DLLEXPORT EN_getcurvevalue(EN_Project *p, int curveIndex, int pointIndex, EN_API_FLOAT_TYPE *x, EN_API_FLOAT_TYPE *y); - int DLLEXPORT EN_setcurvevalue(EN_Project *p, int curveIndex, int pointIndex, EN_API_FLOAT_TYPE x, EN_API_FLOAT_TYPE y); - int DLLEXPORT EN_setcurve(EN_Project *p, int index, EN_API_FLOAT_TYPE *x, EN_API_FLOAT_TYPE *y, int len); - int DLLEXPORT EN_addcurve(EN_Project *p, char *id); - int DLLEXPORT EN_getrule(EN_Project *p, int index, int *nPremises, int *nTrueActions, int *nFalseActions, EN_API_FLOAT_TYPE *priority); - int DLLEXPORT EN_setrulepriority(EN_Project *p, int index, EN_API_FLOAT_TYPE priority); - int DLLEXPORT EN_getpremise(EN_Project *p, int indexRule, int indexPremise, int *logop, int *object, int *indexObj, int *variable, int *relop, int *status, EN_API_FLOAT_TYPE *value); - int DLLEXPORT EN_setpremise(EN_Project *p, int indexRule, int indexPremise, int logop, int object, int indexObj, int variable, int relop, int status, EN_API_FLOAT_TYPE value); - int DLLEXPORT EN_setpremiseindex(EN_Project *p, int indexRule, int indexPremise, int indexObj); - int DLLEXPORT EN_setpremisestatus(EN_Project *p, int indexRule, int indexPremise, int status); - int DLLEXPORT EN_setpremisevalue(EN_Project *p, int indexRule, int indexPremise, EN_API_FLOAT_TYPE value); - int DLLEXPORT EN_gettrueaction(EN_Project *p, int indexRule, int indexAction, int *indexLink, int *status, EN_API_FLOAT_TYPE *setting); - int DLLEXPORT EN_settrueaction(EN_Project *p, int indexRule, int indexAction, int indexLink, int status, EN_API_FLOAT_TYPE setting); - int DLLEXPORT EN_getfalseaction(EN_Project *p, int indexRule, int indexAction, int *indexLink, int *status, EN_API_FLOAT_TYPE *setting); - int DLLEXPORT EN_setfalseaction(EN_Project *p, int indexRule, int indexAction, int indexLink, int status, EN_API_FLOAT_TYPE setting); - int DLLEXPORT EN_getruleID(EN_Project *p, int indexRule, char* id); - int DLLEXPORT EN_addnode(EN_Project *p, char *id, EN_NodeType nodeType); - int DLLEXPORT EN_addlink(EN_Project *p, char *id, EN_LinkType linkType, char *fromNode, char *toNode); - int DLLEXPORT EN_deletenode(EN_Project *p, int nodeIndex); - int DLLEXPORT EN_deletelink(EN_Project *p, int linkIndex); - - - - - + + int DLLEXPORT EN_setcontrol(EN_ProjectHandle ph, int cindex, int ctype, int lindex, EN_API_FLOAT_TYPE setting, int nindex, EN_API_FLOAT_TYPE level); + int DLLEXPORT EN_setnodevalue(EN_ProjectHandle ph, int index, int code, EN_API_FLOAT_TYPE v); + int DLLEXPORT EN_setlinkvalue(EN_ProjectHandle ph, int index, int code, EN_API_FLOAT_TYPE v); + int DLLEXPORT EN_addpattern(EN_ProjectHandle ph, char *id); + int DLLEXPORT EN_setpattern(EN_ProjectHandle ph, int index, EN_API_FLOAT_TYPE *f, int len); + int DLLEXPORT EN_setpatternvalue(EN_ProjectHandle ph, int index, int period, EN_API_FLOAT_TYPE value); + int DLLEXPORT EN_settimeparam(EN_ProjectHandle ph, int code, long value); + int DLLEXPORT EN_setoption(EN_ProjectHandle ph, int code, EN_API_FLOAT_TYPE v); + int DLLEXPORT EN_setstatusreport(EN_ProjectHandle ph, int code); + int DLLEXPORT EN_setqualtype(EN_ProjectHandle ph, int qualcode, char *chemname, char *chemunits, char *tracenode); + + int DLLEXPORT EN_getdemandmodel(EN_ProjectHandle ph, int *type, EN_API_FLOAT_TYPE *pmin, + EN_API_FLOAT_TYPE *preq, EN_API_FLOAT_TYPE *pexp); + int DLLEXPORT EN_setdemandmodel(EN_ProjectHandle ph, int type, EN_API_FLOAT_TYPE pmin, + EN_API_FLOAT_TYPE preq, EN_API_FLOAT_TYPE pexp); + + int DLLEXPORT EN_getqualinfo(EN_ProjectHandle ph, int *qualcode, char *chemname, char *chemunits, int *tracenode); + int DLLEXPORT EN_setbasedemand(EN_ProjectHandle ph, int nodeIndex, int demandIdx, EN_API_FLOAT_TYPE baseDemand); + int DLLEXPORT EN_getcurveindex(EN_ProjectHandle ph, char *id, int *index); + int DLLEXPORT EN_getcurveid(EN_ProjectHandle ph, int index, char *id); + int DLLEXPORT EN_getcurvelen(EN_ProjectHandle ph, int index, int *len); + int DLLEXPORT EN_getcurvevalue(EN_ProjectHandle ph, int curveIndex, int pointIndex, EN_API_FLOAT_TYPE *x, EN_API_FLOAT_TYPE *y); + int DLLEXPORT EN_setcurvevalue(EN_ProjectHandle ph, int curveIndex, int pointIndex, EN_API_FLOAT_TYPE x, EN_API_FLOAT_TYPE y); + int DLLEXPORT EN_setcurve(EN_ProjectHandle ph, int index, EN_API_FLOAT_TYPE *x, EN_API_FLOAT_TYPE *y, int len); + int DLLEXPORT EN_addcurve(EN_ProjectHandle ph, char *id); + int DLLEXPORT EN_getrule(EN_ProjectHandle ph, int index, int *nPremises, int *nTrueActions, int *nFalseActions, EN_API_FLOAT_TYPE *priority); + int DLLEXPORT EN_setrulepriority(EN_ProjectHandle ph, int index, EN_API_FLOAT_TYPE priority); + int DLLEXPORT EN_getpremise(EN_ProjectHandle ph, int indexRule, int indexPremise, int *logop, int *object, int *indexObj, int *variable, int *relop, int *status, EN_API_FLOAT_TYPE *value); + int DLLEXPORT EN_setpremise(EN_ProjectHandle ph, int indexRule, int indexPremise, int logop, int object, int indexObj, int variable, int relop, int status, EN_API_FLOAT_TYPE value); + int DLLEXPORT EN_setpremiseindex(EN_ProjectHandle ph, int indexRule, int indexPremise, int indexObj); + int DLLEXPORT EN_setpremisestatus(EN_ProjectHandle ph, int indexRule, int indexPremise, int status); + int DLLEXPORT EN_setpremisevalue(EN_ProjectHandle ph, int indexRule, int indexPremise, EN_API_FLOAT_TYPE value); + int DLLEXPORT EN_gettrueaction(EN_ProjectHandle ph, int indexRule, int indexAction, int *indexLink, int *status, EN_API_FLOAT_TYPE *setting); + int DLLEXPORT EN_settrueaction(EN_ProjectHandle ph, int indexRule, int indexAction, int indexLink, int status, EN_API_FLOAT_TYPE setting); + int DLLEXPORT EN_getfalseaction(EN_ProjectHandle ph, int indexRule, int indexAction, int *indexLink, int *status, EN_API_FLOAT_TYPE *setting); + int DLLEXPORT EN_setfalseaction(EN_ProjectHandle ph, int indexRule, int indexAction, int indexLink, int status, EN_API_FLOAT_TYPE setting); + int DLLEXPORT EN_getruleID(EN_ProjectHandle ph, int indexRule, char* id); + int DLLEXPORT EN_addnode(EN_ProjectHandle ph, char *id, EN_NodeType nodeType); + int DLLEXPORT EN_addlink(EN_ProjectHandle ph, char *id, EN_LinkType linkType, char *fromNode, char *toNode); + int DLLEXPORT EN_deletenode(EN_ProjectHandle ph, int nodeIndex); + int DLLEXPORT EN_deletelink(EN_ProjectHandle ph, int linkIndex); #if defined(__cplusplus) } diff --git a/run/CMakeLists.txt b/run/CMakeLists.txt new file mode 100644 index 0000000..53e792b --- /dev/null +++ b/run/CMakeLists.txt @@ -0,0 +1,28 @@ +# EPANET COMMAND LINE EXECUTABLE +cmake_minimum_required (VERSION 3.0.2) + + +# Sets for output directory for executables and libraries. +set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin) +set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib) +set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib) + +# Sets the position independent code property for all targets. +set(CMAKE_POSITION_INDEPENDENT_CODE ON) + + +# Set up file groups for exe target +set(EPANET_CLI_SOURCES main.c) +include_directories(include) + +source_group("CLI" FILES ${EPANET_CLI_SOURCES}) + + +# Creates the EPANET command line executable +add_definitions(-D WITH_GENX) +add_executable(runepanet ${EPANET_CLI_SOURCES}) +if(NOT WIN32) + target_link_libraries(runepanet LINK_PUBLIC epanet m) +else(NOT WIN32) + target_link_libraries(runepanet LINK_PUBLIC epanet) +endif(NOT WIN32) diff --git a/run/main.c b/run/main.c index bda132f..ed79edf 100644 --- a/run/main.c +++ b/run/main.c @@ -5,8 +5,8 @@ #define MAXMSG 255 /* Max. # characters in message text */ #define MAXWARNCODE 99 /* text copied here, no more need of include "text.h" */ -#define FMT01 "\nEPANET Version %d.%d.%d" -#define FMT03 "\n Correct syntax is:\n %s \n" +#define FMT01 "\nEPANET Version %d.%d.%d\n" +#define FMT03 "\nUsage:\n %s []\n" #define FMT09 "\n\nEPANET completed." #define FMT10 "\nEPANET completed. There are warnings." #define FMT11 "\nEPANET completed. There are errors." diff --git a/src/enumstxt.h b/src/enumstxt.h index c3abbc3..6688a1a 100755 --- a/src/enumstxt.h +++ b/src/enumstxt.h @@ -74,6 +74,10 @@ char *PressUnitsTxt[] = {w_PSI, w_KPA, w_METERS}; +char *DemandModelTxt[] = { w_DDA, + w_PDA, + NULL }; + char *QualTxt[] = {w_NONE, w_CHEM, w_AGE, diff --git a/src/epanet.c b/src/epanet.c index 03d1300..6463c08 100644 --- a/src/epanet.c +++ b/src/epanet.c @@ -129,9 +129,14 @@ execute function x and set the error code equal to its return value. #include "funcs.h" #include "text.h" #include "types.h" -#define EXTERN -////////////////////////////////////////////#include "epanet2.h" -#include "vars.h" + +// This single global variable is used only when the library is called +// in "legacy mode" with the 2.1-style API. +EN_Project *_defaultModel; + + +// Local functions +void errorLookup(int errcode, char *errmsg, int len); /**************************************************************** @@ -156,25 +161,30 @@ execute function x and set the error code equal to its return value. ** needed then the argument should be NULL. **------------------------------------------------------------------------- */ -int DLLEXPORT ENepanet(char *f1, char *f2, char *f3, - void (*pviewprog)(char *)) { +int DLLEXPORT ENepanet(const char *f1, const char *f2, const char *f3, void (*pviewprog)(char *)) +{ int errcode = 0; - ERRCODE(EN_alloc(&_defaultModel)); + + ERRCODE(EN_createproject(&_defaultModel)); ERRCODE(EN_open(_defaultModel, f1, f2, f3)); _defaultModel->viewprog = pviewprog; + if (_defaultModel->out_files.Hydflag != USE) { ERRCODE(EN_solveH(_defaultModel)); } + ERRCODE(EN_solveQ(_defaultModel)); ERRCODE(EN_report(_defaultModel)); + EN_close(_defaultModel); - EN_free(_defaultModel); + EN_deleteproject(&_defaultModel); + return (errcode); } int DLLEXPORT ENopen(char *f1, char *f2, char *f3) { int errcode = 0; - ERRCODE(EN_alloc(&_defaultModel)); + ERRCODE(EN_createproject(&_defaultModel)); EN_open(_defaultModel, f1, f2, f3); return (errcode); } @@ -262,6 +272,16 @@ int DLLEXPORT ENsetflowunits(int code) { return EN_setflowunits(_defaultModel, code); } +int DLLEXPORT ENgetdemandmodel(int *type, EN_API_FLOAT_TYPE *pmin, EN_API_FLOAT_TYPE *preq, + EN_API_FLOAT_TYPE *pexp) { + return EN_getdemandmodel(_defaultModel, type, pmin, preq, pexp); +} + +int DLLEXPORT ENsetdemandmodel(int type, EN_API_FLOAT_TYPE pmin, EN_API_FLOAT_TYPE preq, + EN_API_FLOAT_TYPE pexp) { + return EN_setdemandmodel(_defaultModel, type, pmin, preq, pexp); +} + int DLLEXPORT ENgetpatternindex(char *id, int *index) { return EN_getpatternindex(_defaultModel, id, index); } @@ -543,28 +563,50 @@ int DLLEXPORT ENdeletenode(int index) { */ /// allocate a project pointer -int DLLEXPORT EN_alloc(EN_Project **p) +int DLLEXPORT EN_createproject(EN_ProjectHandle *ph) +// Note: No error handling available until project allocation { + int errorcode = 0; EN_Project *project = calloc(1, sizeof(EN_Project)); - *p = project; - return 0; + + if (project != NULL){ + project->error_handle = new_errormanager(&errorLookup); + *ph = project; + } + else + errorcode = -1; + + return errorcode; } -int DLLEXPORT EN_free(EN_Project *p) +int DLLEXPORT EN_deleteproject(EN_ProjectHandle *ph) +// Note: No error handling available after project deallocation { - free(p); - return 0; + int errorcode = 0; + EN_Project *p = (EN_Project*)(*ph); + + if (p == NULL) + errorcode = -1; + else + { + dst_errormanager(p->error_handle); + free(p); + + *ph = NULL; + } + + return 0; } -int DLLEXPORT EN_init(EN_Project *pr, char *f2, char *f3, + +int DLLEXPORT EN_init(EN_ProjectHandle *ph, char *f2, char *f3, EN_FlowUnits UnitsType, EN_FormType HeadlossFormula) /*---------------------------------------------------------------- ** Input: ** f2 = pointer to name of report file ** f3 = pointer to name of binary output file - UnitsType = flow units flag - HeadlossFormula = headloss formula flag - + ** UnitsType = flow units flag + ** HeadlossFormula = headloss formula flag ** Output: none ** Returns: error code ** Purpose: opens EPANET @@ -578,6 +620,8 @@ int DLLEXPORT EN_init(EN_Project *pr, char *f2, char *f3, _fpreset(); #endif + EN_Project *pr = (EN_Project*)*ph; + /* Set system flags */ pr->Openflag = TRUE; pr->hydraulics.OpenHflag = FALSE; @@ -612,10 +656,10 @@ int DLLEXPORT EN_init(EN_Project *pr, char *f2, char *f3, // initialize default pattern getpatterns(pr); - return (errcode); + return set_error(pr->error_handle, errcode); } -int DLLEXPORT EN_open(EN_Project *p, char *f1, char *f2, char *f3) +int DLLEXPORT EN_open(EN_ProjectHandle ph, const char *f1, const char *f2, const char *f3) /*---------------------------------------------------------------- ** Input: f1 = pointer to name of input file ** f2 = pointer to name of report file @@ -634,6 +678,8 @@ int DLLEXPORT EN_open(EN_Project *p, char *f1, char *f2, char *f3) _fpreset(); #endif + EN_Project *p = (EN_Project*)ph; + /* Set system flags */ p->Openflag = FALSE; p->hydraulics.OpenHflag = FALSE; @@ -654,7 +700,7 @@ int DLLEXPORT EN_open(EN_Project *p, char *f1, char *f2, char *f3) ERRCODE(openfiles(p, f1, f2, f3)); if (errcode > 0) { errmsg(p, errcode); - return (errcode); + return set_error(p->error_handle, errcode); } writelogo(p); @@ -685,10 +731,10 @@ int DLLEXPORT EN_open(EN_Project *p, char *f1, char *f2, char *f3) p->Openflag = TRUE; } else errmsg(p, errcode); - return (errcode); + return set_error(p->error_handle, errcode); } -int DLLEXPORT EN_saveinpfile(EN_Project *p, char *filename) +int DLLEXPORT EN_saveinpfile(EN_ProjectHandle ph, char *filename) /*---------------------------------------------------------------- ** Input: filename = name of INP file ** Output: none @@ -697,12 +743,15 @@ int DLLEXPORT EN_saveinpfile(EN_Project *p, char *filename) **---------------------------------------------------------------- */ { + EN_Project *p = (EN_Project*)ph; + if (!p->Openflag) - return (102); - return (saveinpfile(p, filename)); + return set_error(p->error_handle, 102); + + return set_error(p->error_handle, saveinpfile(p, filename)); } -int DLLEXPORT EN_close(EN_Project *p) +int DLLEXPORT EN_close(EN_ProjectHandle ph) /*---------------------------------------------------------------- ** Input: none ** Output: none @@ -712,6 +761,8 @@ int DLLEXPORT EN_close(EN_Project *p) */ { out_file_t *out; + + EN_Project *p = (EN_Project*)ph; if (p->Openflag) { writetime(p, FMT105); @@ -755,7 +806,8 @@ int DLLEXPORT EN_close(EN_Project *p) p->save_options.SaveHflag = FALSE; p->quality.OpenQflag = FALSE; p->save_options.SaveQflag = FALSE; - return (0); + + return set_error(p->error_handle, 0); } /* @@ -764,7 +816,7 @@ int DLLEXPORT EN_close(EN_Project *p) ---------------------------------------------------------------- */ -int DLLEXPORT EN_solveH(EN_Project *p) +int DLLEXPORT EN_solveH(EN_ProjectHandle ph) /*---------------------------------------------------------------- ** Input: none ** Output: none @@ -776,11 +828,13 @@ int DLLEXPORT EN_solveH(EN_Project *p) int errcode; long t, tstep; + EN_Project *p = (EN_Project*)ph; + /* Open hydraulics solver */ - errcode = EN_openH(p); + errcode = EN_openH(ph); if (!errcode) { /* Initialize hydraulics */ - errcode = EN_initH(p, EN_SAVE); + errcode = EN_initH(ph, EN_SAVE); writecon(FMT14); /* Analyze each hydraulic period */ @@ -799,8 +853,8 @@ int DLLEXPORT EN_solveH(EN_Project *p) /* Solve for hydraulics & advance to next time period */ tstep = 0; - ERRCODE(EN_runH(p, &t)); - ERRCODE(EN_nextH(p, &tstep)); + ERRCODE(EN_runH(ph, &t)); + ERRCODE(EN_nextH(ph, &tstep)); /*** Updated 6/24/02 ***/ writecon("\b\b\b\b\b\b\b\b\b\b"); } while (tstep > 0); @@ -811,12 +865,13 @@ int DLLEXPORT EN_solveH(EN_Project *p) /*** Updated 6/24/02 ***/ writecon("\b\b\b\b\b\b\b\b "); - EN_closeH(p); + EN_closeH(ph); errcode = MAX(errcode, p->Warnflag); - return (errcode); + + return set_error(p->error_handle, errcode); } -int DLLEXPORT EN_saveH(EN_Project *p) +int DLLEXPORT EN_saveH(EN_ProjectHandle ph) /*---------------------------------------------------------------- ** Input: none ** Output: none @@ -831,9 +886,11 @@ int DLLEXPORT EN_saveH(EN_Project *p) char tmpflag; int errcode; + EN_Project *p = (EN_Project*)ph; + /* Check if hydraulic results exist */ if (!p->save_options.SaveHflag) - return (104); + return set_error(p->error_handle, 104); /* Temporarily turn off WQ analysis */ tmpflag = p->quality.Qualflag; @@ -849,10 +906,10 @@ int DLLEXPORT EN_saveH(EN_Project *p) if (errcode) { errmsg(p, errcode); } - return (errcode); + return set_error(p->error_handle, errcode); } -int DLLEXPORT EN_openH(EN_Project *p) +int DLLEXPORT EN_openH(EN_ProjectHandle ph) /*---------------------------------------------------------------- ** Input: none ** Output: none @@ -863,16 +920,18 @@ int DLLEXPORT EN_openH(EN_Project *p) { int errcode = 0; + EN_Project *p = (EN_Project*)ph; + /* Check that input data exists */ p->hydraulics.OpenHflag = FALSE; p->save_options.SaveHflag = FALSE; if (!p->Openflag) { - return (102); + return set_error(p->error_handle, 102); } /* Check that previously saved hydraulics file not in use */ if (p->out_files.Hydflag == USE) { - return (107); + return set_error(p->error_handle, 107); } /* Open hydraulics solver */ @@ -881,11 +940,12 @@ int DLLEXPORT EN_openH(EN_Project *p) p->hydraulics.OpenHflag = TRUE; else errmsg(p, errcode); - return (errcode); + + return set_error(p->error_handle, errcode); } /*** Updated 3/1/01 ***/ -int DLLEXPORT EN_initH(EN_Project *p, int flag) +int DLLEXPORT EN_initH(EN_ProjectHandle ph, int flag) /*---------------------------------------------------------------- ** Input: flag = 2-digit flag where 1st (left) digit indicates ** if link flows should be re-initialized (1) or @@ -900,6 +960,8 @@ int DLLEXPORT EN_initH(EN_Project *p, int flag) int errcode = 0; int sflag, fflag; + EN_Project *p = (EN_Project*)ph; + /* Reset status flags */ p->save_options.SaveHflag = FALSE; p->Warnflag = FALSE; @@ -910,7 +972,7 @@ int DLLEXPORT EN_initH(EN_Project *p, int flag) /* Check that hydraulics solver was opened */ if (!p->hydraulics.OpenHflag) - return (103); + return set_error(p->error_handle, 103); /* Open hydraulics file */ p->save_options.Saveflag = FALSE; @@ -920,7 +982,7 @@ int DLLEXPORT EN_initH(EN_Project *p, int flag) p->save_options.Saveflag = TRUE; else { errmsg(p, errcode); - return errcode; + return set_error(p->error_handle, errcode); } } @@ -928,57 +990,67 @@ int DLLEXPORT EN_initH(EN_Project *p, int flag) inithyd(p, fflag); if (p->report.Statflag > 0) writeheader(p, STATHDR, 0); - return (errcode); + return set_error(p->error_handle, errcode); } -int DLLEXPORT EN_runH(EN_Project *p, long *t) { +int DLLEXPORT EN_runH(EN_ProjectHandle ph, long *t) { int errcode; + + EN_Project *p = (EN_Project*)ph; + *t = 0; if (!p->hydraulics.OpenHflag) - return (103); + return set_error(p->error_handle, 103); errcode = runhyd(p, t); if (errcode) errmsg(p, errcode); - return (errcode); + return set_error(p->error_handle, errcode); } -int DLLEXPORT EN_nextH(EN_Project *p, long *tstep) { +int DLLEXPORT EN_nextH(EN_ProjectHandle ph, long *tstep) { int errcode; + + EN_Project *p = (EN_Project*)ph; + *tstep = 0; if (!p->hydraulics.OpenHflag) - return (103); + return set_error(p->error_handle, 103); errcode = nexthyd(p, tstep); if (errcode) errmsg(p, errcode); else if (p->save_options.Saveflag && *tstep == 0) p->save_options.SaveHflag = TRUE; - return (errcode); + return set_error(p->error_handle, errcode); } -int DLLEXPORT EN_closeH(EN_Project *p) +int DLLEXPORT EN_closeH(EN_ProjectHandle ph) { + EN_Project *p = (EN_Project*)ph; + if (!p->Openflag) { - return (102); + return set_error(p->error_handle, 102); } if (p->hydraulics.OpenHflag) { closehyd(p); } p->hydraulics.OpenHflag = FALSE; - return (0); + return set_error(p->error_handle, 0); } -int DLLEXPORT EN_savehydfile(EN_Project *p, char *filename) { +int DLLEXPORT EN_savehydfile(EN_ProjectHandle ph, char *filename) { FILE *f; int c; FILE *HydFile; + EN_Project *p = (EN_Project*)ph; + /* Check that hydraulics results exist */ if (p->out_files.HydFile == NULL || !p->save_options.SaveHflag) - return (104); + return set_error(p->error_handle, 104); /* Open file */ if ((f = fopen(filename, "w+b")) == NULL) - return (305); + return set_error(p->error_handle, 305); /* Copy from HydFile to f */ HydFile = p->out_files.HydFile; @@ -987,17 +1059,19 @@ int DLLEXPORT EN_savehydfile(EN_Project *p, char *filename) { fputc(c, f); } fclose(f); - return (0); + return set_error(p->error_handle, 0); } -int DLLEXPORT EN_usehydfile(EN_Project *p, char *filename) { +int DLLEXPORT EN_usehydfile(EN_ProjectHandle ph, char *filename) { int errcode; + EN_Project *p = (EN_Project*)ph; + /* Check that input data exists & hydraulics system closed */ if (!p->Openflag) - return (102); + return set_error(p->error_handle, 102); if (p->hydraulics.OpenHflag) - return (108); + return set_error(p->error_handle, 108); /* Try to open hydraulics file */ strncpy(p->out_files.HydFname, filename, MAXFNAME); @@ -1011,7 +1085,7 @@ int DLLEXPORT EN_usehydfile(EN_Project *p, char *filename) { p->out_files.Hydflag = SCRATCH; p->save_options.SaveHflag = FALSE; } - return (errcode); + return set_error(p->error_handle, errcode); } /* @@ -1020,15 +1094,17 @@ int DLLEXPORT EN_usehydfile(EN_Project *p, char *filename) { ---------------------------------------------------------------- */ -int DLLEXPORT EN_solveQ(EN_Project *p) { +int DLLEXPORT EN_solveQ(EN_ProjectHandle ph) { int errcode; long t, tstep; + EN_Project *p = (EN_Project*)ph; + /* Open WQ solver */ - errcode = EN_openQ(p); + errcode = EN_openQ(ph); if (!errcode) { /* Initialize WQ */ - errcode = EN_initQ(p, EN_SAVE); + errcode = EN_initQ(ph, EN_SAVE); if (p->quality.Qualflag) writecon(FMT15); else { @@ -1054,8 +1130,8 @@ int DLLEXPORT EN_solveQ(EN_Project *p) { /* Retrieve current network solution & update WQ to next time period */ tstep = 0; - ERRCODE(EN_runQ(p, &t)); - ERRCODE(EN_nextQ(p, &tstep)); + ERRCODE(EN_runQ(ph, &t)); + ERRCODE(EN_nextQ(ph, &tstep)); /*** Updated 6/24/02 ***/ writecon("\b\b\b\b\b\b\b\b\b\b"); @@ -1067,18 +1143,20 @@ int DLLEXPORT EN_solveQ(EN_Project *p) { /*** Updated 6/24/02 ***/ writecon("\b\b\b\b\b\b\b\b "); - EN_closeQ(p); - return (errcode); + EN_closeQ(ph); + return set_error(p->error_handle, errcode); } -int DLLEXPORT EN_openQ(EN_Project *p) { +int DLLEXPORT EN_openQ(EN_ProjectHandle ph) { int errcode = 0; + EN_Project *p = (EN_Project*)ph; + /* Check that hydraulics results exist */ p->quality.OpenQflag = FALSE; p->save_options.SaveQflag = FALSE; if (!p->Openflag) - return (102); + return set_error(p->error_handle, 102); // !LT! todo - check for p->save_options.SaveHflag / set sequential/step mode // if (!p->save_options.SaveHflag) return(104); @@ -1088,13 +1166,16 @@ int DLLEXPORT EN_openQ(EN_Project *p) { p->quality.OpenQflag = TRUE; else errmsg(p, errcode); - return (errcode); + return set_error(p->error_handle, errcode); } -int DLLEXPORT EN_initQ(EN_Project *p, int saveflag) { +int DLLEXPORT EN_initQ(EN_ProjectHandle ph, int saveflag) { int errcode = 0; + + EN_Project *p = (EN_Project*)ph; + if (!p->quality.OpenQflag) - return (105); + return set_error(p->error_handle, 105); initqual(p); p->save_options.SaveQflag = FALSE; p->save_options.Saveflag = FALSE; @@ -1103,56 +1184,66 @@ int DLLEXPORT EN_initQ(EN_Project *p, int saveflag) { if (!errcode) p->save_options.Saveflag = TRUE; } - return (errcode); + return set_error(p->error_handle, errcode); } -int DLLEXPORT EN_runQ(EN_Project *p, long *t) { +int DLLEXPORT EN_runQ(EN_ProjectHandle ph, long *t) { int errcode; + + EN_Project *p = (EN_Project*)ph; + *t = 0; if (!p->quality.OpenQflag) - return (105); + return set_error(p->error_handle, 105); errcode = runqual(p, t); if (errcode) errmsg(p, errcode); - return (errcode); + return set_error(p->error_handle, errcode); } -int DLLEXPORT EN_nextQ(EN_Project *p, long *tstep) { +int DLLEXPORT EN_nextQ(EN_ProjectHandle ph, long *tstep) { int errcode; + + EN_Project *p = (EN_Project*)ph; + *tstep = 0; if (!p->quality.OpenQflag) - return (105); + return set_error(p->error_handle, 105); errcode = nextqual(p, tstep); if (!errcode && p->save_options.Saveflag && *tstep == 0) { p->save_options.SaveQflag = TRUE; } if (errcode) errmsg(p, errcode); - return (errcode); + return set_error(p->error_handle, errcode); } -int DLLEXPORT EN_stepQ(EN_Project *p, long *tleft) { +int DLLEXPORT EN_stepQ(EN_ProjectHandle ph, long *tleft) { int errcode; + + EN_Project *p = (EN_Project*)ph; + *tleft = 0; if (!p->quality.OpenQflag) - return (105); + return set_error(p->error_handle, 105); errcode = stepqual(p, tleft); if (!errcode && p->save_options.Saveflag && *tleft == 0) { p->save_options.SaveQflag = TRUE; } if (errcode) errmsg(p, errcode); - return (errcode); + return set_error(p->error_handle, errcode); } -int DLLEXPORT EN_closeQ(EN_Project *p) +int DLLEXPORT EN_closeQ(EN_ProjectHandle ph) { + + EN_Project *p = (EN_Project*)ph; -{ if (!p->Openflag) - return (102); + return set_error(p->error_handle, 102); closequal(p); p->quality.OpenQflag = FALSE; - return (0); + return set_error(p->error_handle, 0); } /* @@ -1161,48 +1252,59 @@ int DLLEXPORT EN_closeQ(EN_Project *p) ---------------------------------------------------------------- */ -int DLLEXPORT EN_writeline(EN_Project *p, char *line) { +int DLLEXPORT EN_writeline(EN_ProjectHandle ph, char *line) { + + EN_Project *p = (EN_Project*)ph; + if (!p->Openflag) - return (102); + return set_error(p->error_handle, 102); writeline(p, line); - return (0); + return set_error(p->error_handle, 0); } -int DLLEXPORT EN_report(EN_Project *p) { +int DLLEXPORT EN_report(EN_ProjectHandle ph) { int errcode; + EN_Project *p = (EN_Project*)ph; + /* Check if results saved to binary output file */ if (!p->save_options.SaveQflag) - return (106); + return set_error(p->error_handle, 106); errcode = writereport(p); if (errcode) errmsg(p, errcode); - return (errcode); + return set_error(p->error_handle, errcode); } -int DLLEXPORT EN_resetreport(EN_Project *p) { +int DLLEXPORT EN_resetreport(EN_ProjectHandle ph) { int i; + + EN_Project *p = (EN_Project*)ph; + if (!p->Openflag) - return (102); + return set_error(p->error_handle, 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); + return set_error(p->error_handle, 0); } -int DLLEXPORT EN_setreport(EN_Project *p, char *s) { +int DLLEXPORT EN_setreport(EN_ProjectHandle ph, char *s) { char s1[MAXLINE + 1]; + + EN_Project *p = (EN_Project*)ph; + if (!p->Openflag) - return (102); + return set_error(p->error_handle, 102); if (strlen(s) > MAXLINE) - return (250); + return set_error(p->error_handle, 250); strcpy(s1, s); if (setreport(p, s1) > 0) - return (250); + return set_error(p->error_handle, 250); else - return (0); + return set_error(p->error_handle, 0); } /* @@ -1225,14 +1327,16 @@ int DLLEXPORT EN_getversion(int *v) */ { *v = CODEVERSION; - return (0); + return 0; } -int DLLEXPORT EN_getcontrol(EN_Project *pr, int cindex, int *ctype, int *lindex, +int DLLEXPORT EN_getcontrol(EN_ProjectHandle ph, int cindex, int *ctype, int *lindex, EN_API_FLOAT_TYPE *setting, int *nindex, EN_API_FLOAT_TYPE *level) { double s, lvl; + EN_Project *pr = (EN_Project*)ph; + EN_Network *net = &pr->network; Scontrol *Control = net->Control; @@ -1241,16 +1345,16 @@ int DLLEXPORT EN_getcontrol(EN_Project *pr, int cindex, int *ctype, int *lindex, const int Njuncs = net->Njuncs; double *Ucf = pr->Ucf; - + s = 0.0; lvl = 0.0; *ctype = 0; *lindex = 0; *nindex = 0; if (!pr->Openflag) - return (102); + return set_error(pr->error_handle, 102); if (cindex < 1 || cindex > net->Ncontrols) - return (241); + return set_error(pr->error_handle, 241); *ctype = Control[cindex].Type; *lindex = Control[cindex].Link; s = Control[cindex].Setting; @@ -1283,15 +1387,18 @@ int DLLEXPORT EN_getcontrol(EN_Project *pr, int cindex, int *ctype, int *lindex, lvl = (EN_API_FLOAT_TYPE)Control[cindex].Time; *setting = (EN_API_FLOAT_TYPE)s; *level = (EN_API_FLOAT_TYPE)lvl; - return (0); + return set_error(pr->error_handle, 0); } -int DLLEXPORT EN_getcount(EN_Project *pr, EN_CountType code, int *count) { +int DLLEXPORT EN_getcount(EN_ProjectHandle ph, EN_CountType code, int *count) { + + EN_Project *pr = (EN_Project*)ph; + EN_Network *net = &pr->network; *count = 0; if (!pr->Openflag) - return (102); + return set_error(pr->error_handle, 102); switch (code) { case EN_NODECOUNT: *count = net->Nnodes; @@ -1315,13 +1422,16 @@ int DLLEXPORT EN_getcount(EN_Project *pr, EN_CountType code, int *count) { *count = net->Nrules; break; default: - return (251); + return set_error(pr->error_handle, 251); } - return (0); + return set_error(pr->error_handle, 0); } -int DLLEXPORT EN_getoption(EN_Project *pr, EN_Option code, +int DLLEXPORT EN_getoption(EN_ProjectHandle ph, EN_Option code, EN_API_FLOAT_TYPE *value) { + + EN_Project *pr = (EN_Project*)ph; + hydraulics_t *hyd = &pr->hydraulics; quality_t *qu = &pr->quality; double *Ucf = pr->Ucf; @@ -1329,7 +1439,7 @@ int DLLEXPORT EN_getoption(EN_Project *pr, EN_Option code, double v = 0.0; *value = 0.0; if (!pr->Openflag) - return (102); + return set_error(pr->error_handle, 102); switch (code) { case EN_TRIALS: v = (double)hyd->MaxIter; @@ -1356,15 +1466,17 @@ int DLLEXPORT EN_getoption(EN_Project *pr, EN_Option code, break; default: - return (251); + return set_error(pr->error_handle, 251); } *value = (EN_API_FLOAT_TYPE)v; - return (0); + return set_error(pr->error_handle, 0); } -int DLLEXPORT EN_gettimeparam(EN_Project *pr, int code, long *value) { - +int DLLEXPORT EN_gettimeparam(EN_ProjectHandle ph, int code, long *value) { int i; + + EN_Project *pr = (EN_Project*)ph; + report_options_t *rep = &pr->report; quality_t *qu = &pr->quality; time_options_t *time = &pr->time_options; @@ -1372,9 +1484,9 @@ int DLLEXPORT EN_gettimeparam(EN_Project *pr, int code, long *value) { *value = 0; if (!pr->Openflag) - return (102); + return set_error(pr->error_handle, 102); if (code < EN_DURATION || code > EN_NEXTEVENTIDX) - return (251); + return set_error(pr->error_handle, 251); switch (code) { case EN_DURATION: *value = time->Dur; @@ -1423,25 +1535,31 @@ int DLLEXPORT EN_gettimeparam(EN_Project *pr, int code, long *value) { *value = i; break; } - return (0); + return set_error(pr->error_handle, 0); } -int DLLEXPORT EN_getflowunits(EN_Project *p, int *code) { +int DLLEXPORT EN_getflowunits(EN_ProjectHandle ph, int *code) { + + EN_Project *p = (EN_Project*)ph; + *code = -1; if (!p->Openflag) - return (102); + return set_error(p->error_handle, 102); *code = p->parser.Flowflag; - return (0); + return set_error(p->error_handle, 0); } -int DLLEXPORT EN_setflowunits(EN_Project *p, int code) { +int DLLEXPORT EN_setflowunits(EN_ProjectHandle ph, int code) { int i, j; double qfactor, vfactor, hfactor, efactor, xfactor, yfactor; + + EN_Project *p = (EN_Project*)ph; + double *Ucf = p->Ucf; EN_Network *net = &p->network; if (!p->Openflag) { - return(102); + return set_error(p->error_handle, 102); } /* Determine unit system based on flow units */ @@ -1505,117 +1623,179 @@ int DLLEXPORT EN_setflowunits(EN_Project *p, int code) { } } - return(0); + return set_error(p->error_handle, 0); } -int DLLEXPORT EN_getpatternindex(EN_Project *p, char *id, int *index) { +int DLLEXPORT EN_getdemandmodel(EN_ProjectHandle ph, int *type, EN_API_FLOAT_TYPE *pmin, + EN_API_FLOAT_TYPE *preq, EN_API_FLOAT_TYPE *pexp) +{ + EN_Project *p = (EN_Project*)ph; + + *type = p->hydraulics.DemandModel; + *pmin = (EN_API_FLOAT_TYPE)(p->hydraulics.Pmin * p->Ucf[PRESSURE]); + *preq = (EN_API_FLOAT_TYPE)(p->hydraulics.Preq * p->Ucf[PRESSURE]); + *pexp = (EN_API_FLOAT_TYPE)(p->hydraulics.Pexp); + + return set_error(p->error_handle, 0); +} + +int DLLEXPORT EN_setdemandmodel(EN_ProjectHandle ph, int type, EN_API_FLOAT_TYPE pmin, + EN_API_FLOAT_TYPE preq, EN_API_FLOAT_TYPE pexp) +{ + EN_Project *p = (EN_Project*)ph; + + if (type < 0 || type > EN_PDA) return set_error(p->error_handle, 251); + if (pmin > preq || pexp <= 0.0) return set_error(p->error_handle, 202); + p->hydraulics.DemandModel = type; + p->hydraulics.Pmin = pmin / p->Ucf[PRESSURE]; + p->hydraulics.Preq = preq / p->Ucf[PRESSURE]; + p->hydraulics.Pexp = pexp; + + return set_error(p->error_handle, 0); +} + + +int DLLEXPORT EN_getpatternindex(EN_ProjectHandle ph, char *id, int *index) { int i; + + EN_Project *p = (EN_Project*)ph; + *index = 0; if (!p->Openflag) - return (102); + return set_error(p->error_handle, 102); for (i = 1; i <= p->network.Npats; i++) { if (strcmp(id, p->network.Pattern[i].ID) == 0) { *index = i; - return (0); + return set_error(p->error_handle, 0); } } *index = 0; - return (205); + return set_error(p->error_handle, 205); } -int DLLEXPORT EN_getpatternid(EN_Project *p, int index, char *id) { +int DLLEXPORT EN_getpatternid(EN_ProjectHandle ph, int index, char *id) { + + EN_Project *p = (EN_Project*)ph; + strcpy(id, ""); if (!p->Openflag) - return (102); + return set_error(p->error_handle, 102); if (index < 1 || index > p->network.Npats) - return (205); + return set_error(p->error_handle, 205); strcpy(id, p->network.Pattern[index].ID); - return (0); + return set_error(p->error_handle, 0); } -int DLLEXPORT EN_getpatternlen(EN_Project *p, int index, int *len) { +int DLLEXPORT EN_getpatternlen(EN_ProjectHandle ph, int index, int *len) { + + EN_Project *p = (EN_Project*)ph; + if (!p->Openflag) - return (102); + return set_error(p->error_handle, 102); if (index < 1 || index > p->network.Npats) - return (205); + return set_error(p->error_handle, 205); *len = p->network.Pattern[index].Length; - return (0); + return set_error(p->error_handle, 0); } -int DLLEXPORT EN_getpatternvalue(EN_Project *p, int index, int period, +int DLLEXPORT EN_getpatternvalue(EN_ProjectHandle ph, int index, int period, EN_API_FLOAT_TYPE *value) { + + EN_Project *p = (EN_Project*)ph; + *value = 0.0; if (!p->Openflag) - return (102); + return set_error(p->error_handle, 102); if (index < 1 || index > p->network.Npats) - return (205); + return set_error(p->error_handle, 205); if (period < 1 || period > p->network.Pattern[index].Length) - return (251); + return set_error(p->error_handle, 251); *value = (EN_API_FLOAT_TYPE)p->network.Pattern[index].F[period - 1]; - return (0); + return set_error(p->error_handle, 0); } -int DLLEXPORT EN_getcurveindex(EN_Project *p, char *id, int *index) { +int DLLEXPORT EN_getcurveindex(EN_ProjectHandle ph, char *id, int *index) { int i; + + EN_Project *p = (EN_Project*)ph; + *index = 0; if (!p->Openflag) - return (102); + return set_error(p->error_handle, 102); for (i = 1; i <= p->network.Ncurves; i++) { if (strcmp(id, p->network.Curve[i].ID) == 0) { *index = i; - return (0); + return set_error(p->error_handle, 0); } } *index = 0; - return (206); + return set_error(p->error_handle, 206); } -int DLLEXPORT EN_getcurveid(EN_Project *p, int index, char *id) { +int DLLEXPORT EN_getcurveid(EN_ProjectHandle ph, int index, char *id) { + + EN_Project *p = (EN_Project*)ph; + strcpy(id, ""); if (!p->Openflag) - return (102); + return set_error(p->error_handle, 102); if (index < 1 || index > p->network.Ncurves) - return (206); + return set_error(p->error_handle, 206); strcpy(id, p->network.Curve[index].ID); - return (0); + return set_error(p->error_handle, 0); } -int DLLEXPORT EN_getcurvelen(EN_Project *p, int index, int *len) { +int DLLEXPORT EN_getcurvelen(EN_ProjectHandle ph, int index, int *len) { + + EN_Project *p = (EN_Project*)ph; + if (!p->Openflag) - return (102); + return set_error(p->error_handle, 102); if (index < 1 || index > p->network.Ncurves) - return (206); + return set_error(p->error_handle, 206); *len = p->network.Curve[index].Npts; - return (0); + return set_error(p->error_handle, 0); } -int DLLEXPORT EN_getcurvevalue(EN_Project *p, int index, int pnt, +int DLLEXPORT EN_getcurvevalue(EN_ProjectHandle ph, int index, int pnt, EN_API_FLOAT_TYPE *x, EN_API_FLOAT_TYPE *y) { + + EN_Project *p = (EN_Project*)ph; + *x = 0.0; *y = 0.0; if (!p->Openflag) - return (102); + return set_error(p->error_handle, 102); if (index < 1 || index > p->network.Ncurves) - return (206); + return set_error(p->error_handle, 206); if (pnt < 1 || pnt > p->network.Curve[index].Npts) - return (251); + return set_error(p->error_handle, 251); *x = (EN_API_FLOAT_TYPE)p->network.Curve[index].X[pnt - 1]; *y = (EN_API_FLOAT_TYPE)p->network.Curve[index].Y[pnt - 1]; - return (0); + return set_error(p->error_handle, 0); } -int DLLEXPORT EN_getqualtype(EN_Project *p, int *qualcode, int *tracenode) { +int DLLEXPORT EN_getqualtype(EN_ProjectHandle ph, int *qualcode, int *tracenode) { + + EN_Project *p = (EN_Project*)ph; + *tracenode = 0; if (!p->Openflag) - return (102); + return set_error(p->error_handle, 102); *qualcode = p->quality.Qualflag; if (p->quality.Qualflag == TRACE) *tracenode = p->quality.TraceNode; - return (0); + return set_error(p->error_handle, 0); } -int DLLEXPORT EN_getqualinfo(EN_Project *p, int *qualcode, char *chemname, char *chemunits, int *tracenode) { - EN_getqualtype(p, qualcode, tracenode); + +int DLLEXPORT EN_getqualinfo(EN_ProjectHandle ph, int *qualcode, char *chemname, + char *chemunits, int *tracenode) { + + EN_Project *p = (EN_Project*)ph; + + EN_getqualtype(ph, qualcode, tracenode); + if (p->quality.Qualflag == TRACE) { strncpy(chemname, "", MAXID); strncpy(chemunits, "dimensionless", MAXID); @@ -1623,10 +1803,68 @@ int DLLEXPORT EN_getqualinfo(EN_Project *p, int *qualcode, char *chemname, char strncpy(chemname, p->quality.ChemName, MAXID); strncpy(chemunits, p->quality.ChemUnits, MAXID); } - return 0; + return set_error(p->error_handle, 0); +} + +void errorLookup(int errcode, char *dest_msg, int dest_len) +// Purpose: takes error code returns error message +{ + char *msg = NULL; + + switch (errcode) + { + case 1: msg = WARN1; + break; + case 2: msg = WARN2; + break; + case 3: msg = WARN3; + break; + case 4: msg = WARN4; + break; + case 5: msg = WARN5; + break; + case 6: msg = WARN6; + break; + default: + msg = geterrmsg(errcode, msg); + } + strncpy(dest_msg, msg, MAXMSG); +} + +void DLLEXPORT EN_clearError(EN_ProjectHandle ph) +{ + EN_Project *p = (EN_Project*)ph; + + clear_error(p->error_handle); +} + +int DLLEXPORT EN_checkError(EN_ProjectHandle ph, char** msg_buffer) +// +// Purpose: Returns the error message or NULL. +// +// Note: Caller must free memory allocated by EN_check_error +// +{ + int errorcode = 0; + char *temp = NULL; + EN_Project *p = (EN_Project*)ph; + + + if (p == NULL) return -1; + else + { + errorcode = p->error_handle->error_status; + if (errorcode) + temp = check_error(p->error_handle); + + *msg_buffer = temp; + } + + return errorcode; } int DLLEXPORT EN_geterror(int errcode, char *errmsg, int n) { + // Deprecate? char newMsg[MAXMSG+1]; switch (errcode) { @@ -1653,23 +1891,32 @@ int DLLEXPORT EN_geterror(int errcode, char *errmsg, int n) { strncpy(errmsg, newMsg, n); } if (strlen(errmsg) == 0) - return (251); + return 251; else - return (0); + return 0; } -int DLLEXPORT EN_getstatistic(EN_Project *p, int code, EN_API_FLOAT_TYPE *value) { +int DLLEXPORT EN_getstatistic(EN_ProjectHandle ph, int code, EN_API_FLOAT_TYPE *value) { + + EN_Project *p = (EN_Project*)ph; + switch (code) { case EN_ITERATIONS: - *value = (EN_API_FLOAT_TYPE)p->hydraulics.iterations; + *value = (EN_API_FLOAT_TYPE)p->hydraulics.Iterations; break; case EN_RELATIVEERROR: - *value = (EN_API_FLOAT_TYPE)p->hydraulics.relativeError; + *value = (EN_API_FLOAT_TYPE)p->hydraulics.RelativeError; break; + case EN_MAXHEADERROR: + *value = (EN_API_FLOAT_TYPE)(p->hydraulics.MaxHeadError * p->Ucf[HEAD]); + break; + case EN_MAXFLOWCHANGE: + *value = (EN_API_FLOAT_TYPE)(p->hydraulics.MaxFlowChange * p->Ucf[FLOW]); + break; default: break; } - return 0; + return set_error(p->error_handle, 0); } /* @@ -1678,33 +1925,42 @@ int DLLEXPORT EN_getstatistic(EN_Project *p, int code, EN_API_FLOAT_TYPE *value) ---------------------------------------------------------------- */ -int DLLEXPORT EN_getnodeindex(EN_Project *p, char *id, int *index) { +int DLLEXPORT EN_getnodeindex(EN_ProjectHandle ph, char *id, int *index) { + + EN_Project *p = (EN_Project*)ph; + *index = 0; if (!p->Openflag) - return (102); + return set_error(p->error_handle, 102); *index = findnode(&p->network,id); if (*index == 0) - return (203); + return set_error(p->error_handle, 203); else - return (0); + return set_error(p->error_handle, 0); } -int DLLEXPORT EN_getnodeid(EN_Project *p, int index, char *id) { +int DLLEXPORT EN_getnodeid(EN_ProjectHandle ph, int index, char *id) { + + EN_Project *p = (EN_Project*)ph; + strcpy(id, ""); if (!p->Openflag) - return (102); + return set_error(p->error_handle, 102); if (index < 1 || index > p->network.Nnodes) - return (203); + return set_error(p->error_handle, 203); strcpy(id, p->network.Node[index].ID); - return (0); + return set_error(p->error_handle, 0); } -int DLLEXPORT EN_getnodetype(EN_Project *p, int index, int *code) { +int DLLEXPORT EN_getnodetype(EN_ProjectHandle ph, int index, int *code) { + + EN_Project *p = (EN_Project*)ph; + *code = -1; if (!p->Openflag) - return (102); + return set_error(p->error_handle, 102); if (index < 1 || index > p->network.Nnodes) - return (203); + return set_error(p->error_handle, 203); if (index <= p->network.Njuncs) *code = EN_JUNCTION; else { @@ -1713,48 +1969,56 @@ int DLLEXPORT EN_getnodetype(EN_Project *p, int index, int *code) { else *code = EN_TANK; } - return (0); + return set_error(p->error_handle, 0); } -int DLLEXPORT EN_getcoord(EN_Project *p, int index, EN_API_FLOAT_TYPE *x, +int DLLEXPORT EN_getcoord(EN_ProjectHandle ph, int index, EN_API_FLOAT_TYPE *x, EN_API_FLOAT_TYPE *y) { + + EN_Project *p = (EN_Project*)ph; + if (!p->Openflag) - return (102); + return set_error(p->error_handle, 102); if (index < 1 || index > p->network.Nnodes) - return (203); + return set_error(p->error_handle, 203); if (!p->parser.Coordflag) - return (255); + return set_error(p->error_handle, 255); // check if node have coords if (p->network.Coord[index].HaveCoords == FALSE) - return (254); + return set_error(p->error_handle, 254); - *x = p->network.Coord[index].X; - *y = p->network.Coord[index].Y; - return 0; + *x = (EN_API_FLOAT_TYPE)p->network.Coord[index].X; + *y = (EN_API_FLOAT_TYPE)p->network.Coord[index].Y; + + return set_error(p->error_handle, 0); } -int DLLEXPORT EN_setcoord(EN_Project *p, int index, EN_API_FLOAT_TYPE x, +int DLLEXPORT EN_setcoord(EN_ProjectHandle ph, int index, EN_API_FLOAT_TYPE x, EN_API_FLOAT_TYPE y) { + + EN_Project *p = (EN_Project*)ph; + if (!p->Openflag) - return (102); + return set_error(p->error_handle, 102); if (index < 1 || index > p->network.Nnodes) - return (203); + return set_error(p->error_handle, 203); if (!p->parser.Coordflag) - return (255); + return set_error(p->error_handle, 255); p->network.Coord[index].X = x; p->network.Coord[index].Y = y; p->network.Coord[index].HaveCoords = TRUE; - return 0; + return set_error(p->error_handle, 0); } -int DLLEXPORT EN_getnodevalue(EN_Project *p, int index, int code, +int DLLEXPORT EN_getnodevalue(EN_ProjectHandle ph, int index, int code, EN_API_FLOAT_TYPE *value) { double v = 0.0; Pdemand demand; Psource source; + EN_Project *p = (EN_Project*)ph; EN_Network *net = &p->network; hydraulics_t *hyd = &p->hydraulics; @@ -1774,9 +2038,9 @@ int DLLEXPORT EN_getnodevalue(EN_Project *p, int index, int code, /* Check for valid arguments */ *value = 0.0; if (!p->Openflag) - return (102); + return set_error(p->error_handle, 102); if (index <= 0 || index > Nnodes) - return (203); + return set_error(p->error_handle, 203); /* Retrieve called-for parameter */ switch (code) { @@ -1820,7 +2084,7 @@ int DLLEXPORT EN_getnodevalue(EN_Project *p, int index, int code, case EN_SOURCEPAT: source = Node[index].S; if (source == NULL) - return (240); + return set_error(p->error_handle, 240); if (code == EN_SOURCEQUAL) v = source->C0; else if (code == EN_SOURCEMASS) @@ -1833,7 +2097,7 @@ int DLLEXPORT EN_getnodevalue(EN_Project *p, int index, int code, case EN_TANKLEVEL: if (index <= Njuncs) - return (251); + return set_error(p->error_handle, 251); v = (Tank[index - Njuncs].H0 - Node[index].El) * Ucf[ELEV]; break; @@ -1934,15 +2198,15 @@ int DLLEXPORT EN_getnodevalue(EN_Project *p, int index, int code, case EN_TANKVOLUME: if (index <= Njuncs) - return (251); + return set_error(p->error_handle, 251); v = tankvolume(p, index - Njuncs, hyd->NodeHead[index]) * Ucf[VOLUME]; break; default: - return (251); + return set_error(p->error_handle, 251); } *value = (EN_API_FLOAT_TYPE)v; - return (0); + return set_error(p->error_handle, 0); } /* @@ -1951,55 +2215,70 @@ int DLLEXPORT EN_getnodevalue(EN_Project *p, int index, int code, ---------------------------------------------------------------- */ -int DLLEXPORT EN_getlinkindex(EN_Project *p, char *id, int *index) { +int DLLEXPORT EN_getlinkindex(EN_ProjectHandle ph, char *id, int *index) { + + EN_Project *p = (EN_Project*)ph; + *index = 0; if (!p->Openflag) - return (102); + return set_error(p->error_handle, 102); *index = findlink(&p->network,id); if (*index == 0) - return (204); + return set_error(p->error_handle, 204); else - return (0); + return set_error(p->error_handle, 0); } -int DLLEXPORT EN_getlinkid(EN_Project *p, int index, char *id) { +int DLLEXPORT EN_getlinkid(EN_ProjectHandle ph, int index, char *id) { + + EN_Project *p = (EN_Project*)ph; + strcpy(id, ""); if (!p->Openflag) - return (102); + return set_error(p->error_handle, 102); if (index < 1 || index > p->network.Nlinks) - return (204); + return set_error(p->error_handle, 204); strcpy(id, p->network.Link[index].ID); - return (0); + return set_error(p->error_handle, 0); } -int DLLEXPORT EN_getlinktype(EN_Project *p, int index, EN_LinkType *code) { +int DLLEXPORT EN_getlinktype(EN_ProjectHandle ph, int index, EN_LinkType *code) { + + EN_Project *p = (EN_Project*)ph; + *code = -1; if (!p->Openflag) - return (102); + return set_error(p->error_handle, 102); if (index < 1 || index > p->network.Nlinks) - return (204); + return set_error(p->error_handle, 204); *code = p->network.Link[index].Type; - return (0); + return set_error(p->error_handle, 0); } -int DLLEXPORT EN_getlinknodes(EN_Project *p, int index, int *node1, +int DLLEXPORT EN_getlinknodes(EN_ProjectHandle ph, int index, int *node1, int *node2) { + + EN_Project *p = (EN_Project*)ph; + *node1 = 0; *node2 = 0; if (!p->Openflag) - return (102); + return set_error(p->error_handle, 102); if (index < 1 || index > p->network.Nlinks) - return (204); + return set_error(p->error_handle, 204); *node1 = p->network.Link[index].N1; *node2 = p->network.Link[index].N2; - return (0); + return set_error(p->error_handle, 0); } -int DLLEXPORT EN_getlinkvalue(EN_Project *p, int index, EN_LinkProperty code, EN_API_FLOAT_TYPE *value) { +int DLLEXPORT EN_getlinkvalue(EN_ProjectHandle ph, int index, EN_LinkProperty code, + EN_API_FLOAT_TYPE *value) { double a, h, q, v = 0.0; int returnValue = 0; int pmp; + EN_Project *p = (EN_Project*)ph; + EN_Network *net = &p->network; hydraulics_t *hyd = &p->hydraulics; @@ -2015,9 +2294,9 @@ int DLLEXPORT EN_getlinkvalue(EN_Project *p, int index, EN_LinkProperty code, EN /* Check for valid arguments */ *value = 0.0; if (!p->Openflag) - return (102); + return set_error(p->error_handle, 102); if (index <= 0 || index > Nlinks) - return (204); + return set_error(p->error_handle, 204); /* Retrieve called-for parameter */ switch (code) { @@ -2059,7 +2338,7 @@ int DLLEXPORT EN_getlinkvalue(EN_Project *p, int index, EN_LinkProperty code, EN case EN_INITSETTING: if (Link[index].Type == EN_PIPE || Link[index].Type == EN_CVPIPE) - return (ENgetlinkvalue(index, EN_ROUGHNESS, value)); + return set_error(p->error_handle, ENgetlinkvalue(index, EN_ROUGHNESS, value)); v = Link[index].Kc; switch (Link[index].Type) { case EN_PRV: @@ -2162,7 +2441,7 @@ int DLLEXPORT EN_getlinkvalue(EN_Project *p, int index, EN_LinkProperty code, EN case EN_SETTING: if (Link[index].Type == EN_PIPE || Link[index].Type == EN_CVPIPE) { - return (ENgetlinkvalue(index, EN_ROUGHNESS, value)); + return set_error(p->error_handle, ENgetlinkvalue(index, EN_ROUGHNESS, value)); } if (LinkSetting[index] == MISSING) { v = 0.0; @@ -2234,19 +2513,21 @@ int DLLEXPORT EN_getlinkvalue(EN_Project *p, int index, EN_LinkProperty code, EN returnValue = 251; } *value = (EN_API_FLOAT_TYPE)v; - return returnValue; + return set_error(p->error_handle, returnValue); } -int DLLEXPORT EN_getcurve(EN_Project *p, int curveIndex, char *id, int *nValues, +int DLLEXPORT EN_getcurve(EN_ProjectHandle ph, int curveIndex, char *id, int *nValues, EN_API_FLOAT_TYPE **xValues, EN_API_FLOAT_TYPE **yValues) { int iPoint, nPoints; Scurve curve; EN_API_FLOAT_TYPE *pointX, *pointY; + EN_Project *p = (EN_Project*)ph; + /* Check that input file opened */ if (!p->Openflag) - return (102); + return set_error(p->error_handle, 102); curve = p->network.Curve[curveIndex]; nPoints = curve.Npts; @@ -2267,7 +2548,7 @@ int DLLEXPORT EN_getcurve(EN_Project *p, int curveIndex, char *id, int *nValues, *xValues = pointX; *yValues = pointY; - return (0); + return set_error(p->error_handle, 0); } /* @@ -2276,7 +2557,7 @@ int DLLEXPORT EN_getcurve(EN_Project *p, int curveIndex, char *id, int *nValues, ---------------------------------------------------------------- */ -int DLLEXPORT EN_setcontrol(EN_Project *p, int cindex, int ctype, int lindex, +int DLLEXPORT EN_setcontrol(EN_ProjectHandle ph, int cindex, int ctype, int lindex, EN_API_FLOAT_TYPE setting, int nindex, EN_API_FLOAT_TYPE level) { char status = ACTIVE; @@ -2292,13 +2573,15 @@ int DLLEXPORT EN_setcontrol(EN_Project *p, int cindex, int ctype, int lindex, int Nlinks; double *Ucf; + EN_Project *p = (EN_Project*)ph; + /* Check that input file opened */ if (!p->Openflag) - return (102); + return set_error(p->error_handle, 102); /* Check that control exists */ if (cindex < 1 || cindex > p->network.Ncontrols) - return (241); + return set_error(p->error_handle, 241); net = &p->network; @@ -2316,25 +2599,25 @@ int DLLEXPORT EN_setcontrol(EN_Project *p, int cindex, int ctype, int lindex, /* Check that controlled link exists */ if (lindex == 0) { Control[cindex].Link = 0; - return (0); + return set_error(p->error_handle, 0); } if (lindex < 0 || lindex > Nlinks) - return (204); + return set_error(p->error_handle, 204); /* Cannot control check valve. */ if (Link[lindex].Type == EN_CVPIPE) - return (207); + return set_error(p->error_handle, 207); /* Check for valid parameters */ if (ctype < 0 || ctype > EN_TIMEOFDAY) - return (251); + return set_error(p->error_handle, 251); if (ctype == EN_LOWLEVEL || ctype == EN_HILEVEL) { if (nindex < 1 || nindex > Nnodes) - return (203); + return set_error(p->error_handle, 203); } else nindex = 0; if (s < 0.0 || lvl < 0.0) - return (202); + return set_error(p->error_handle, 202); /* Adjust units of control parameters */ switch (Link[lindex].Type) { @@ -2354,7 +2637,7 @@ int DLLEXPORT EN_setcontrol(EN_Project *p, int cindex, int ctype, int lindex, else if (s == 1.0) status = OPEN; else - return (202); + return set_error(p->error_handle, 202); s = Link[lindex].Kc; break; @@ -2385,10 +2668,10 @@ int DLLEXPORT EN_setcontrol(EN_Project *p, int cindex, int ctype, int lindex, Control[cindex].Setting = s; Control[cindex].Grade = lvl; Control[cindex].Time = t; - return (0); + return set_error(p->error_handle, 0); } -int DLLEXPORT EN_setnodevalue(EN_Project *p, int index, int code, EN_API_FLOAT_TYPE v) +int DLLEXPORT EN_setnodevalue(EN_ProjectHandle ph, int index, int code, EN_API_FLOAT_TYPE v) /*---------------------------------------------------------------- ** Input: index = node index ** code = node parameter code (see EPANET2.H) @@ -2399,6 +2682,8 @@ int DLLEXPORT EN_setnodevalue(EN_Project *p, int index, int code, EN_API_FLOAT_T **---------------------------------------------------------------- */ { + EN_Project *p = (EN_Project*)ph; + EN_Network *net = &p->network; hydraulics_t *hyd = &p->hydraulics; quality_t *qu = &p->quality; @@ -2419,9 +2704,9 @@ int DLLEXPORT EN_setnodevalue(EN_Project *p, int index, int code, EN_API_FLOAT_T double value = v; if (!p->Openflag) - return (102); + return set_error(p->error_handle, 102); if (index <= 0 || index > Nnodes) - return (203); + return set_error(p->error_handle, 203); switch (code) { case EN_ELEVATION: if (index <= Njuncs) @@ -2451,7 +2736,7 @@ int DLLEXPORT EN_setnodevalue(EN_Project *p, int index, int code, EN_API_FLOAT_T /* NOTE: primary demand category is last on demand list */ j = ROUND(value); if (j < 0 || j > Npats) - return (205); + return set_error(p->error_handle, 205); if (index <= Njuncs) { for (demand = Node[index].D; demand != NULL; demand = demand->next) { if (demand->next == NULL) @@ -2463,9 +2748,9 @@ int DLLEXPORT EN_setnodevalue(EN_Project *p, int index, int code, EN_API_FLOAT_T case EN_EMITTER: if (index > Njuncs) - return (203); + return set_error(p->error_handle, 203); if (value < 0.0) - return (202); + return set_error(p->error_handle, 202); if (value > 0.0) value = pow((Ucf[FLOW] / value), hyd->Qexp) / Ucf[PRESSURE]; Node[index].Ke = value; @@ -2473,7 +2758,7 @@ int DLLEXPORT EN_setnodevalue(EN_Project *p, int index, int code, EN_API_FLOAT_T case EN_INITQUAL: if (value < 0.0) - return (202); + return set_error(p->error_handle, 202); Node[index].C0 = value / Ucf[QUALITY]; if (index > Njuncs) Tank[index - Njuncs].C = Node[index].C0; @@ -2483,12 +2768,12 @@ int DLLEXPORT EN_setnodevalue(EN_Project *p, int index, int code, EN_API_FLOAT_T case EN_SOURCETYPE: case EN_SOURCEPAT: if (value < 0.0) - return (202); + return set_error(p->error_handle, 202); source = Node[index].S; if (source == NULL) { source = (struct Ssource *)malloc(sizeof(struct Ssource)); if (source == NULL) - return (101); + return set_error(p->error_handle, 101); source->Type = CONCEN; source->C0 = 0.0; source->Pat = 0; @@ -2499,21 +2784,21 @@ int DLLEXPORT EN_setnodevalue(EN_Project *p, int index, int code, EN_API_FLOAT_T } else if (code == EN_SOURCEPAT) { j = ROUND(value); if (j < 0 || j > Npats) - return (205); + return set_error(p->error_handle, 205); source->Pat = j; } else // code == EN_SOURCETYPE { j = ROUND(value); if (j < CONCEN || j > FLOWPACED) - return (251); + return set_error(p->error_handle, 251); else source->Type = (char)j; } - return (0); + return set_error(p->error_handle, 0); case EN_TANKLEVEL: if (index <= Njuncs) - return (251); + return set_error(p->error_handle, 251); j = index - Njuncs; if (Tank[j].A == 0.0) /* Tank is a reservoir */ { @@ -2525,7 +2810,7 @@ int DLLEXPORT EN_setnodevalue(EN_Project *p, int index, int code, EN_API_FLOAT_T } else { value = Node[index].El + value / Ucf[ELEV]; if (value > Tank[j].Hmax || value < Tank[j].Hmin) - return (202); + return set_error(p->error_handle, 202); Tank[j].H0 = value; Tank[j].V0 = tankvolume(p, j, Tank[j].H0); // Resetting Volume in addition to initial volume @@ -2540,9 +2825,9 @@ int DLLEXPORT EN_setnodevalue(EN_Project *p, int index, int code, EN_API_FLOAT_T case EN_TANKDIAM: if (value <= 0.0) - return (202); + return set_error(p->error_handle, 202); if (index <= Njuncs) - return (251); + return set_error(p->error_handle, 251); j = index - Njuncs; if (j > 0 && Tank[j].A > 0.0) { value /= Ucf[ELEV]; @@ -2551,81 +2836,81 @@ int DLLEXPORT EN_setnodevalue(EN_Project *p, int index, int code, EN_API_FLOAT_T Tank[j].V0 = tankvolume(p, j, Tank[j].H0); Tank[j].Vmax = tankvolume(p, j, Tank[j].Hmax); } else { - return (251); + return set_error(p->error_handle, 251); } break; case EN_MINVOLUME: if (value < 0.0) - return (202); + return set_error(p->error_handle, 202); if (index <= Njuncs) - return (251); + return set_error(p->error_handle, 251); j = index - Njuncs; if (j > 0 && Tank[j].A > 0.0) { Tank[j].Vmin = value / Ucf[VOLUME]; Tank[j].V0 = tankvolume(p, j, Tank[j].H0); Tank[j].Vmax = tankvolume(p, j, Tank[j].Hmax); } else { - return (251); + return set_error(p->error_handle, 251); } break; case EN_MINLEVEL: if (value < 0.0) - return (202); + return set_error(p->error_handle, 202); if (index <= Njuncs) - return (251); // not a tank or reservoir + return set_error(p->error_handle, 251); // not a tank or reservoir j = index - Njuncs; if (Tank[j].A == 0.0) - return (251); // node is a reservoir + return set_error(p->error_handle, 251); // node is a reservoir Htmp = value / Ucf[ELEV] + Node[index].El; if (Htmp < Tank[j].Hmax && Htmp <= Tank[j].H0) { if (Tank[j].Vcurve > 0) - return (202); + return set_error(p->error_handle, 202); Tank[j].Hmin = Htmp; Tank[j].Vmin = tankvolume(p, j, Tank[j].Hmin); } else { - return (251); + return set_error(p->error_handle, 251); } break; case EN_MAXLEVEL: if (value < 0.0) - return (202); + return set_error(p->error_handle, 202); if (index <= Njuncs) - return (251); // not a tank or reservoir + return set_error(p->error_handle, 251); // not a tank or reservoir j = index - Njuncs; if (Tank[j].A == 0.0) - return (251); // node is a reservoir + return set_error(p->error_handle, 251); // node is a reservoir Htmp = value / Ucf[ELEV] + Node[index].El; if (Htmp > Tank[j].Hmin && Htmp >= Tank[j].H0) { if (Tank[j].Vcurve > 0) - return (202); + return set_error(p->error_handle, 202); Tank[j].Hmax = Htmp; Tank[j].Vmax = tankvolume(p, j, Tank[j].Hmax); } else { - return (251); + return set_error(p->error_handle, 251); } break; case EN_MIXMODEL: j = ROUND(value); if (index <= Njuncs) - return (251); + return set_error(p->error_handle, 251); if (j < MIX1 || j > LIFO) - return (202); + return set_error(p->error_handle, 202); if (index > Njuncs && Tank[index - Njuncs].A > 0.0) { Tank[index - Njuncs].MixModel = (char)j; } else { - return (251); + return set_error(p->error_handle, 251); } break; case EN_MIXFRACTION: if (value < 0.0 || value > 1.0) - return (202); + return set_error(p->error_handle, 202); if (index <= Njuncs) - return (251); + return set_error(p->error_handle, 251); j = index - Njuncs; if (j > 0 && Tank[j].A > 0.0) { Tank[j].V1max = value * Tank[j].Vmax; @@ -2634,25 +2919,25 @@ int DLLEXPORT EN_setnodevalue(EN_Project *p, int index, int code, EN_API_FLOAT_T case EN_TANK_KBULK: if (index <= Njuncs) - return (251); + return set_error(p->error_handle, 251); j = index - Njuncs; if (j > 0 && Tank[j].A > 0.0) { Tank[j].Kb = value / SECperDAY; qu->Reactflag = 1; } else { - return (251); + return set_error(p->error_handle, 251); } break; /*** New parameter additions ends here. ***/ default: - return (251); + return set_error(p->error_handle, 251); } - return (0); + return set_error(p->error_handle, 0); } -int DLLEXPORT EN_setlinkvalue(EN_Project *p, int index, int code, +int DLLEXPORT EN_setlinkvalue(EN_ProjectHandle ph, int index, int code, EN_API_FLOAT_TYPE v) /*---------------------------------------------------------------- @@ -2665,7 +2950,8 @@ int DLLEXPORT EN_setlinkvalue(EN_Project *p, int index, int code, **---------------------------------------------------------------- */ { - + EN_Project *p = (EN_Project*)ph; + EN_Network *net = &p->network; hydraulics_t *hyd = &p->hydraulics; quality_t *qu = &p->quality; @@ -2681,14 +2967,14 @@ int DLLEXPORT EN_setlinkvalue(EN_Project *p, int index, int code, double r, value = v; if (!p->Openflag) - return (102); + return set_error(p->error_handle, 102); if (index <= 0 || index > Nlinks) - return (204); + return set_error(p->error_handle, 204); switch (code) { case EN_DIAMETER: if (Link[index].Type != EN_PUMP) { if (value <= 0.0) - return (202); + return set_error(p->error_handle, 202); value /= Ucf[DIAM]; /* Convert to feet */ r = Link[index].Diam / value; /* Ratio of old to new diam */ Link[index].Km *= SQR(r) * SQR(r); /* Adjust minor loss factor */ @@ -2700,7 +2986,7 @@ int DLLEXPORT EN_setlinkvalue(EN_Project *p, int index, int code, case EN_LENGTH: if (Link[index].Type <= EN_PIPE) { if (value <= 0.0) - return (202); + return set_error(p->error_handle, 202); Link[index].Len = value / Ucf[ELEV]; resistcoeff(p, index); } @@ -2709,7 +2995,7 @@ int DLLEXPORT EN_setlinkvalue(EN_Project *p, int index, int code, case EN_ROUGHNESS: if (Link[index].Type <= EN_PIPE) { if (value <= 0.0) - return (202); + return set_error(p->error_handle, 202); Link[index].Kc = value; if (hyd->Formflag == DW) Link[index].Kc /= (1000.0 * Ucf[ELEV]); @@ -2720,7 +3006,7 @@ int DLLEXPORT EN_setlinkvalue(EN_Project *p, int index, int code, case EN_MINORLOSS: if (Link[index].Type != EN_PUMP) { if (value <= 0.0) - return (202); + return set_error(p->error_handle, 202); Link[index].Km = 0.02517 * value / SQR(Link[index].Diam) / SQR(Link[index].Diam); } @@ -2730,10 +3016,10 @@ int DLLEXPORT EN_setlinkvalue(EN_Project *p, int index, int code, case EN_STATUS: /* Cannot set status for a check valve */ if (Link[index].Type == EN_CVPIPE) - return (207); + return set_error(p->error_handle, 207); s = (char)ROUND(value); if (s < 0 || s > 1) - return (251); + return set_error(p->error_handle, 251); if (code == EN_INITSTATUS) setlinkstatus(p, index, s, &Link[index].Stat, &Link[index].Kc); else @@ -2743,9 +3029,9 @@ int DLLEXPORT EN_setlinkvalue(EN_Project *p, int index, int code, case EN_INITSETTING: case EN_SETTING: if (value < 0.0) - return (202); + return set_error(p->error_handle, 202); if (Link[index].Type == EN_PIPE || Link[index].Type == EN_CVPIPE) - return (ENsetlinkvalue(index, EN_ROUGHNESS, v)); + return set_error(p->error_handle, ENsetlinkvalue(index, EN_ROUGHNESS, v)); else { switch (Link[index].Type) { case EN_PUMP: @@ -2763,10 +3049,10 @@ int DLLEXPORT EN_setlinkvalue(EN_Project *p, int index, int code, /*** Updated 9/7/00 ***/ case EN_GPV: - return (202); /* Cannot modify setting for GPV */ + return set_error(p->error_handle, 202); /* Cannot modify setting for GPV */ default: - return (251); + return set_error(p->error_handle, 251); } if (code == EN_INITSETTING) setlinksetting(p, index, value, &Link[index].Stat, &Link[index].Kc); @@ -2791,15 +3077,17 @@ int DLLEXPORT EN_setlinkvalue(EN_Project *p, int index, int code, break; default: - return (251); + return set_error(p->error_handle, 251); } - return (0); + return set_error(p->error_handle, 0); } -int DLLEXPORT EN_addpattern(EN_Project *p, char *id) { +int DLLEXPORT EN_addpattern(EN_ProjectHandle ph, char *id) { int i, j, n, err = 0; Spattern *tmpPat; + EN_Project *p = (EN_Project*)ph; + EN_Network *net = &p->network; parser_data_t *par = &p->parser; @@ -2811,21 +3099,21 @@ int DLLEXPORT EN_addpattern(EN_Project *p, char *id) { /* Check if a pattern with same id already exists */ if (!p->Openflag) - return (102); + return set_error(p->error_handle, 102); if (ENgetpatternindex(id, &i) == 0) - return (215); + return set_error(p->error_handle, 215); /* Check that id name is not too long */ if (strlen(id) > MAXID) - return (250); + return set_error(p->error_handle, 250); /* Allocate memory for a new array of patterns */ n = Npats + 1; tmpPat = (Spattern *)calloc(n + 1, sizeof(Spattern)); if (tmpPat == NULL) - return (101); + return set_error(p->error_handle, 101); /* Copy contents of old pattern array to new one */ @@ -2857,7 +3145,7 @@ int DLLEXPORT EN_addpattern(EN_Project *p, char *id) { if (tmpPat[i].F) free(tmpPat[i].F); free(tmpPat); - return (101); + return set_error(p->error_handle, 101); } // Replace old pattern array with new one @@ -2868,12 +3156,14 @@ int DLLEXPORT EN_addpattern(EN_Project *p, char *id) { Pattern = tmpPat; net->Npats = n; par->MaxPats = n; - return 0; + return set_error(p->error_handle, 0); } -int DLLEXPORT EN_setpattern(EN_Project *p, int index, EN_API_FLOAT_TYPE *f, int n) { +int DLLEXPORT EN_setpattern(EN_ProjectHandle ph, int index, EN_API_FLOAT_TYPE *f, int n) { int j; + EN_Project *p = (EN_Project*)ph; + EN_Network *net = &p->network; Spattern *Pattern = net->Pattern; const int Npats = net->Npats; @@ -2881,26 +3171,28 @@ int DLLEXPORT EN_setpattern(EN_Project *p, int index, EN_API_FLOAT_TYPE *f, int /* Check for valid arguments */ if (!p->Openflag) - return (102); + return set_error(p->error_handle, 102); if (index <= 0 || index > Npats) - return (205); + return set_error(p->error_handle, 205); if (n <= 0) - return (202); + return set_error(p->error_handle, 202); /* Re-set number of time periods & reallocate memory for multipliers */ Pattern[index].Length = n; Pattern[index].F = (double *)realloc(Pattern[index].F, n * sizeof(double)); if (Pattern[index].F == NULL) - return (101); + return set_error(p->error_handle, 101); /* Load multipliers into pattern */ for (j = 0; j < n; j++) Pattern[index].F[j] = f[j]; - return (0); + return set_error(p->error_handle, 0); } -int DLLEXPORT EN_setpatternvalue(EN_Project *p, int index, int period, EN_API_FLOAT_TYPE value) { +int DLLEXPORT EN_setpatternvalue(EN_ProjectHandle ph, int index, int period, EN_API_FLOAT_TYPE value) { + EN_Project *p = (EN_Project*)ph; + EN_Network *net = &p->network; Spattern *Pattern = net->Pattern; @@ -2909,17 +3201,19 @@ int DLLEXPORT EN_setpatternvalue(EN_Project *p, int index, int period, EN_API_FL if (!p->Openflag) - return (102); + return set_error(p->error_handle, 102); if (index <= 0 || index > Npats) - return (205); + return set_error(p->error_handle, 205); if (period <= 0 || period > Pattern[index].Length) - return (251); + return set_error(p->error_handle, 251); Pattern[index].F[period - 1] = value; - return (0); + return set_error(p->error_handle, 0); } -int DLLEXPORT EN_addcurve(EN_Project *p, char *id) { +int DLLEXPORT EN_addcurve(EN_ProjectHandle ph, char *id) { + EN_Project *p = (EN_Project*)ph; + EN_Network *net = &p->network; parser_data_t *par = &p->parser; Scurve *Curve = net->Curve; @@ -2930,21 +3224,21 @@ int DLLEXPORT EN_addcurve(EN_Project *p, char *id) { /* Check if a curve with same id already exists */ if (!p->Openflag) - return (102); + return set_error(p->error_handle, 102); if (ENgetcurveindex(id, &i) == 0) - return (215); + return set_error(p->error_handle, 215); /* Check that id name is not too long */ if (strlen(id) > MAXID) - return (250); + return set_error(p->error_handle, 250); /* Allocate memory for a new array of curves */ n = net->Ncurves + 1; tmpCur = (Scurve *)calloc(n + 1, sizeof(Scurve)); if (tmpCur == NULL) - return (101); + return set_error(p->error_handle, 101); /* Copy contents of old curve array to new one */ @@ -2990,7 +3284,7 @@ int DLLEXPORT EN_addcurve(EN_Project *p, char *id) { free(tmpCur[i].Y); } free(tmpCur); - return (101); + return set_error(p->error_handle, 101); } // Replace old curves array with new one @@ -3003,65 +3297,71 @@ int DLLEXPORT EN_addcurve(EN_Project *p, char *id) { net->Curve = tmpCur; net->Ncurves = n; par->MaxCurves = n; - return 0; + return set_error(p->error_handle, 0); } -int DLLEXPORT EN_setcurve(EN_Project *p, int index, EN_API_FLOAT_TYPE *x, EN_API_FLOAT_TYPE *y, int n) { +int DLLEXPORT EN_setcurve(EN_ProjectHandle ph, int index, EN_API_FLOAT_TYPE *x, EN_API_FLOAT_TYPE *y, int n) { + EN_Project *p = (EN_Project*)ph; + EN_Network *net = &p->network; Scurve *Curve = net->Curve; int j; /* Check for valid arguments */ if (!p->Openflag) - return (102); + return set_error(p->error_handle, 102); if (index <= 0 || index > net->Ncurves) - return (206); + return set_error(p->error_handle, 206); if (n <= 0) - return (202); + return set_error(p->error_handle, 202); /* Re-set number of points & reallocate memory for values */ Curve[index].Npts = n; Curve[index].X = (double *)realloc(Curve[index].X, n * sizeof(double)); Curve[index].Y = (double *)realloc(Curve[index].Y, n * sizeof(double)); if (Curve[index].X == NULL) - return (101); + return set_error(p->error_handle, 101); if (Curve[index].Y == NULL) - return (101); + return set_error(p->error_handle, 101); /* Load values into curve */ for (j = 0; j < n; j++) { Curve[index].X[j] = x[j]; Curve[index].Y[j] = y[j]; } - return (0); + return set_error(p->error_handle, 0); } -int DLLEXPORT EN_setcurvevalue(EN_Project *p, int index, int pnt, EN_API_FLOAT_TYPE x, EN_API_FLOAT_TYPE y) { - +int DLLEXPORT EN_setcurvevalue(EN_ProjectHandle ph, int index, int pnt, EN_API_FLOAT_TYPE x, EN_API_FLOAT_TYPE y) { + + EN_Project *p = (EN_Project*)ph; + EN_Network *net = &p->network; Scurve *Curve = net->Curve; const int Ncurves = net->Ncurves; if (!p->Openflag) - return (102); + return set_error(p->error_handle, 102); if (index <= 0 || index > Ncurves) - return (206); + return set_error(p->error_handle, 206); if (pnt <= 0 || pnt > Curve[index].Npts) - return (251); + return set_error(p->error_handle, 251); Curve[index].X[pnt - 1] = x; Curve[index].Y[pnt - 1] = y; - return (0); + return set_error(p->error_handle, 0); } -int DLLEXPORT EN_settimeparam(EN_Project *p, int code, long value) +int DLLEXPORT EN_settimeparam(EN_ProjectHandle ph, int code, long value) { + EN_Project *p = (EN_Project*)ph; + report_options_t *rep = &p->report; quality_t *qu = &p->quality; time_options_t *time = &p->time_options; if (!p->Openflag) - return (102); + return set_error(p->error_handle, 102); if (p->hydraulics.OpenHflag || p->quality.OpenQflag) { // --> there's nothing wrong with changing certain time parameters during a // simulation run, or before the run has started. @@ -3077,7 +3377,7 @@ int DLLEXPORT EN_settimeparam(EN_Project *p, int code, long value) */ } if (value < 0) - return (202); + return set_error(p->error_handle, 202); switch (code) { case EN_DURATION: time->Dur = value; @@ -3087,7 +3387,7 @@ int DLLEXPORT EN_settimeparam(EN_Project *p, int code, long value) case EN_HYDSTEP: if (value == 0) - return (202); + return set_error(p->error_handle, 202); time->Hstep = value; time->Hstep = MIN(time->Pstep, time->Hstep); time->Hstep = MIN(time->Rstep, time->Hstep); @@ -3096,14 +3396,14 @@ int DLLEXPORT EN_settimeparam(EN_Project *p, int code, long value) case EN_QUALSTEP: if (value == 0) - return (202); + return set_error(p->error_handle, 202); qu->Qstep = value; qu->Qstep = MIN(qu->Qstep, time->Hstep); break; case EN_PATTERNSTEP: if (value == 0) - return (202); + return set_error(p->error_handle, 202); time->Pstep = value; if (time->Hstep > time->Pstep) time->Hstep = time->Pstep; @@ -3115,7 +3415,7 @@ int DLLEXPORT EN_settimeparam(EN_Project *p, int code, long value) case EN_REPORTSTEP: if (value == 0) - return (202); + return set_error(p->error_handle, 202); time->Rstep = value; if (time->Hstep > time->Rstep) time->Hstep = time->Rstep; @@ -3123,20 +3423,20 @@ int DLLEXPORT EN_settimeparam(EN_Project *p, int code, long value) case EN_REPORTSTART: if (time->Rstart > time->Dur) - return (202); + return set_error(p->error_handle, 202); time->Rstart = value; break; case EN_RULESTEP: if (value == 0) - return (202); + return set_error(p->error_handle, 202); time->Rulestep = value; time->Rulestep = MIN(time->Rulestep, time->Hstep); break; case EN_STATISTIC: if (value > RANGE) - return (202); + return set_error(p->error_handle, 202); rep->Tstatflag = (char)value; break; @@ -3149,12 +3449,12 @@ int DLLEXPORT EN_settimeparam(EN_Project *p, int code, long value) break; default: - return (251); + return set_error(p->error_handle, 251); } - return (0); + return set_error(p->error_handle, 0); } -int DLLEXPORT EN_setoption(EN_Project *p, int code, EN_API_FLOAT_TYPE v) +int DLLEXPORT EN_setoption(EN_ProjectHandle ph, int code, EN_API_FLOAT_TYPE v) /*---------------------------------------------------------------- ** Input: code = option code (see EPANET2.H) ** v = option value @@ -3164,6 +3464,8 @@ int DLLEXPORT EN_setoption(EN_Project *p, int code, EN_API_FLOAT_TYPE v) **---------------------------------------------------------------- */ { + EN_Project *p = (EN_Project*)ph; + EN_Network *net = &p->network; hydraulics_t *hyd = &p->hydraulics; quality_t *qu = &p->quality; @@ -3176,26 +3478,26 @@ int DLLEXPORT EN_setoption(EN_Project *p, int code, EN_API_FLOAT_TYPE v) int i, j; double Ke, n, ucf, value = v; if (!p->Openflag) - return (102); + return set_error(p->error_handle, 102); switch (code) { case EN_TRIALS: if (value < 1.0) - return (202); + return set_error(p->error_handle, 202); hyd->MaxIter = (int)value; break; case EN_ACCURACY: if (value < 1.e-5 || value > 1.e-1) - return (202); + return set_error(p->error_handle, 202); hyd->Hacc = value; break; case EN_TOLERANCE: if (value < 0.0) - return (202); + return set_error(p->error_handle, 202); qu->Ctol = value / Ucf[QUALITY]; break; case EN_EMITEXPON: if (value <= 0.0) - return (202); + return set_error(p->error_handle, 202); n = 1.0 / value; ucf = pow(Ucf[FLOW], n) / Ucf[PRESSURE]; for (i = 1; i <= Njuncs; i++) { @@ -3208,38 +3510,43 @@ int DLLEXPORT EN_setoption(EN_Project *p, int code, EN_API_FLOAT_TYPE v) break; case EN_DEMANDMULT: if (value <= 0.0) - return (202); + return set_error(p->error_handle, 202); hyd->Dmult = value; break; case EN_HEADERROR: if (value < 0.0) - return (202); + return set_error(p->error_handle, 202); hyd->HeadErrorLimit = value / Ucf[HEAD]; break; case EN_FLOWCHANGE: if (value < 0.0) - return (202); + return set_error(p->error_handle, 202); hyd->FlowChangeLimit = value / Ucf[FLOW]; break; default: - return (251); + return set_error(p->error_handle, 251); } - return (0); + return set_error(p->error_handle, 0); } -int DLLEXPORT EN_setstatusreport(EN_Project *p, int code) { +int DLLEXPORT EN_setstatusreport(EN_ProjectHandle ph, int code) { int errcode = 0; + + EN_Project *p = (EN_Project*)ph; + if (code >= 0 && code <= 2) p->report.Statflag = (char)code; else errcode = 202; - return (errcode); + return set_error(p->error_handle, errcode); } -int DLLEXPORT EN_setqualtype(EN_Project *p, int qualcode, char *chemname, char *chemunits, char *tracenode) { +int DLLEXPORT EN_setqualtype(EN_ProjectHandle ph, int qualcode, char *chemname, char *chemunits, char *tracenode) { + EN_Project *p = (EN_Project*)ph; + EN_Network *net = &p->network; report_options_t *rep = &p->report; quality_t *qu = &p->quality; @@ -3251,9 +3558,9 @@ int DLLEXPORT EN_setqualtype(EN_Project *p, int qualcode, char *chemname, char * double ccf = 1.0; if (!p->Openflag) - return (102); + return set_error(p->error_handle, 102); if (qualcode < EN_NONE || qualcode > EN_TRACE) - return (251); + return set_error(p->error_handle, 251); qu->Qualflag = (char)qualcode; qu->Ctol *= Ucf[QUALITY]; if (qu->Qualflag == CHEM) /* Chemical constituent */ @@ -3271,7 +3578,7 @@ int DLLEXPORT EN_setqualtype(EN_Project *p, int qualcode, char *chemname, char * { qu->TraceNode = findnode(net,tracenode); if (qu->TraceNode == 0) - return (203); + return set_error(p->error_handle, 203); strncpy(qu->ChemName, u_PERCENT, MAXID); strncpy(qu->ChemUnits, tracenode, MAXID); @@ -3300,26 +3607,30 @@ int DLLEXPORT EN_setqualtype(EN_Project *p, int qualcode, char *chemname, char * Ucf[REACTRATE] = ccf; qu->Ctol /= Ucf[QUALITY]; - return (0); + return set_error(p->error_handle, 0); } -int DLLEXPORT EN_getheadcurveindex(EN_Project *p, int index, int *curveindex) { +int DLLEXPORT EN_getheadcurveindex(EN_ProjectHandle ph, int index, int *curveindex) { + EN_Project *p = (EN_Project*)ph; + EN_Network *net = &p->network; Slink *Link = net->Link; Spump *Pump = net->Pump; const int Nlinks = net->Nlinks; if (!p->Openflag) - return (102); + return set_error(p->error_handle, 102); if (index < 1 || index > Nlinks || EN_PUMP != Link[index].Type) - return (204); + return set_error(p->error_handle, 204); *curveindex = Pump[findpump(net, index)].Hcurve; - return (0); + return set_error(p->error_handle, 0); } -int DLLEXPORT EN_setheadcurveindex(EN_Project *p, int index, int curveindex) { +int DLLEXPORT EN_setheadcurveindex(EN_ProjectHandle ph, int index, int curveindex) { + EN_Project *p = (EN_Project*)ph; + EN_Network *net = &p->network; Slink *Link = net->Link; @@ -3331,12 +3642,12 @@ int DLLEXPORT EN_setheadcurveindex(EN_Project *p, int index, int curveindex) { Spump *pump; if (!p->Openflag) - return (102); + return set_error(p->error_handle, 102); if (index < 1 || index > Nlinks || EN_PUMP != Link[index].Type) { - return (204); + return set_error(p->error_handle, 204); } if (curveindex <= 0 || curveindex > Ncurves) { - return (206); + return set_error(p->error_handle, 206); } pIdx = findpump(net, index); pump = &p->network.Pump[pIdx]; @@ -3355,11 +3666,13 @@ int DLLEXPORT EN_setheadcurveindex(EN_Project *p, int index, int curveindex) { pump->Hmax /= Ucf[HEAD]; p->network.Curve[curveindex].Type = P_CURVE; - return (0); + return set_error(p->error_handle, 0); } -int DLLEXPORT EN_getpumptype(EN_Project *p, int index, int *type) { +int DLLEXPORT EN_getpumptype(EN_ProjectHandle ph, int index, int *type) { + EN_Project *p = (EN_Project*)ph; + EN_Network *net = &p->network; Slink *Link = net->Link; @@ -3368,23 +3681,25 @@ int DLLEXPORT EN_getpumptype(EN_Project *p, int index, int *type) { *type = -1; if (!p->Openflag) - return (102); + return set_error(p->error_handle, 102); if (index < 1 || index > Nlinks || EN_PUMP != Link[index].Type) - return (204); + return set_error(p->error_handle, 204); *type = Pump[findpump(&p->network, index)].Ptype; - return (0); + return set_error(p->error_handle, 0); } -int DLLEXPORT EN_getcurvetype(EN_Project *p, int curveindex, int *type) { +int DLLEXPORT EN_getcurvetype(EN_ProjectHandle ph, int curveindex, int *type) { + EN_Project *p = (EN_Project*)ph; + EN_Network *net = &p->network; if (!p->Openflag) - return (102); + return set_error(p->error_handle, 102); if (curveindex < 1 || curveindex > net->Ncurves) - return (206); + return set_error(p->error_handle, 206); *type = net->Curve[curveindex].Type; - return (0); + return set_error(p->error_handle, 0); } /* @@ -3393,7 +3708,7 @@ int DLLEXPORT EN_getcurvetype(EN_Project *p, int curveindex, int *type) { ---------------------------------------------------------------- */ -int openfiles(EN_Project *p, char *f1, char *f2, char *f3) +int openfiles(EN_Project *p, const char *f1, const char *f2, const char *f3) /*---------------------------------------------------------------- ** Input: f1 = pointer to name of input file ** f2 = pointer to name of report file @@ -3428,23 +3743,23 @@ int openfiles(EN_Project *p, char *f1, char *f2, char *f3) if (strcomp(f1, f2) || strcomp(f1, f3) || (strcomp(f2, f3) && (strlen(f2) > 0 || strlen(f3) > 0))) { writecon(FMT04); - return (301); + return set_error(p->error_handle, 301); } /* Attempt to open input and report files */ if ((par->InFile = fopen(f1, "rt")) == NULL) { writecon(FMT05); writecon(f1); - return (302); + return set_error(p->error_handle, 302); } if (strlen(f2) == 0) rep->RptFile = stdout; else if ((rep->RptFile = fopen(f2, "wt")) == NULL) { writecon(FMT06); - return (303); + return set_error(p->error_handle, 303); } - return (0); + return set_error(p->error_handle, 0); } /* End of openfiles */ int openhydfile(EN_Project *p) @@ -3475,7 +3790,7 @@ int openhydfile(EN_Project *p) /* If HydFile currently open, then close it if its not a scratch file */ if (out->HydFile != NULL) { if (out->Hydflag == SCRATCH) - return (0); + return set_error(p->error_handle, 0); fclose(out->HydFile); } @@ -3495,7 +3810,7 @@ int openhydfile(EN_Project *p) break; } if (out->HydFile == NULL) - return (305); + return set_error(p->error_handle, 305); /* If a previous hydraulics solution is not being used, then */ /* save the current network size parameters to the file. */ @@ -3519,22 +3834,22 @@ int openhydfile(EN_Project *p) if (out->Hydflag == USE) { fread(&magic, sizeof(INT4), 1, out->HydFile); if (magic != MAGICNUMBER) - return (306); + return set_error(p->error_handle, 306); fread(&version, sizeof(INT4), 1, out->HydFile); if (version != ENGINE_VERSION) - return (306); + return set_error(p->error_handle, 306); if (fread(nsize, sizeof(INT4), 6, out->HydFile) < 6) - return (306); + return set_error(p->error_handle, 306); if (nsize[0] != Nnodes || nsize[1] != Nlinks || nsize[2] != Ntanks || nsize[3] != Npumps || nsize[4] != Nvalves || nsize[5] != time->Dur) - return (306); + return set_error(p->error_handle, 306); p->save_options.SaveHflag = TRUE; } /* Save current position in hydraulics file */ /* where storage of hydraulic results begins */ out->HydOffset = ftell(out->HydFile); - return (errcode); + return set_error(p->error_handle, errcode); } int openoutfile(EN_Project *p) @@ -3602,7 +3917,7 @@ int openoutfile(EN_Project *p) } else out->TmpOutFile = out->OutFile; } - return (errcode); + return set_error(p->error_handle, errcode); } /* @@ -3776,7 +4091,7 @@ int allocdata(EN_Project *p) /* Allocate memory for rule base (see RULES.C) */ if (!errcode) errcode = allocrules(p); - return (errcode); + return set_error(p->error_handle, errcode); } /* End of allocdata */ void freeTmplist(STmplist *t) @@ -3926,35 +4241,6 @@ char *getTmpName(EN_Project *p, char *fname) // --- free the pointer returned by _tempnam if (name) free(name); -/* -/////////////////// DEPRECATED ///////////////////////////////////// -// --- for Windows systems: -#ifdef WINDOWS - // --- use system function tmpnam() to create a temporary file name - char name[MAXFNAME + 1]; - int n; - - tmpnam(name); - - // --- if user supplied the name of a temporary directory, - // then make it be the prefix of the full file name - n = (int)strlen(out->TmpDir); - if (n > 0) { - strcpy(fname, out->TmpDir); - if (fname[n - 1] != '\\') - strcat(fname, "\\"); - } - - // --- otherwise, use the relative path notation as the file name - // prefix so that the file will be placed in the current directory - else { - strcpy(fname, ".\\"); - } - - // --- now add the prefix to the file name - strcat(fname, name); -*/ - // --- for non-Windows systems: #else // --- use system function mkstemp() to create a temporary file name @@ -3964,7 +4250,7 @@ char *getTmpName(EN_Project *p, char *fname) return fname; } -int strcomp(char *s1, char *s2) +int strcomp(const char *s1, const char *s2) /*--------------------------------------------------------------- ** Input: s1 = character string ** s2 = character string @@ -4104,11 +4390,11 @@ char *geterrmsg(int errcode, char *msg) */ { switch (errcode) { /* Warnings */ -#define DAT(code,enumer,string) case code: strcpy(msg, string); break; +#define DAT(code,enumer,string) case code: msg = string; break; #include "errors.dat" #undef DAT default: - strcpy(msg, ""); + msg = ""; } return (msg); } @@ -4130,7 +4416,7 @@ void errmsg(EN_Project *p, int errcode) } } -void writecon(char *s) +void writecon(const char *s) /*---------------------------------------------------------------- ** Input: text string ** Output: none @@ -4159,44 +4445,52 @@ void writewin(void (*vp)(char *), char *s) } } -int DLLEXPORT EN_getnumdemands(EN_Project *p, int nodeIndex, int *numDemands) { +int DLLEXPORT EN_getnumdemands(EN_ProjectHandle ph, int nodeIndex, int *numDemands) { Pdemand d; int n = 0; + + EN_Project *p = (EN_Project*)ph; + /* Check for valid arguments */ if (!p->Openflag) - return (102); + return set_error(p->error_handle, 102); if (nodeIndex <= 0 || nodeIndex > p->network.Nnodes) - return (203); + return set_error(p->error_handle, 203); for (d = p->network.Node[nodeIndex].D; d != NULL; d = d->next) n++; *numDemands = n; - return 0; + return set_error(p->error_handle, 0); } -int DLLEXPORT EN_getbasedemand(EN_Project *p, int nodeIndex, int demandIdx, EN_API_FLOAT_TYPE *baseDemand) { +int DLLEXPORT EN_getbasedemand(EN_ProjectHandle ph, int nodeIndex, int demandIdx, EN_API_FLOAT_TYPE *baseDemand) { Pdemand d; int n = 1; + + EN_Project *p = (EN_Project*)ph; + /* Check for valid arguments */ if (!p->Openflag) - return (102); + return set_error(p->error_handle, 102); if (nodeIndex <= 0 || nodeIndex > p->network.Nnodes) - return (203); + return set_error(p->error_handle, 203); if (nodeIndex <= p->network.Njuncs) { for (d = p->network.Node[nodeIndex].D; n < demandIdx && d != NULL; d = d->next) { n++; } if (n != demandIdx) { - return (253); + return set_error(p->error_handle, 253); } *baseDemand = (EN_API_FLOAT_TYPE)(d->Base * p->Ucf[FLOW]); } else { *baseDemand = (EN_API_FLOAT_TYPE)(0.0); } - return 0; + return set_error(p->error_handle, 0); } -int DLLEXPORT EN_setbasedemand(EN_Project *pr, int nodeIndex, int demandIdx, EN_API_FLOAT_TYPE baseDemand) { +int DLLEXPORT EN_setbasedemand(EN_ProjectHandle ph, int nodeIndex, int demandIdx, EN_API_FLOAT_TYPE baseDemand) { + EN_Project *pr = (EN_Project*)ph; + EN_Network *net = &pr->network; Snode *Node = net->Node; @@ -4209,21 +4503,23 @@ int DLLEXPORT EN_setbasedemand(EN_Project *pr, int nodeIndex, int demandIdx, EN_ int n = 1; /* Check for valid arguments */ if (!pr->Openflag) - return (102); + return set_error(pr->error_handle, 102); if (nodeIndex <= 0 || nodeIndex > Nnodes) - return (203); + return set_error(pr->error_handle, 203); if (nodeIndex <= Njuncs) { for (d = Node[nodeIndex].D; n < demandIdx && d != NULL; d = d->next) n++; if (n != demandIdx) - return (253); + return set_error(pr->error_handle, 253); d->Base = baseDemand / Ucf[FLOW]; } - return 0; + return set_error(pr->error_handle, 0); } -int DLLEXPORT EN_getdemandpattern(EN_Project *p, int nodeIndex, int demandIdx, int *pattIdx) { +int DLLEXPORT EN_getdemandpattern(EN_ProjectHandle ph, int nodeIndex, int demandIdx, int *pattIdx) { + EN_Project *p = (EN_Project*)ph; + EN_Network *net = &p->network; Snode *Node = net->Node; const int Nnodes = net->Nnodes; @@ -4232,19 +4528,21 @@ int DLLEXPORT EN_getdemandpattern(EN_Project *p, int nodeIndex, int demandIdx, i int n = 1; /* Check for valid arguments */ if (!p->Openflag) - return (102); + return set_error(p->error_handle, 102); if (nodeIndex <= 0 || nodeIndex > Nnodes) - return (203); + return set_error(p->error_handle, 203); for (d = Node[nodeIndex].D; n < demandIdx && d != NULL; d = d->next) n++; if (n != demandIdx) - return (253); + return set_error(p->error_handle, 253); *pattIdx = d->Pat; - return 0; + return set_error(p->error_handle, 0); } -int DLLEXPORT EN_getaveragepatternvalue(EN_Project *p, int index, EN_API_FLOAT_TYPE *value) { +int DLLEXPORT EN_getaveragepatternvalue(EN_ProjectHandle ph, int index, EN_API_FLOAT_TYPE *value) { + EN_Project *p = (EN_Project*)ph; + EN_Network *net = &p->network; Spattern *Pattern = net->Pattern; @@ -4253,34 +4551,36 @@ int DLLEXPORT EN_getaveragepatternvalue(EN_Project *p, int index, EN_API_FLOAT_T int i; *value = 0.0; if (!p->Openflag) - return (102); + return set_error(p->error_handle, 102); if (index < 1 || index > Npats) - return (205); + return set_error(p->error_handle, 205); // if (period < 1 || period > Pattern[index].Length) return(251); for (i = 0; i < Pattern[index].Length; i++) { - *value += Pattern[index].F[i]; + *value += (EN_API_FLOAT_TYPE)Pattern[index].F[i]; } *value /= (EN_API_FLOAT_TYPE)Pattern[index].Length; - return (0); + return set_error(p->error_handle, 0); } -int DLLEXPORT EN_setlinktype(EN_Project *p, char *id, EN_LinkType toType) { +int DLLEXPORT EN_setlinktype(EN_ProjectHandle ph, char *id, EN_LinkType toType) { int i; EN_LinkType fromType; + EN_Project *p = (EN_Project*)ph; + EN_Network *net = &p->network; if (!p->Openflag) - return (102); + return set_error(p->error_handle, 102); /* Check if a link with the id exists */ if (EN_getlinkindex(p, id, &i) != 0) - return (215); + return set_error(p->error_handle, 215); /* Get the current type of the link */ ENgetlinktype(i, &fromType); if (fromType == toType) - return (0); + return set_error(p->error_handle, 0); /* Change link from Pipe */ if (toType <= EN_PIPE) { @@ -4298,13 +4598,16 @@ int DLLEXPORT EN_setlinktype(EN_Project *p, char *id, EN_LinkType toType) { } else if (fromType == EN_PUMP) { net->Npumps--; } - return 0; + return set_error(p->error_handle, 0); } -int DLLEXPORT EN_addnode(EN_Project *p, char *id, EN_NodeType nodeType) { +int DLLEXPORT EN_addnode(EN_ProjectHandle ph, char *id, EN_NodeType nodeType) { int i, nIdx; int index; struct Sdemand *demand; + + EN_Project *p = (EN_Project*)ph; + EN_Network *net = &p->network; hydraulics_t *hyd = &p->hydraulics; quality_t *qu = &p->quality; @@ -4314,13 +4617,13 @@ int DLLEXPORT EN_addnode(EN_Project *p, char *id, EN_NodeType nodeType) { /* Check if a node with same id already exists */ if (!p->Openflag) - return (102); + return set_error(p->error_handle, 102); if (ENgetnodeindex(id, &i) == 0) - return (215); + return set_error(p->error_handle, 215); /* Check that id name is not too long */ if (strlen(id) > MAXID) - return (250); + return set_error(p->error_handle, 250); /* Grow arrays to accomodate the new values */ net->Node = (Snode *)realloc(net->Node, (net->Nnodes + 2) * sizeof(Snode)); @@ -4362,7 +4665,7 @@ int DLLEXPORT EN_addnode(EN_Project *p, char *id, EN_NodeType nodeType) { } } } else { - nIdx = net->Nnodes++; + nIdx = net->Nnodes+1; node = &net->Node[nIdx]; coord = &net->Coord[nIdx]; net->Ntanks++; @@ -4412,13 +4715,16 @@ int DLLEXPORT EN_addnode(EN_Project *p, char *id, EN_NodeType nodeType) { /* Insert new node into hash table */ ENHashTableInsert(net->NodeHashTable, node->ID, nIdx); /* see HASH.C */ - return (0); + return set_error(p->error_handle, 0); } -int DLLEXPORT EN_addlink(EN_Project *p, char *id, EN_LinkType linkType, char *fromNode, +int DLLEXPORT EN_addlink(EN_ProjectHandle ph, char *id, EN_LinkType linkType, char *fromNode, char *toNode) { int i, n; int N1, N2; + + EN_Project *p = (EN_Project*)ph; + EN_Network *net = &p->network; hydraulics_t *hyd = &p->hydraulics; Slink *link; @@ -4426,21 +4732,21 @@ int DLLEXPORT EN_addlink(EN_Project *p, char *id, EN_LinkType linkType, char *fr /* Check if a link with same id already exists */ if (!p->Openflag) - return (102); + return set_error(p->error_handle, 102); if (ENgetlinkindex(id, &i) == 0) - return (215); + return set_error(p->error_handle, 215); /* Lookup the from and to nodes */ N1 = ENHashTableFind(net->NodeHashTable, fromNode); N2 = ENHashTableFind(net->NodeHashTable, toNode); if (N1 == 0 || N2 == 0) { - return (203); + return set_error(p->error_handle, 203); } /* Check that id name is not too long */ if (strlen(id) > MAXID) - return (250); + return set_error(p->error_handle, 250); net->Nlinks++; n = net->Nlinks; @@ -4522,19 +4828,22 @@ int DLLEXPORT EN_addlink(EN_Project *p, char *id, EN_LinkType linkType, char *fr } ENHashTableInsert(net->LinkHashTable, link->ID, n); - return (0); + return set_error(p->error_handle, 0); } -int DLLEXPORT EN_deletelink(EN_Project *p, int index) { +int DLLEXPORT EN_deletelink(EN_ProjectHandle ph, int index) { int i; EN_LinkType linkType; + + EN_Project *p = (EN_Project*)ph; + EN_Network *net = &p->network; Slink *link; if (!p->Openflag) - return (102); + return set_error(p->error_handle, 102); if (index <= 0 || index > net->Nlinks) - return (203); + return set_error(p->error_handle, 203); EN_getlinktype(p, index, &linkType); @@ -4581,19 +4890,21 @@ int DLLEXPORT EN_deletelink(EN_Project *p, int index) { net->Nvalves--; } - return 0; + return set_error(p->error_handle, 0); } -int DLLEXPORT EN_deletenode(EN_Project *p, int index) { +int DLLEXPORT EN_deletenode(EN_ProjectHandle ph, int index) { + + EN_Project *p = (EN_Project*)ph; EN_Network *net = &p->network; int i, nodeType; if (!p->Openflag) - return (102); + return set_error(p->error_handle, 102); if (index <= 0 || index > net->Nnodes) - return (203); + return set_error(p->error_handle, 203); EN_getnodetype(p, index, &nodeType); @@ -4649,10 +4960,10 @@ int DLLEXPORT EN_deletenode(EN_Project *p, int index) { net->Nnodes--; - return (0); + return set_error(p->error_handle, 0); } -int DLLEXPORT EN_getrule(EN_Project *pr, int index, int *nPremises, int *nTrueActions, int *nFalseActions, EN_API_FLOAT_TYPE *priority) +int DLLEXPORT EN_getrule(EN_ProjectHandle ph, int index, int *nPremises, int *nTrueActions, int *nFalseActions, EN_API_FLOAT_TYPE *priority) /*---------------------------------------------------------------- ** Input: index = index of the rule ** nPremises = number of conditions (IF AND OR) @@ -4668,10 +4979,12 @@ int DLLEXPORT EN_getrule(EN_Project *pr, int index, int *nPremises, int *nTrueAc Premise *p; Action *c; + EN_Project *pr = (EN_Project*)ph; + EN_Network *net = &pr->network; if (index > net->Nrules) - return (257); + return set_error(pr->error_handle, 257); *priority = (EN_API_FLOAT_TYPE)pr->rules.Rule[index].priority; count = 1; p = pr->rules.Rule[index].Pchain; @@ -4698,24 +5011,24 @@ int DLLEXPORT EN_getrule(EN_Project *pr, int index, int *nPremises, int *nTrueAc } } *nFalseActions = count; - return (0); + return set_error(pr->error_handle, 0); } -int DLLEXPORT EN_getpremise(EN_Project *pr, int indexRule, int idxPremise, int *logop, +int DLLEXPORT EN_getpremise(EN_ProjectHandle ph, int indexRule, int idxPremise, int *logop, int *object, int *indexObj, int *variable, int *relop, int *status, EN_API_FLOAT_TYPE *value) { int count = 1, error = 0, nPremises, a, b; EN_API_FLOAT_TYPE priority; Premise *p; - + EN_Project *pr = (EN_Project*)ph; if (indexRule > pr->network.Nrules) { - return (257); + return set_error(pr->error_handle, 257); } error = EN_getrule(pr, indexRule, &nPremises, &a, &b, &priority); if (idxPremise > nPremises) { - return (258); + return set_error(pr->error_handle, 258); } p = pr->rules.Rule[indexRule].Pchain; @@ -4729,11 +5042,11 @@ int DLLEXPORT EN_getpremise(EN_Project *pr, int indexRule, int idxPremise, int * *variable = p->variable; *relop = p->relop; *status = p->status; - *value = p[0].value; - return (0); + *value = (EN_API_FLOAT_TYPE)p[0].value; + return set_error(pr->error_handle, 0); } -int DLLEXPORT EN_setrulepriority(EN_Project *pr, int index, EN_API_FLOAT_TYPE priority) +int DLLEXPORT EN_setrulepriority(EN_ProjectHandle ph, int index, EN_API_FLOAT_TYPE priority) /*---------------------------------------------------------------- ** Input: index = index of the rule ** priority = rule priority @@ -4742,29 +5055,32 @@ int DLLEXPORT EN_setrulepriority(EN_Project *pr, int index, EN_API_FLOAT_TYPE pr **---------------------------------------------------------------- */ { + EN_Project *pr = (EN_Project*)ph; + if (index > pr->network.Nrules) { - return (257); + return set_error(pr->error_handle, 257); } pr->rules.Rule[index].priority = priority; - return (0); + return set_error(pr->error_handle, 0); } -int DLLEXPORT EN_setpremise(EN_Project *pr, int indexRule, int indexPremise, int logop, +int DLLEXPORT EN_setpremise(EN_ProjectHandle ph, int indexRule, int indexPremise, int logop, int object, int indexObj, int variable, int relop, int status, EN_API_FLOAT_TYPE value) { int count = 1, error = 0, nPremises, a, b; EN_API_FLOAT_TYPE priority; Premise *p; - + EN_Project *pr; + pr = (EN_Project*)ph; if (indexRule > pr->network.Nrules) { - return (257); + return set_error(pr->error_handle, 257); } error = EN_getrule(pr, indexRule, &nPremises, &a, &b, &priority); if (indexPremise > nPremises) { - return (258); + return set_error(pr->error_handle, 258); } p = pr->rules.Rule[indexRule].Pchain; while (count < indexPremise) { @@ -4778,19 +5094,21 @@ int DLLEXPORT EN_setpremise(EN_Project *pr, int indexRule, int indexPremise, int p->relop = relop; p->status = status; p->value = value; - return (0); + return set_error(pr->error_handle, 0); } -int DLLEXPORT EN_setpremiseindex(EN_Project *pr, int indexRule, int indexPremise, int indexObj) { +int DLLEXPORT EN_setpremiseindex(EN_ProjectHandle ph, int indexRule, int indexPremise, int indexObj) { int count = 1, error = 0, nPremises, a, b; EN_API_FLOAT_TYPE priority; Premise *p; + EN_Project *pr = (EN_Project*)ph; + if (indexRule > pr->network.Nrules) - return (257); + return set_error(pr->error_handle, 257); error = EN_getrule(pr, indexRule, &nPremises, &a, &b, &priority); if (indexPremise > nPremises) { - return (258); + return set_error(pr->error_handle, 258); } p = pr->rules.Rule[indexRule].Pchain; while (count < indexPremise) { @@ -4798,20 +5116,22 @@ int DLLEXPORT EN_setpremiseindex(EN_Project *pr, int indexRule, int indexPremise p = p->next; } p->index = indexObj; - return (0); + return set_error(pr->error_handle, 0); } -int DLLEXPORT EN_setpremisestatus(EN_Project *pr, int indexRule, int indexPremise, int status) { +int DLLEXPORT EN_setpremisestatus(EN_ProjectHandle ph, int indexRule, int indexPremise, int status) { int count = 1, error = 0, nPremises, a, b; EN_API_FLOAT_TYPE priority; Premise *p; + EN_Project *pr = (EN_Project*)ph; + if (indexRule > pr->network.Nrules) { - return (257); + return set_error(pr->error_handle, 257); } error = EN_getrule(pr, indexRule, &nPremises, &a, &b, &priority); if (indexPremise > nPremises) { - return (258); + return set_error(pr->error_handle, 258); } p = pr->rules.Rule[indexRule].Pchain; while (count < indexPremise) { @@ -4819,20 +5139,22 @@ int DLLEXPORT EN_setpremisestatus(EN_Project *pr, int indexRule, int indexPremis p = p->next; } p->status = status; - return (0); + return set_error(pr->error_handle, 0); } -int DLLEXPORT EN_setpremisevalue(EN_Project *pr, int indexRule, int indexPremise, +int DLLEXPORT EN_setpremisevalue(EN_ProjectHandle ph, int indexRule, int indexPremise, EN_API_FLOAT_TYPE value) { int count = 1, error = 0, nPremises, a, b; EN_API_FLOAT_TYPE priority; Premise *p; + EN_Project *pr = (EN_Project*)ph; + if (indexRule > pr->network.Nrules) - return (257); + return set_error(pr->error_handle, 257); error = EN_getrule(pr, indexRule, &nPremises, &a, &b, &priority); if (indexPremise > nPremises) { - return (258); + return set_error(pr->error_handle, 258); } p = pr->rules.Rule[indexRule].Pchain; while (count < indexPremise) { @@ -4840,21 +5162,23 @@ int DLLEXPORT EN_setpremisevalue(EN_Project *pr, int indexRule, int indexPremise p = p->next; } p->value = value; - return (0); + return set_error(pr->error_handle, 0); } -int DLLEXPORT EN_gettrueaction(EN_Project *pr, int indexRule, int indexAction, int *indexLink, +int DLLEXPORT EN_gettrueaction(EN_ProjectHandle ph, int indexRule, int indexAction, int *indexLink, int *status, EN_API_FLOAT_TYPE *setting) { int count = 1, error = 0, nTrueAction, c, b; EN_API_FLOAT_TYPE priority; Action *a; + EN_Project *pr = (EN_Project*)ph; + if (indexRule > pr->network.Nrules) { - return (252); + return set_error(pr->error_handle, 252); } error = EN_getrule(pr, indexRule, &c, &nTrueAction, &b, &priority); if (indexAction > nTrueAction) { - return (253); + return set_error(pr->error_handle, 253); } a = pr->rules.Rule[indexRule].Tchain; while (count < indexAction) { @@ -4863,22 +5187,24 @@ int DLLEXPORT EN_gettrueaction(EN_Project *pr, int indexRule, int indexAction, i } *indexLink = a->link; *status = a->status; - *setting = a->setting; - return (0); + *setting = (EN_API_FLOAT_TYPE)a->setting; + return set_error(pr->error_handle, 0); } -int DLLEXPORT EN_settrueaction(EN_Project *pr, int indexRule, int indexAction, int indexLink, +int DLLEXPORT EN_settrueaction(EN_ProjectHandle ph, int indexRule, int indexAction, int indexLink, int status, EN_API_FLOAT_TYPE setting) { int count = 1, error = 0, nTrueAction, c, b; EN_API_FLOAT_TYPE priority; Action *a; + EN_Project *pr = (EN_Project*)ph; + if (indexRule > pr->network.Nrules) { - return (257); + return set_error(pr->error_handle, 257); } error = EN_getrule(pr, indexRule, &c, &nTrueAction, &b, &priority); if (indexAction > nTrueAction) { - return (258); + return set_error(pr->error_handle, 258); } a = pr->rules.Rule[indexRule].Tchain; while (count < indexAction) { @@ -4888,21 +5214,23 @@ int DLLEXPORT EN_settrueaction(EN_Project *pr, int indexRule, int indexAction, i a->link = indexLink; a->status = status; a->setting = setting; - return (0); + return set_error(pr->error_handle, 0); } -int DLLEXPORT EN_getfalseaction(EN_Project *pr, int indexRule, int indexAction, int *indexLink, +int DLLEXPORT EN_getfalseaction(EN_ProjectHandle ph, int indexRule, int indexAction, int *indexLink, int *status, EN_API_FLOAT_TYPE *setting) { int count = 1, error = 0, nFalseAction, c, b; EN_API_FLOAT_TYPE priority; Action *a; + EN_Project *pr = (EN_Project*)ph; + if (indexRule > pr->network.Nrules) { - return (257); + return set_error(pr->error_handle, 257); } error = EN_getrule(pr, indexRule, &c, &b, &nFalseAction, &priority); if (indexAction > nFalseAction) { - return (258); + return set_error(pr->error_handle, 258); } a = pr->rules.Rule[indexRule].Fchain; while (count < indexAction) { @@ -4911,22 +5239,24 @@ int DLLEXPORT EN_getfalseaction(EN_Project *pr, int indexRule, int indexAction, } *indexLink = a->link; *status = a->status; - *setting = a->setting; - return (0); + *setting = (EN_API_FLOAT_TYPE)a->setting; + return set_error(pr->error_handle, 0); } -int DLLEXPORT EN_setfalseaction(EN_Project *pr, int indexRule, int indexAction, int indexLink, +int DLLEXPORT EN_setfalseaction(EN_ProjectHandle ph, int indexRule, int indexAction, int indexLink, int status, EN_API_FLOAT_TYPE setting) { int count = 1, error = 0, nFalseAction, c, b; EN_API_FLOAT_TYPE priority; Action *a; + EN_Project *pr = (EN_Project*)ph; + if (indexRule > pr->network.Nrules) { - return (257); + return set_error(pr->error_handle, 257); } error = EN_getrule(pr, indexRule, &c, &b, &nFalseAction, &priority); if (indexAction > nFalseAction) { - return (258); + return set_error(pr->error_handle, 258); } a = pr->rules.Rule[indexRule].Fchain; while (count < indexAction) { @@ -4937,17 +5267,20 @@ int DLLEXPORT EN_setfalseaction(EN_Project *pr, int indexRule, int indexAction, a->status = status; a->setting = setting; - return (0); + return set_error(pr->error_handle, 0); } -int DLLEXPORT EN_getruleID(EN_Project *pr, int indexRule, char *id) { +int DLLEXPORT EN_getruleID(EN_ProjectHandle ph, int indexRule, char *id) { + + EN_Project *pr = (EN_Project*)ph; + strcpy(id, ""); if (!pr->Openflag) - return (102); + return set_error(pr->error_handle, 102); if (indexRule < 1 || indexRule > pr->network.Nrules) - return (257); + return set_error(pr->error_handle, 257); strcpy(id, pr->rules.Rule[indexRule].label); - return (0); + return set_error(pr->error_handle, 0); } /*************************** END OF EPANET.C ***************************/ diff --git a/src/funcs.h b/src/funcs.h index c8dfb05..b961a82 100755 --- a/src/funcs.h +++ b/src/funcs.h @@ -36,10 +36,11 @@ int allocdata(EN_Project *pr); /* Allocates memory void freeTmplist(STmplist *); /* Frees items in linked list */ void freeFloatlist(SFloatlist *); /* Frees list of floats */ void freedata(EN_Project *pr); /* Frees allocated memory */ -int openfiles(EN_Project *pr, char *,char *,char *); /* Opens input & report files */ +int openfiles(EN_Project *pr, const char *, + const char *,const char *); /* Opens input & report files */ int openhydfile(EN_Project *pr); /* Opens hydraulics file */ int openoutfile(EN_Project *pr); /* Opens binary output file */ -int strcomp(char *, char *); /* Compares two strings */ +int strcomp(const char *, const char *); /* Compares two strings */ char* getTmpName(EN_Project *p, char* fname); /* Gets temporary file name */ double interp(int n, double x[], double y[], double xx); /* Interpolates a data curve */ @@ -51,7 +52,7 @@ int findvalve(EN_Network *n, int); /* Find valve index from node int findpump(EN_Network *n, int); /* Find pump index from node index */ // (AH) char *geterrmsg(int errcode, char *msg); /* Gets text of error message */ void errmsg(EN_Project *p, int); /* Reports program error */ -void writecon(char *); /* Writes text to console */ +void writecon(const char *); /* Writes text to console */ void writewin(void (*vp)(char *), char *); /* Passes text to calling app */ /* ------- INPUT1.C --------------------*/ @@ -126,6 +127,8 @@ void freerules(EN_Project *pr); /* Frees rule base memory int writeRuleinInp(EN_Project *pr, FILE *f, /* Writes rule to an INP file */ int RuleIdx); +int writeRuleinInp(EN_Project *pr, FILE *f, int RuleIdx); + /* ------------- REPORT.C --------------*/ int writereport(EN_Project *pr); /* Writes formatted report */ void writelogo(EN_Project *pr); /* Writes program logo */ @@ -158,28 +161,13 @@ void inithyd(EN_Project *pr, int initFlags); /* Re-sets initial condition int runhyd(EN_Project *pr, long *); /* Solves 1-period hydraulics */ int nexthyd(EN_Project *pr, long *); /* Moves to next time period */ void closehyd(EN_Project *pr); /* Closes hydraulics solver */ -int allocmatrix(EN_Project *pr); /* Allocates matrix coeffs. */ -void freematrix(EN_Project *pr); /* Frees matrix coeffs. */ -void initlinkflow(EN_Project *pr, int, char, - double); /* Initializes link flow */ -void setlinkflow(EN_Project *pr, int, double); /* Sets link flow via headloss*/ void setlinkstatus(EN_Project *pr, int, char, StatType *, double *); /* Sets link status */ void setlinksetting(EN_Project *pr, int, double, StatType *, double *); /* Sets pump/valve setting */ - -void demands(EN_Project *pr); /* Computes current demands */ -int controls(EN_Project *pr); /* Controls link settings */ -long timestep(EN_Project *pr); /* Computes new time step */ int tanktimestep(EN_Project *pr, long *); /* Time till tanks fill/drain */ -void controltimestep(EN_Project *pr, long *); /* Time till control action */ -void ruletimestep(EN_Project *pr, long *); /* Time till rule action */ - -void addenergy(EN_Project *pr, long); /* Accumulates energy usage */ void getenergy(EN_Project *pr, int, double *, double *); /* Computes link energy use */ - -void tanklevels(EN_Project *pr, long); /* Computes new tank levels */ double tankvolume(EN_Project *pr, int,double); /* Finds tank vol. from grade */ double tankgrade(EN_Project *pr, int,double); /* Finds tank grade from vol. */ @@ -188,8 +176,13 @@ int hydsolve(EN_Project *pr, int *,double *); /* Solves network equations /* ----------- HYDCOEFFS.C --------------*/ void resistcoeff(EN_Project *pr, int k); /* Finds pipe flow resistance */ -void hlosscoeff(EN_Project *pr, int k); /* Finds link head loss coeff */ +void headlosscoeffs(EN_Project *pr); // Finds link head loss coeffs. void matrixcoeffs(EN_Project *pr); /* Finds hyd. matrix coeffs. */ +double emitflowchange(EN_Project *pr, int i); /* Change in emitter outflow */ +double demandflowchange(EN_Project *pr, int i, // Change in demand outflow + double dp, double n); +void demandparams(EN_Project *pr, double *dp, // PDA function parameters + double *n); /* ----------- SMATRIX.C ---------------*/ int createsparse(EN_Project *pr); /* Creates sparse matrix */ diff --git a/src/hydcoeffs.c b/src/hydcoeffs.c index 9b830d9..00b0b82 100644 --- a/src/hydcoeffs.c +++ b/src/hydcoeffs.c @@ -29,15 +29,21 @@ const double AB = 3.28895476345e-03; // 5.74/(4000^.9) const double AC = -5.14214965799e-03; // AA*AB // External functions -//void resistcoeff(EN_Project *pr, int k); -//void hlosscoeff(EN_Project *pr, int k); -//void matrixcoeffs(EN_Project *pr); +//void resistcoeff(EN_Project *pr, int k); +//void headlosscoeffs(EN_Project *pr); +//void matrixcoeffs(EN_Project *pr); +//double emitflowchange(EN_Project *pr, int i); +//double demandflowchange(EN_Project *pr, int i, double dp, double n); +//void demandparams(EN_Project *pr, double *dp, double *n); // Local functions static void linkcoeffs(EN_Project *pr); static void nodecoeffs(EN_Project *pr); static void valvecoeffs(EN_Project *pr); static void emittercoeffs(EN_Project *pr); +static void demandcoeffs(EN_Project *pr); +static void demandheadloss(double d, double dfull, double dp, + double n, double *hloss, double *hgrad); static void pipecoeff(EN_Project *pr, int k); static void DWpipecoeff(EN_Project *pr, int k); @@ -55,7 +61,6 @@ static void psvcoeff(EN_Project *pr, int k, int n1, int n2); static void fcvcoeff(EN_Project *pr, int k, int n1, int n2); - void resistcoeff(EN_Project *pr, int k) /* **-------------------------------------------------------------------- @@ -83,13 +88,14 @@ void resistcoeff(EN_Project *pr, int k) switch (hyd->Formflag) { case HW: - link->R = 4.727*L / pow(e, hyd->Hexp) / pow(d, 4.871); + link->R = 4.727 * L / pow(e, hyd->Hexp) / pow(d, 4.871); break; case DW: - link->R = L / 2.0 / 32.2 / d / SQR(PI*SQR(d) / 4.0); + link->R = L / 2.0 / 32.2 / d / SQR(PI * SQR(d) / 4.0); break; case CM: - link->R = SQR(4.0*e / (1.49*PI*d*d)) * pow((d / 4.0), -1.333)*L; + link->R = SQR(4.0 * e / (1.49 * PI * SQR(d))) * + pow((d / 4.0), -1.333) * L; } break; @@ -106,45 +112,46 @@ void resistcoeff(EN_Project *pr, int k) } -void hlosscoeff(EN_Project *pr, int k) +void headlosscoeffs(EN_Project *pr) /* **-------------------------------------------------------------- -** Input: k = link index +** Input: none ** Output: none -** Purpose: computes P and Y coefficients for a link +** Purpose: computes coefficients P (1 / head loss gradient) +** and Y (head loss / gradient) for all links. **-------------------------------------------------------------- */ { + int k; EN_Network *net = &pr->network; hydraulics_t *hyd = &pr->hydraulics; - solver_t *sol = &hyd->solver; - Slink *link = &net->Link[k]; - switch (link->Type) + for (k = 1; k <= net->Nlinks; k++) { - case EN_CVPIPE: - case EN_PIPE: - pipecoeff(pr, k); - break; - case EN_PUMP: - pumpcoeff(pr, k); - break; - case EN_PBV: - pbvcoeff(pr, k); - break; - case EN_TCV: - tcvcoeff(pr, k); - break; - case EN_GPV: - gpvcoeff(pr, k); - break; - case EN_FCV: - case EN_PRV: - case EN_PSV: - if (hyd->LinkSetting[k] == MISSING) { - valvecoeff(pr, k); + switch (net->Link[k].Type) + { + case EN_CVPIPE: + case EN_PIPE: + pipecoeff(pr, k); + break; + case EN_PUMP: + pumpcoeff(pr, k); + break; + case EN_PBV: + pbvcoeff(pr, k); + break; + case EN_TCV: + tcvcoeff(pr, k); + break; + case EN_GPV: + gpvcoeff(pr, k); + break; + case EN_FCV: + case EN_PRV: + case EN_PSV: + if (hyd->LinkSetting[k] == MISSING) valvecoeff(pr, k); + else hyd->solver.P[k] = 0.0; } - else sol->P[k] = 0.0; } } @@ -162,13 +169,23 @@ void matrixcoeffs(EN_Project *pr) solver_t *sol = &hyd->solver; EN_Network *net = &pr->network; + // Reset values of all diagonal coeffs. (Aii), off-diagonal + // coeffs. (Aij), r.h.s. coeffs. (F) and node flow balance (X_tmp) memset(sol->Aii, 0, (net->Nnodes + 1) * sizeof(double)); memset(sol->Aij, 0, (hyd->Ncoeffs + 1) * sizeof(double)); memset(sol->F, 0, (net->Nnodes + 1) * sizeof(double)); memset(hyd->X_tmp, 0, (net->Nnodes + 1) * sizeof(double)); + + // Compute matrix coeffs. from links, emitters, and nodal demands linkcoeffs(pr); emittercoeffs(pr); + demandcoeffs(pr); + + // Update nodal flow balances with demands and add onto r.h.s. coeffs. nodecoeffs(pr); + + // Finally, find coeffs. for PRV/PSV/FCV control valves whose + // status is not fixed to OPEN/CLOSED valvecoeffs(pr); } @@ -189,7 +206,7 @@ void linkcoeffs(EN_Project *pr) solver_t *sol = &hyd->solver; Slink *link; - // Examine each link of network */ + // Examine each link of network for (k = 1; k <= net->Nlinks; k++) { if (sol->P[k] == 0.0) continue; @@ -197,7 +214,7 @@ void linkcoeffs(EN_Project *pr) n1 = link->N1; // Start node of link n2 = link->N2; // End node of link - // Update net nodal inflows (X), solution matrix (A) and RHS array (F) + // Update net nodal inflows (X_tmp), solution matrix (A) and RHS array (F) // (Use covention that flow out of node is (-), flow into node is (+)) hyd->X_tmp[n1] -= hyd->LinkFlows[k]; hyd->X_tmp[n2] += hyd->LinkFlows[k]; @@ -212,21 +229,18 @@ void linkcoeffs(EN_Project *pr) sol->F[sol->Row[n1]] += sol->Y[k]; // RHS coeff. } - // Node n1 is a tank - else { - sol->F[sol->Row[n2]] += (sol->P[k] * hyd->NodeHead[n1]); - } + // Node n1 is a tank/reservoir + else sol->F[sol->Row[n2]] += (sol->P[k] * hyd->NodeHead[n1]); // Node n2 is junction - if (n2 <= net->Njuncs) { + if (n2 <= net->Njuncs) + { sol->Aii[sol->Row[n2]] += sol->P[k]; // Diagonal coeff. sol->F[sol->Row[n2]] -= sol->Y[k]; // RHS coeff. } - // Node n2 is a tank - else { - sol->F[sol->Row[n1]] += (sol->P[k] * hyd->NodeHead[n2]); - } + // Node n2 is a tank/reservoir + else sol->F[sol->Row[n1]] += (sol->P[k] * hyd->NodeHead[n2]); } } @@ -236,7 +250,7 @@ void nodecoeffs(EN_Project *pr) **---------------------------------------------------------------- ** Input: none ** Output: none -** Purpose: completes calculation of nodal flow imbalance (X) +** Purpose: completes calculation of nodal flow imbalance (X_tmp) ** & flow correction (F) arrays **---------------------------------------------------------------- */ @@ -250,7 +264,7 @@ void nodecoeffs(EN_Project *pr) // flow imbalance & add imbalance to RHS array F. for (i = 1; i <= net->Njuncs; i++) { - hyd->X_tmp[i] -= hyd->NodeDemand[i]; + hyd->X_tmp[i] -= hyd->DemandFlows[i]; sol->F[sol->Row[i]] += hyd->X_tmp[i]; } } @@ -276,15 +290,20 @@ void valvecoeffs(EN_Project *pr) // Examine each valve for (i = 1; i <= net->Nvalves; i++) { + // Find valve's link index valve = &net->Valve[i]; - k = valve->Link; // Link index of valve + k = valve->Link; + + // Coeffs. for fixed status valves have already been computed + if (hyd->LinkSetting[k] == MISSING) continue; + + // Start & end nodes of valve's link link = &net->Link[k]; - if (hyd->LinkSetting[k] == MISSING) { - continue; // Valve status fixed - } - n1 = link->N1; // Start & end nodes + n1 = link->N1; n2 = link->N2; - switch (link->Type) // Call valve-specific function + + // Call valve-specific function + switch (link->Type) { case EN_PRV: prvcoeff(pr, k, n1, n2); @@ -330,17 +349,17 @@ void emittercoeffs(EN_Project *pr) for (i = 1; i <= net->Njuncs; i++) { node = &net->Node[i]; - if (node->Ke == 0.0) { - continue; - } + if (node->Ke == 0.0) continue; ke = MAX(CSMALL, node->Ke); // emitter coeff. q = hyd->EmitterFlows[i]; // emitter flow z = ke * pow(ABS(q), hyd->Qexp); // emitter head loss p = hyd->Qexp * z / ABS(q); // head loss gradient - if (p < hyd->RQtol) { + if (p < hyd->RQtol) + { p = 1.0 / hyd->RQtol; } - else { + else + { p = 1.0 / p; // inverse head loss gradient } y = SGN(q)*z*p; // head loss / gradient @@ -351,6 +370,173 @@ void emittercoeffs(EN_Project *pr) } +double emitflowchange(EN_Project *pr, int i) +/* +**-------------------------------------------------------------- +** Input: i = node index +** Output: returns change in flow at an emitter node +** Purpose: computes flow change at an emitter node +**-------------------------------------------------------------- +*/ +{ + double ke, p; + hydraulics_t *hyd = &pr->hydraulics; + Snode *node = &pr->network.Node[i]; + + ke = MAX(CSMALL, node->Ke); + p = hyd->Qexp * ke * pow(ABS(hyd->EmitterFlows[i]), (hyd->Qexp - 1.0)); + if (p < hyd->RQtol) p = 1 / hyd->RQtol; + else p = 1.0 / p; + return(hyd->EmitterFlows[i] / hyd->Qexp - p * (hyd->NodeHead[i] - node->El)); +} + + +void demandparams(EN_Project *pr, double *dp, double *n) +/* +**-------------------------------------------------------------- +** Input: none +** Output: dp = pressure range over which demands can vary +** n = exponent in head loss v. demand function +** Purpose: retrieves parameters that define a pressure dependent +** demand function. +**-------------------------------------------------------------- +*/ +{ + hydraulics_t *hyd = &pr->hydraulics; + + // If required pressure equals minimum pressure, use a linear demand + // curve with a 0.01 PSI pressure range to approximate an all or + // nothing demand solution + if (hyd->Preq == hyd->Pmin) + { + *dp = 0.01 / PSIperFT; + *n = 1.0; + } + + // Otherwise use the user-supplied demand curve parameters + else + { + *dp = hyd->Preq - hyd->Pmin; + *n = 1.0 / hyd->Pexp; + } +} + + +void demandcoeffs(EN_Project *pr) +/* +**-------------------------------------------------------------- +** Input: none +** Output: none +** Purpose: computes matrix coeffs. for pressure dependent demands +** +** Note: Pressure dependent demands are modelled like emitters +** with Hloss = Pserv * (D / Dfull)^(1/Pexp) +** where D (actual demand) is zero for negative pressure +** and is Dfull above pressure Pserv. +**-------------------------------------------------------------- +*/ +{ + int i, row; + double dp, // pressure range over which demand can vary (ft) + n, // exponent in head loss v. demand function + hloss, // head loss in supplying demand (ft) + hgrad; // gradient of demand head loss (ft/cfs) + + hydraulics_t *hyd = &pr->hydraulics; + solver_t *sol = &hyd->solver; + EN_Network *net = &pr->network; + + // Get demand function parameters + if (hyd->DemandModel == DDA) return; + demandparams(pr, &dp, &n); + + // Examine each junction node + for (i = 1; i <= net->Njuncs; i++) + { + // Skip junctions with non-positive demands + if (hyd->NodeDemand[i] <= 0.0) continue; + + // Find head loss for demand outflow at node's elevation + demandheadloss(hyd->DemandFlows[i], hyd->NodeDemand[i], dp, n, + &hloss, &hgrad); + + // Update row of solution matrix A & its r.h.s. F + row = sol->Row[i]; + sol->Aii[row] += 1.0 / hgrad; + sol->F[row] += (hloss + net->Node[i].El + hyd->Pmin) / hgrad; + } +} + + +double demandflowchange(EN_Project *pr, int i, double dp, double n) +/* +**-------------------------------------------------------------- +** Input: i = node index +** dp = pressure range fro demand funtion (ft) +** n = exponent in head v. demand function +** Output: returns change in pressure dependent demand flow +** Purpose: computes change in outflow at at a node subject to +** pressure dependent demands +**-------------------------------------------------------------- +*/ +{ + double hloss, hgrad; + hydraulics_t *hyd = &pr->hydraulics; + + demandheadloss(hyd->DemandFlows[i], hyd->NodeDemand[i], dp, n, &hloss, &hgrad); + return (hloss - hyd->NodeHead[i] + pr->network.Node[i].El + hyd->Pmin) / hgrad; +} + + +void demandheadloss(double d, double dfull, double dp, double n, + double *hloss, double *hgrad) + /* + **-------------------------------------------------------------- + ** Input: d = actual junction demand (cfs) + ** dfull = full junction demand required (cfs) + ** dp = pressure range for demand function (ft) + ** n = exponent in head v. demand function + ** Output: hloss = head loss delivering demand d (ft) + ** hgrad = gradient of head loss (ft/cfs) + ** Purpose: computes head loss and its gradient for delivering + ** a pressure dependent demand flow. + **-------------------------------------------------------------- + */ +{ + const double RB = 1.0e9; + const double EPS = 0.001; + double r = d / dfull; + + // Use upper barrier function for excess demand above full value + if (r > 1.0) + { + *hgrad = RB; + *hloss = dp + RB * (d - dfull); + } + + // Use lower barrier function for negative demand + else if (r < 0) + { + *hgrad = RB; + *hloss = RB * d; + } + + // Use linear head loss function for near zero demand + else if (r < EPS) + { + *hgrad = dp * pow(EPS, n - 1.0) / dfull; + *hloss = (*hgrad) * d; + } + + // Otherwise use power head loss function + else + { + *hgrad = n * dp * pow(r, n - 1.0) / dfull; + *hloss = (*hgrad) * d / n; + } +} + + void pipecoeff(EN_Project *pr, int k) /* **-------------------------------------------------------------- @@ -382,8 +568,9 @@ void pipecoeff(EN_Project *pr, int k) return; } - // ... head loss formula is Darcy-Weisbach - if (hyd->Formflag == DW) { + // Use custom function for Darcy-Weisbach formula + if (hyd->Formflag == DW) + { DWpipecoeff(pr, k); return; } @@ -550,7 +737,8 @@ void pumpcoeff(EN_Project *pr, int k) Spump *pump; // Use high resistance pipe if pump closed or cannot deliver head - if (hyd->LinkStatus[k] <= CLOSED || setting == 0.0) { + if (hyd->LinkStatus[k] <= CLOSED || setting == 0.0) + { sol->P[k] = 1.0 / CBIG; sol->Y[k] = hyd->LinkFlows[k]; return; @@ -580,9 +768,7 @@ void pumpcoeff(EN_Project *pr, int k) h0 = SQR(setting) * pump->H0; n = pump->N; r = pump->R * pow(setting, 2.0 - n); - if (n != 1.0) { - r = n * r * pow(q, n - 1.0); - } + if (n != 1.0) r = n * r * pow(q, n - 1.0); // Compute inverse headloss gradient (P) and flow correction factor (Y) sol->P[k] = 1.0 / MAX(r, hyd->RQtol); @@ -615,15 +801,9 @@ void curvecoeff(EN_Project *pr, int i, double q, double *h0, double *r) // Find linear segment of curve that brackets flow q k2 = 0; - while (k2 < npts && x[k2] < q) - k2++; - - if (k2 == 0) - k2++; - - else if (k2 == npts) - k2--; - + while (k2 < npts && x[k2] < q) k2++; + if (k2 == 0) k2++; + else if (k2 == npts) k2--; k1 = k2 - 1; // Compute slope and intercept of this segment @@ -653,13 +833,12 @@ void gpvcoeff(EN_Project *pr, int k) solver_t *sol = &hyd->solver; // Treat as a pipe if valve closed - if (hyd->LinkStatus[k] == CLOSED) { - valvecoeff(pr, k); - } + if (hyd->LinkStatus[k] == CLOSED) valvecoeff(pr, k); // Otherwise utilize headloss curve // whose index is stored in K - else { + else + { // Find slope & intercept of headloss curve. q = ABS(hyd->LinkFlows[k]); q = MAX(q, TINY); @@ -687,18 +866,22 @@ void pbvcoeff(EN_Project *pr, int k) Slink *link = &pr->network.Link[k]; // If valve fixed OPEN or CLOSED then treat as a pipe - if (hyd->LinkSetting[k] == MISSING || hyd->LinkSetting[k] == 0.0) { + if (hyd->LinkSetting[k] == MISSING || hyd->LinkSetting[k] == 0.0) + { valvecoeff(pr, k); } // If valve is active - else { + else + { // Treat as a pipe if minor loss > valve setting - if (link->Km * SQR(hyd->LinkFlows[k]) > hyd->LinkSetting[k]) { + if (link->Km * SQR(hyd->LinkFlows[k]) > hyd->LinkSetting[k]) + { valvecoeff(pr, k); } // Otherwise force headloss across valve to be equal to setting - else { + else + { sol->P[k] = CBIG; sol->Y[k] = hyd->LinkSetting[k] * CBIG; } @@ -723,7 +906,8 @@ void tcvcoeff(EN_Project *pr, int k) km = link->Km; // If valve not fixed OPEN or CLOSED, compute its loss coeff. - if (hyd->LinkSetting[k] != MISSING) { + if (hyd->LinkSetting[k] != MISSING) + { link->Km = 0.02517 * hyd->LinkSetting[k] / (SQR(link->Diam)*SQR(link->Diam)); } @@ -768,7 +952,8 @@ void prvcoeff(EN_Project *pr, int k, int n1, int n2) sol->Y[k] = hyd->LinkFlows[k] + hyd->X_tmp[n2]; // Force flow balance sol->F[j] += (hset * CBIG); // Force head = hset sol->Aii[j] += CBIG; // at downstream node - if (hyd->X_tmp[n2] < 0.0) { + if (hyd->X_tmp[n2] < 0.0) + { sol->F[i] += hyd->X_tmp[n2]; } return; @@ -818,7 +1003,8 @@ void psvcoeff(EN_Project *pr, int k, int n1, int n2) sol->Y[k] = hyd->LinkFlows[k] - hyd->X_tmp[n1]; // Force flow balance sol->F[i] += (hset * CBIG); // Force head = hset sol->Aii[i] += CBIG; // at upstream node - if (hyd->X_tmp[n1] > 0.0) { + if (hyd->X_tmp[n1] > 0.0) + { sol->F[j] += hyd->X_tmp[n1]; } return; @@ -919,9 +1105,7 @@ void valvecoeff(EN_Project *pr, int k) if (link->Km > 0.0) { p = 2.0 * link->Km * fabs(flow); - if (p < hyd->RQtol) { - p = hyd->RQtol; - } + if (p < hyd->RQtol) p = hyd->RQtol; sol->P[k] = 1.0 / p; sol->Y[k] = flow / 2.0; } diff --git a/src/hydraul.c b/src/hydraul.c index 545ed37..3984ec0 100755 --- a/src/hydraul.c +++ b/src/hydraul.c @@ -60,6 +60,20 @@ AUTHOR: L. Rossman #define QZERO 1.e-6 /* Equivalent to zero flow */ +// Local functions +int allocmatrix(EN_Project *pr); +void freematrix(EN_Project *pr); +void initlinkflow(EN_Project *pr, int, char, double); +void setlinkflow(EN_Project *pr, int, double); +void demands(EN_Project *pr); +int controls(EN_Project *pr); +long timestep(EN_Project *pr); +void controltimestep(EN_Project *pr, long *); +void ruletimestep(EN_Project *pr, long *); +void addenergy(EN_Project *pr, long); +void tanklevels(EN_Project *pr, long); + + int openhyd(EN_Project *pr) /* *-------------------------------------------------------------- @@ -194,15 +208,12 @@ int runhyd(EN_Project *pr, long *t) /* Solve network hydraulic equations */ errcode = hydsolve(pr,&iter,&relerr); + if (!errcode) { /* Report new status & save results */ if (rep->Statflag) { writehydstat(pr,iter,relerr); } - - /* solution info */ - hyd->relativeError = relerr; - hyd->iterations = iter; /*** Updated 3/1/01 ***/ /* If system unbalanced and no extra trials */ @@ -316,6 +327,7 @@ int allocmatrix(EN_Project *pr) s->Aii = (double *) calloc(net->Nnodes+1,sizeof(double)); s->Aij = (double *) calloc(hyd->Ncoeffs+1,sizeof(double)); s->F = (double *) calloc(net->Nnodes+1,sizeof(double)); + hyd->DemandFlows = (double *)calloc(net->Nnodes + 1, sizeof(double)); hyd->EmitterFlows = (double *) calloc(net->Nnodes+1,sizeof(double)); s->P = (double *) calloc(net->Nlinks+1,sizeof(double)); s->Y = (double *) calloc(net->Nlinks+1,sizeof(double)); @@ -324,6 +336,7 @@ int allocmatrix(EN_Project *pr) ERRCODE(MEMCHECK(s->Aii)); ERRCODE(MEMCHECK(s->Aij)); ERRCODE(MEMCHECK(s->F)); + ERRCODE(MEMCHECK(hyd->DemandFlows)); ERRCODE(MEMCHECK(hyd->EmitterFlows)); ERRCODE(MEMCHECK(s->P)); ERRCODE(MEMCHECK(s->Y)); @@ -348,6 +361,7 @@ void freematrix(EN_Project *pr) free(s->Aii); free(s->Aij); free(s->F); + free(hyd->DemandFlows); free(hyd->EmitterFlows); free(s->P); free(s->Y); @@ -551,7 +565,6 @@ void setlinksetting(EN_Project *pr, int index, double value, StatType *s, doubl } - void demands(EN_Project *pr) /* **-------------------------------------------------------------------- @@ -591,6 +604,9 @@ void demands(EN_Project *pr) sum += djunc; } hyd->NodeDemand[i] = sum; + + // Initialize pressure dependent demand + hyd->DemandFlows[i] = sum; } /* Update head at fixed grade nodes with time patterns. */ diff --git a/src/hydsolver.c b/src/hydsolver.c index beeff1d..7e6f0d6 100644 --- a/src/hydsolver.c +++ b/src/hydsolver.c @@ -22,7 +22,7 @@ The solver implements Todini's Global Gradient Algorithm. #include "types.h" #include "funcs.h" -// Error in network being hydraulically balanced +// Hydraulic balance error for network being analyzed typedef struct { double maxheaderror; double maxflowerror; @@ -33,22 +33,25 @@ typedef struct { } Hydbalance; // External functions -//int hydsolve(EN_Project *pr, int *iter, double *relerr); +//int hydsolve(EN_Project *pr, int *iter, double *relerr); +//void headlosscoeffs(EN_Project *pr); +//void matrixcoeffs(EN_Project *pr); + +extern int valvestatus(EN_Project *pr); //(see HYDSTATUS.C) +extern int linkstatus(EN_Project *pr); //(see HYDSTATUS.C) // Local functions static int badvalve(EN_Project *pr, int); -static int valvestatus(EN_Project *pr); -static int linkstatus(EN_Project *pr); -static StatType cvstatus(EN_Project *pr, StatType, double, double); -static StatType pumpstatus(EN_Project *pr, int, double); -static StatType prvstatus(EN_Project *pr, int, StatType, double, double, double); -static StatType psvstatus(EN_Project *pr, int, StatType, double, double, double); -static StatType fcvstatus(EN_Project *pr, int, StatType, double, double); -static void tankstatus(EN_Project *pr, int, int, int); static int pswitch(EN_Project *pr); static double newflows(EN_Project *pr, Hydbalance *hbal); -static double emitflowchange(EN_Project *pr, int i); +static void newlinkflows(EN_Project *pr, Hydbalance *hbal, double *qsum, + double *dqsum); +static void newemitterflows(EN_Project *pr, Hydbalance *hbal, double *qsum, + double *dqsum); +static void newdemandflows(EN_Project *pr, Hydbalance *hbal, double *qsum, + double *dqsum); + static void checkhydbalance(EN_Project *pr, Hydbalance *hbal); static int hasconverged(EN_Project *pr, double *relerr, Hydbalance *hbal); static void reporthydbal(EN_Project *pr, Hydbalance *hbal); @@ -75,122 +78,117 @@ int hydsolve(EN_Project *pr, int *iter, double *relerr) ** is > 0 then future computed flow changes are only 60% of ** their full value. A complete status check on all links ** is made when convergence is achieved. If convergence is -** not achieved in hyd->MaxIter trials and hyd->ExtraIter > 0 then -** another hyd->ExtraIter trials are made with no status changes +** not achieved in MaxIter trials and ExtraIter > 0 then +** another ExtraIter trials are made with no status changes ** made to any links and a warning message is generated. ** ** This procedure calls linsolve() which appears in SMATRIX.C. **------------------------------------------------------------------- */ { - int i; /* Node index */ - int errcode = 0; /* Node causing solution error */ - int nextcheck; /* Next status check trial */ - int maxtrials; /* Max. trials for convergence */ - double newerr; /* New convergence error */ - int valveChange; /* Valve status change flag */ - int statChange; /* Non-valve status change flag */ - Hydbalance hydbal; /* Hydraulic balance errors */ + int i; // Node index + int errcode = 0; // Node causing solution error + int nextcheck; // Next status check trial + int maxtrials; // Max. trials for convergence + double newerr; // New convergence error + int valveChange; // Valve status change flag + int statChange; // Non-valve status change flag + Hydbalance hydbal; // Hydraulic balance errors EN_Network *net = &pr->network; hydraulics_t *hyd = &pr->hydraulics; solver_t *sol = &hyd->solver; report_options_t *rep = &pr->report; - /* Initialize status checking & relaxation factor */ + // Initialize status checking & relaxation factor nextcheck = hyd->CheckFreq; hyd->RelaxFactor = 1.0; - /* Repeat iterations until convergence or trial limit is exceeded. */ - /* (hyd->ExtraIter used to increase trials in case of status cycling.) */ - if (pr->report.Statflag == FULL) { + // Repeat iterations until convergence or trial limit is exceeded. + // (ExtraIter used to increase trials in case of status cycling.) + if (pr->report.Statflag == FULL) + { writerelerr(pr, 0, 0); } maxtrials = hyd->MaxIter; - if (hyd->ExtraIter > 0) { + if (hyd->ExtraIter > 0) + { maxtrials += hyd->ExtraIter; } *iter = 1; - while (*iter <= maxtrials) { + while (*iter <= maxtrials) + { /* ** Compute coefficient matrices A & F and solve A*H = F ** where H = heads, A = Jacobian coeffs. derived from ** head loss gradients, & F = flow correction terms. ** Solution for H is returned in F from call to linsolve(). */ - for (i = 1; i <= net->Nlinks; i++) { - hlosscoeff(pr, i); - } + headlosscoeffs(pr); matrixcoeffs(pr); errcode = linsolve(pr, net->Njuncs); - /* Take action depending on error code */ - if (errcode < 0) { - break; /* Memory allocation problem */ - } - if (errcode > 0) { /* Ill-conditioning problem */ - /* If control valve causing problem, fix its status & continue, */ - /* otherwise end the iterations with no solution. */ - if (badvalve(pr, sol->Order[errcode])) { - continue; - } + // Quit if memory allocation error + if (errcode < 0) break; + + // Matrix ill-conditioning problem - if control valve causing problem, + // fix its status & continue, otherwise quit with no solution. + if (errcode > 0) + { + if (badvalve(pr, sol->Order[errcode])) continue; else break; } - /* Update current solution. */ - /* (Row[i] = row of solution matrix corresponding to node i). */ - for (i = 1; i <= net->Njuncs; i++) { - hyd->NodeHead[i] = sol->F[sol->Row[i]]; /* Update heads */ + // Update current solution. + // (Row[i] = row of solution matrix corresponding to node i) + for (i = 1; i <= net->Njuncs; i++) + { + hyd->NodeHead[i] = sol->F[sol->Row[i]]; // Update heads } - newerr = newflows(pr, &hydbal); /* Update flows */ + newerr = newflows(pr, &hydbal); // Update flows *relerr = newerr; - /* Write convergence error to status report if called for */ - if (rep->Statflag == FULL) { + // Write convergence error to status report if called for + if (rep->Statflag == FULL) + { writerelerr(pr, *iter, *relerr); } - /* Apply solution damping & check for change in valve status */ + // Apply solution damping & check for change in valve status hyd->RelaxFactor = 1.0; valveChange = FALSE; - if (hyd->DampLimit > 0.0) { - if (*relerr <= hyd->DampLimit) { + if (hyd->DampLimit > 0.0) + { + if (*relerr <= hyd->DampLimit) + { hyd->RelaxFactor = 0.6; valveChange = valvestatus(pr); } } - else { + else + { valveChange = valvestatus(pr); } - /* Check for convergence */ - if (hasconverged(pr, relerr, &hydbal)) { - /* We have convergence. Quit if we are into extra iterations. */ - if (*iter > hyd->MaxIter) { - break; - } + // Check for convergence + if (hasconverged(pr, relerr, &hydbal)) + { + // We have convergence - quit if we are into extra iterations + if (*iter > hyd->MaxIter) break; - /* Quit if no status changes occur. */ + // Quit if no status changes occur statChange = FALSE; - if (valveChange) { - statChange = TRUE; - } - if (linkstatus(pr)) { - statChange = TRUE; - } - if (pswitch(pr)) { - statChange = TRUE; - } - if (!statChange) { - break; - } + if (valveChange) statChange = TRUE; + if (linkstatus(pr)) statChange = TRUE; + if (pswitch(pr)) statChange = TRUE; + if (!statChange) break; - /* We have a status change so continue the iterations */ + // We have a status change so continue the iterations nextcheck = *iter + hyd->CheckFreq; } - /* No convergence yet. See if its time for a periodic status */ - /* check on pumps, CV's, and pipes connected to tanks. */ + // No convergence yet - see if its time for a periodic status + // check on pumps, CV's, and pipes connected to tank else if (*iter <= hyd->MaxCheck && *iter == nextcheck) { linkstatus(pr); @@ -199,20 +197,28 @@ int hydsolve(EN_Project *pr, int *iter, double *relerr) (*iter)++; } - /* Iterations ended. Report any errors. */ - if (errcode < 0) errcode = 101; /* Memory allocation error */ + // Iterations ended - report any errors. + if (errcode < 0) errcode = 101; // Memory allocation error else if (errcode > 0) { - writehyderr(pr, sol->Order[errcode]); /* Ill-conditioned eqns. error */ + writehyderr(pr, sol->Order[errcode]); // Ill-conditioned matrix error errcode = 110; } - /* Add any emitter flows to junction demands */ - for (i = 1; i <= net->Njuncs; i++) { - hyd->NodeDemand[i] += hyd->EmitterFlows[i]; + // Replace junction demands with total outflow values + for (i = 1; i <= net->Njuncs; i++) + { + hyd->NodeDemand[i] = hyd->DemandFlows[i] + hyd->EmitterFlows[i]; } + + // Save convergence info + hyd->RelativeError = *relerr; + hyd->MaxHeadError = hydbal.maxheaderror; + hyd->MaxFlowChange = hydbal.maxflowchange; + hyd->Iterations = *iter; + return(errcode); -} /* End of netsolve */ +} int badvalve(EN_Project *pr, int n) @@ -235,513 +241,44 @@ int badvalve(EN_Project *pr, int n) report_options_t *rep = &pr->report; time_options_t *time = &pr->time_options; Slink *link; + EN_LinkType t; - for (i = 1; i <= net->Nvalves; i++) { + for (i = 1; i <= net->Nvalves; i++) + { k = net->Valve[i].Link; link = &net->Link[k]; n1 = link->N1; n2 = link->N2; - if (n == n1 || n == n2) { - EN_LinkType t = link->Type; - if (t == EN_PRV || t == EN_PSV || t == EN_FCV) { - if (hyd->LinkStatus[k] == ACTIVE) { - if (rep->Statflag == FULL) { + if (n == n1 || n == n2) + { + t = link->Type; + if (t == EN_PRV || t == EN_PSV || t == EN_FCV) + { + if (hyd->LinkStatus[k] == ACTIVE) + { + if (rep->Statflag == FULL) + { sprintf(pr->Msg, FMT61, clocktime(rep->Atime, time->Htime), link->ID); writeline(pr, pr->Msg); } - if (link->Type == EN_FCV) { + if (link->Type == EN_FCV) + { hyd->LinkStatus[k] = XFCV; } - else { + else + { hyd->LinkStatus[k] = XPRESSURE; } - return(1); + return 1; } } - return(0); + return 0; } } - return(0); + return 0; } -int valvestatus(EN_Project *pr) -/* -**----------------------------------------------------------------- -** Input: none -** Output: returns 1 if any pressure or flow control valve -** changes status, 0 otherwise -** Purpose: updates status for PRVs & PSVs whose status -** is not fixed to OPEN/CLOSED -**----------------------------------------------------------------- -*/ -{ - int change = FALSE, /* Status change flag */ - i, k, /* Valve & link indexes */ - n1, n2; /* Start & end nodes */ - StatType status; /* Valve status settings */ - double hset; /* Valve head setting */ - Slink *link; - - EN_Network *net = &pr->network; - hydraulics_t *hyd = &pr->hydraulics; - report_options_t *rep = &pr->report; - - for (i = 1; i <= net->Nvalves; i++) /* Examine each valve */ - { - k = net->Valve[i].Link; /* Link index of valve */ - link = &net->Link[k]; - if (hyd->LinkSetting[k] == MISSING) { - continue; /* Valve status fixed */ - } - n1 = link->N1; /* Start & end nodes */ - n2 = link->N2; - status = hyd->LinkStatus[k]; /* Save current status */ - -// if (s != CLOSED /* No change if flow is */ -// && ABS(hyd->LinkFlows[k]) < hyd->Qtol) continue; /* negligible. */ - - switch (link->Type) /* Evaluate new status: */ - { - case EN_PRV: - hset = net->Node[n2].El + hyd->LinkSetting[k]; - hyd->LinkStatus[k] = prvstatus(pr, k, status, hset, hyd->NodeHead[n1], hyd->NodeHead[n2]); - break; - case EN_PSV: - hset = net->Node[n1].El + hyd->LinkSetting[k]; - hyd->LinkStatus[k] = psvstatus(pr, k, status, hset, hyd->NodeHead[n1], hyd->NodeHead[n2]); - break; - -//// FCV status checks moved back into the linkstatus() function //// -// case FCV: S[k] = fcvstatus(k,s,hyd->NodeHead[n1],hyd->NodeHead[n2]); -// break; - - default: - continue; - } - - /*** Updated 9/7/00 ***/ - /* Do not reset flow in valve if its status changes. */ - /* This strategy improves convergence. */ - - /* Check for status change */ - if (status != hyd->LinkStatus[k]) - { - if (rep->Statflag == FULL) { - writestatchange(pr, k, status, hyd->LinkStatus[k]); - } - change = TRUE; - } - } - return(change); -} /* End of valvestatus() */ - - -/*** Updated 9/7/00 ***/ -int linkstatus(EN_Project *pr) -/* -**-------------------------------------------------------------- -** Input: none -** Output: returns 1 if any link changes status, 0 otherwise -** Purpose: determines new status for pumps, CVs, FCVs & pipes -** to tanks. -**-------------------------------------------------------------- -*/ -{ - int change = FALSE, /* Status change flag */ - k, /* Link index */ - n1, /* Start node index */ - n2; /* End node index */ - double dh; /* Head difference */ - StatType status; /* Current status */ - - EN_Network *net = &pr->network; - hydraulics_t *hyd = &pr->hydraulics; - report_options_t *rep = &pr->report; - Slink *link; - - /* Examine each Slink */ - for (k = 1; k <= net->Nlinks; k++) - { - link = &net->Link[k]; - n1 = link->N1; - n2 = link->N2; - dh = hyd->NodeHead[n1] - hyd->NodeHead[n2]; - - /* Re-open temporarily closed links (status = XHEAD or TEMPCLOSED) */ - status = hyd->LinkStatus[k]; - if (status == XHEAD || status == TEMPCLOSED) { - hyd->LinkStatus[k] = OPEN; - } - - /* Check for status changes in CVs and pumps */ - if (link->Type == EN_CVPIPE) { - hyd->LinkStatus[k] = cvstatus(pr, hyd->LinkStatus[k], dh, hyd->LinkFlows[k]); - } - if (link->Type == EN_PUMP && hyd->LinkStatus[k] >= OPEN && hyd->LinkSetting[k] > 0.0) { - hyd->LinkStatus[k] = pumpstatus(pr, k, -dh); - } - - /* Check for status changes in non-fixed FCVs */ - if (link->Type == EN_FCV && hyd->LinkSetting[k] != MISSING) { - hyd->LinkStatus[k] = fcvstatus(pr, k, status, hyd->NodeHead[n1], hyd->NodeHead[n2]); // - } - - /* Check for flow into (out of) full (empty) tanks */ - if (n1 > net->Njuncs || n2 > net->Njuncs) { - tankstatus(pr, k, n1, n2); - } - - /* Note change in link status; do not revise link flow */ - if (status != hyd->LinkStatus[k]) { - change = TRUE; - if (rep->Statflag == FULL) { - writestatchange(pr, k, status, hyd->LinkStatus[k]); - } - - //if (S[k] <= CLOSED) hyd->LinkFlows[k] = QZERO; - //else setlinkflow(k, dh); - } - } - return(change); -} /* End of linkstatus */ - - -StatType cvstatus(EN_Project *pr, StatType s, double dh, double q) -/* -**-------------------------------------------------- -** Input: s = current status -** dh = headloss -** q = flow -** Output: returns new link status -** Purpose: updates status of a check valve. -**-------------------------------------------------- -*/ -{ - hydraulics_t *hyd = &pr->hydraulics; - - /* Prevent reverse flow through CVs */ - if (ABS(dh) > hyd->Htol) - { - if (dh < -hyd->Htol) { - return(CLOSED); - } - else if (q < -hyd->Qtol) { - return(CLOSED); - } - else { - return(OPEN); - } - } - else - { - if (q < -hyd->Qtol) { - return(CLOSED); - } - else { - return(s); - } - } -} - - -StatType pumpstatus(EN_Project *pr, int k, double dh) -/* -**-------------------------------------------------- -** Input: k = link index -** dh = head gain -** Output: returns new pump status -** Purpose: updates status of an open pump. -**-------------------------------------------------- -*/ -{ - int p; - double hmax; - hydraulics_t *hyd = &pr->hydraulics; - EN_Network *net = &pr->network; - - /* Prevent reverse flow through pump */ - p = findpump(net, k); - if (net->Pump[p].Ptype == CONST_HP) { - hmax = BIG; - } - else { - hmax = SQR(hyd->LinkSetting[k]) * net->Pump[p].Hmax; - } - if (dh > hmax + hyd->Htol) { - return(XHEAD); - } - - /*** Flow higher than pump curve no longer results in a status change ***/ - /* Check if pump cannot deliver flow */ - //if (hyd->LinkFlows[k] > K[k]*Pump[p].Qmax + hyd->Qtol) return(XFLOW); - return(OPEN); -} - - -StatType prvstatus(EN_Project *pr, int k, StatType s, double hset, double h1, double h2) -/* -**----------------------------------------------------------- -** Input: k = link index -** s = current status -** hset = valve head setting -** h1 = head at upstream node -** h2 = head at downstream node -** Output: returns new valve status -** Purpose: updates status of a pressure reducing valve. -**----------------------------------------------------------- -*/ -{ - StatType status; /* New valve status */ - double hml; /* Minor headloss */ - hydraulics_t *hyd = &pr->hydraulics; - - double htol = hyd->Htol; - Slink *link = &pr->network.Link[k]; - - status = s; - if (hyd->LinkSetting[k] == MISSING) - return(status); /* Status fixed by user */ - hml = link->Km*SQR(hyd->LinkFlows[k]); /* Head loss when open */ - - /*** Status rules below have changed. ***/ - - switch (s) - { - case ACTIVE: - if (hyd->LinkFlows[k] < -hyd->Qtol) { - status = CLOSED; - } - else if (h1 - hml < hset - htol) { - status = OPEN; - } - else { - status = ACTIVE; - } - break; - case OPEN: - if (hyd->LinkFlows[k] < -hyd->Qtol) { - status = CLOSED; - } - else if (h2 >= hset + htol) { - status = ACTIVE; - } - else { - status = OPEN; - } - break; - case CLOSED: - if (h1 >= hset + htol && h2 < hset - htol) { - status = ACTIVE; - } - else if (h1 < hset - htol && h1 > h2 + htol) { - status = OPEN; - } - else { - status = CLOSED; - } - break; - case XPRESSURE: - if (hyd->LinkFlows[k] < -hyd->Qtol) { - status = CLOSED; - } - break; - default: - break; - } - return(status); -} - - -StatType psvstatus(EN_Project *pr, int k, StatType s, double hset, double h1, double h2) -/* -**----------------------------------------------------------- -** Input: k = link index -** s = current status -** hset = valve head setting -** h1 = head at upstream node -** h2 = head at downstream node -** Output: returns new valve status -** Purpose: updates status of a pressure sustaining valve. -**----------------------------------------------------------- -*/ -{ - StatType status; /* New valve status */ - double hml; /* Minor headloss */ - hydraulics_t *hyd = &pr->hydraulics; - - double htol = hyd->Htol; - - Slink *link = &pr->network.Link[k]; - - status = s; - if (hyd->LinkSetting[k] == MISSING) { - return(status); /* Status fixed by user */ - } - hml = link->Km*SQR(hyd->LinkFlows[k]); /* Head loss when open */ - -/*** Status rules below have changed. ***/ - - switch (s) - { - case ACTIVE: - if (hyd->LinkFlows[k] < -hyd->Qtol) { - status = CLOSED; - } - else if (h2 + hml > hset + htol) { - status = OPEN; - } - else { - status = ACTIVE; - } - break; - case OPEN: - if (hyd->LinkFlows[k] < -hyd->Qtol) { - status = CLOSED; - } - else if (h1 < hset - htol) { - status = ACTIVE; - } - else { - status = OPEN; - } - break; - case CLOSED: - if (h2 > hset + htol && h1 > h2 + htol) { - status = OPEN; - } - else if (h1 >= hset + htol && h1 > h2 + htol) { - status = ACTIVE; - } - else { - status = CLOSED; - } - break; - case XPRESSURE: - if (hyd->LinkFlows[k] < -hyd->Qtol) { - status = CLOSED; - } - break; - default: - break; - } - return(status); -} - - -StatType fcvstatus(EN_Project *pr, int k, StatType s, double h1, double h2) -/* -**----------------------------------------------------------- -** Input: k = link index -** s = current status -** h1 = head at upstream node -** h2 = head at downstream node -** Output: returns new valve status -** Purpose: updates status of a flow control valve. -** -** Valve status changes to XFCV if flow reversal. -** If current status is XFCV and current flow is -** above setting, then valve becomes active. -** If current status is XFCV, and current flow -** positive but still below valve setting, then -** status remains same. -**----------------------------------------------------------- -*/ -{ - StatType status; /* New valve status */ - hydraulics_t *hyd = &pr->hydraulics; - - status = s; - if (h1 - h2 < -hyd->Htol) { - status = XFCV; - } - else if (hyd->LinkFlows[k] < -hyd->Qtol) { - status = XFCV; - } - else if (s == XFCV && hyd->LinkFlows[k] >= hyd->LinkSetting[k]) { - status = ACTIVE; - } - return(status); -} - - -/*** Updated 9/7/00 ***/ -/*** Updated 11/19/01 ***/ -void tankstatus(EN_Project *pr, int k, int n1, int n2) -/* -**---------------------------------------------------------------- -** Input: k = link index -** n1 = start node of link -** n2 = end node of link -** Output: none -** Purpose: closes link flowing into full or out of empty tank -**---------------------------------------------------------------- -*/ -{ - int i, n; - double h, q; - Stank *tank; - - hydraulics_t *hyd = &pr->hydraulics; - EN_Network *net = &pr->network; - Slink *link = &net->Link[k]; - - /* Make node n1 be the tank */ - q = hyd->LinkFlows[k]; - i = n1 - net->Njuncs; - if (i <= 0) { - i = n2 - net->Njuncs; - if (i <= 0) { - return; - } - n = n1; - n1 = n2; - n2 = n; - q = -q; - } - h = hyd->NodeHead[n1] - hyd->NodeHead[n2]; - tank = &net->Tank[i]; - /* Skip reservoirs & closed links */ - if (tank->A == 0.0 || hyd->LinkStatus[k] <= CLOSED) { - return; - } - - /* If tank full, then prevent flow into it */ - if (hyd->NodeHead[n1] >= tank->Hmax - hyd->Htol) - { - - /* Case 1: Link is a pump discharging into tank */ - if (link->Type == EN_PUMP) { - if (link->N2 == n1) - hyd->LinkStatus[k] = TEMPCLOSED; - } - - /* Case 2: Downstream head > tank head */ - /* (i.e., an open outflow check valve would close) */ - else if (cvstatus(pr, OPEN, h, q) == CLOSED) { - hyd->LinkStatus[k] = TEMPCLOSED; - } - } - - /* If tank empty, then prevent flow out of it */ - if (hyd->NodeHead[n1] <= tank->Hmin + hyd->Htol) { - - /* Case 1: Link is a pump discharging from tank */ - if (link->Type == EN_PUMP) { - if (link->N1 == n1) { - hyd->LinkStatus[k] = TEMPCLOSED; - } - } - - /* Case 2: Tank head > downstream head */ - /* (i.e., a closed outflow check valve would open) */ - else if (cvstatus(pr, CLOSED, h, q) == OPEN) { - hyd->LinkStatus[k] = TEMPCLOSED; - } - } -} /* End of tankstatus */ - - int pswitch(EN_Project *pr) /* **-------------------------------------------------------------- @@ -752,98 +289,133 @@ int pswitch(EN_Project *pr) **-------------------------------------------------------------- */ { - int i, /* Control statement index */ - k, /* Link being controlled */ - n, /* Node controlling Slink */ - reset, /* Flag on control conditions */ - change, /* Flag for status or setting change */ - anychange = 0; /* Flag for 1 or more changes */ - char s; /* Current link status */ + int i, // Control statement index + k, // Index of link being controlled + n, // Node controlling link k + reset, // Flag on control conditions + change, // Flag for status or setting change + anychange = 0; // Flag for 1 or more control actions + char s; // Current link status EN_Network *net = &pr->network; hydraulics_t *hyd = &pr->hydraulics; report_options_t *rep = &pr->report; Slink *link; - /* Check each control statement */ + // Check each control statement for (i = 1; i <= net->Ncontrols; i++) { reset = 0; - if ((k = net->Control[i].Link) <= 0) { - continue; - } + k = net->Control[i].Link; + if (k <= 0) continue; - /* Determine if control based on a junction, not a tank */ - if ((n = net->Control[i].Node) > 0 && n <= net->Njuncs) { - /* Determine if control conditions are satisfied */ - if (net->Control[i].Type == LOWLEVEL - && hyd->NodeHead[n] <= net->Control[i].Grade + hyd->Htol) { + // Determine if control based on a junction, not a tank + n = net->Control[i].Node; + if (n > 0 && n <= net->Njuncs) + { + // Determine if control conditions are satisfied + if (net->Control[i].Type == LOWLEVEL && + hyd->NodeHead[n] <= net->Control[i].Grade + hyd->Htol) + { reset = 1; } - if (net->Control[i].Type == HILEVEL - && hyd->NodeHead[n] >= net->Control[i].Grade - hyd->Htol) { + if (net->Control[i].Type == HILEVEL && + hyd->NodeHead[n] >= net->Control[i].Grade - hyd->Htol) + { reset = 1; } } - /* Determine if control forces a status or setting change */ + // Determine if control forces a status or setting change if (reset == 1) { link = &net->Link[k]; change = 0; s = hyd->LinkStatus[k]; - if (link->Type == EN_PIPE) { - if (s != net->Control[i].Status) { - change = 1; - } + if (link->Type == EN_PIPE) + { + if (s != net->Control[i].Status) change = 1; } - if (link->Type == EN_PUMP) { - if (hyd->LinkSetting[k] != net->Control[i].Setting) { - change = 1; - } + if (link->Type == EN_PUMP) + { + if (hyd->LinkSetting[k] != net->Control[i].Setting) change = 1; } - if (link->Type >= EN_PRV) { - if (hyd->LinkSetting[k] != net->Control[i].Setting) { - change = 1; - } - else if (hyd->LinkSetting[k] == MISSING && s != net->Control[i].Status) { + if (link->Type >= EN_PRV) + { + if (hyd->LinkSetting[k] != net->Control[i].Setting) change = 1; + else if (hyd->LinkSetting[k] == MISSING && s != net->Control[i].Status) + { change = 1; } } - /* If a change occurs, update status & setting */ - if (change) { + // If a change occurs, update status & setting + if (change) + { hyd->LinkStatus[k] = net->Control[i].Status; - if (link->Type > EN_PIPE) { + if (link->Type > EN_PIPE) + { hyd->LinkSetting[k] = net->Control[i].Setting; } - if (rep->Statflag == FULL) { + if (rep->Statflag == FULL) + { writestatchange(pr, k, s, hyd->LinkStatus[k]); } - - /* Re-set flow if status has changed */ - // if (S[k] != s) initlinkflow(k, S[k], K[k]); anychange = 1; } } } return(anychange); -} /* End of pswitch */ +} double newflows(EN_Project *pr, Hydbalance *hbal) /* **---------------------------------------------------------------- -** Input: none +** Input: hbal = ptr. to hydraulic balance information ** Output: returns solution convergence error +** Purpose: updates link, emitter & demand flows after new +** nodal heads are computed. +**---------------------------------------------------------------- +*/ +{ + double dqsum, // Network flow change + qsum; // Network total flow + + EN_Network *net = &pr->network; + hydraulics_t *hyd = &pr->hydraulics; + + // Initialize sum of flows & corrections + qsum = 0.0; + dqsum = 0.0; + hbal->maxflowchange = 0.0; + hbal->maxflowlink = 1; + hbal->maxflownode = -1; + + // Update flows in all real and virtual links + newlinkflows(pr, hbal, &qsum, &dqsum); + newemitterflows(pr, hbal, &qsum, &dqsum); + newdemandflows(pr, hbal, &qsum, &dqsum); + + // Return ratio of total flow corrections to total flow + if (qsum > hyd->Hacc) return (dqsum / qsum); + else return dqsum; +} + + +void newlinkflows(EN_Project *pr, Hydbalance *hbal, double *qsum, double *dqsum) +/* +**---------------------------------------------------------------- +** Input: hbal = ptr. to hydraulic balance information +** qsum = sum of current system flows +** dqsum = sum of system flow changes +** Output: updates hbal, qsum and dqsum ** Purpose: updates link flows after new nodal heads computed **---------------------------------------------------------------- */ { double dh, /* Link head loss */ dq; /* Link flow change */ - double dqsum, /* Network flow change */ - qsum; /* Network total flow */ int k, n, n1, n2; EN_Network *net = &pr->network; @@ -851,114 +423,152 @@ double newflows(EN_Project *pr, Hydbalance *hbal) solver_t *sol = &hyd->solver; Slink *link; - /* Initialize net inflows (i.e., demands) at tanks */ - for (n = net->Njuncs + 1; n <= net->Nnodes; n++) { + // Initialize net inflows (i.e., demands) at fixed grade nodes + for (n = net->Njuncs + 1; n <= net->Nnodes; n++) + { hyd->NodeDemand[n] = 0.0; } - /* Initialize sum of flows & corrections */ - qsum = 0.0; - dqsum = 0.0; - - hbal->maxflowchange = 0.0; - hbal->maxflowlink = 1; - - /* Update flows in all links */ + // Examine each link for (k = 1; k <= net->Nlinks; k++) { + // Get link and its end nodes link = &net->Link[k]; - /* - ** Apply flow update formula: - ** dq = Y - P*(new head loss) - ** P = 1/(dh/dq) - ** Y = P*(head loss based on current flow) - ** where P & Y were computed in newcoeffs(). - */ - n1 = link->N1; n2 = link->N2; + + // Apply flow update formula: + // dq = Y - P * (new head loss) + // P = 1 / (previous head loss gradient) + // Y = P * (previous head loss) + // where P & Y were computed in hlosscoeff() in hydcoeffs.c + dh = hyd->NodeHead[n1] - hyd->NodeHead[n2]; dq = sol->Y[k] - sol->P[k] * dh; - /* Adjust flow change by the relaxation factor */ + // Adjust flow change by the relaxation factor dq *= hyd->RelaxFactor; - /* Prevent flow in constant HP pumps from going negative */ - if (link->Type == EN_PUMP) { + // Prevent flow in constant HP pumps from going negative + if (link->Type == EN_PUMP) + { n = findpump(net, k); - if (net->Pump[n].Ptype == CONST_HP && dq > hyd->LinkFlows[k]) { + if (net->Pump[n].Ptype == CONST_HP && dq > hyd->LinkFlows[k]) + { dq = hyd->LinkFlows[k] / 2.0; } } + + // Update link flow and system flow summation hyd->LinkFlows[k] -= dq; + *qsum += ABS(hyd->LinkFlows[k]); + *dqsum += ABS(dq); - /* Update sum of absolute flows & flow corrections */ - qsum += ABS(hyd->LinkFlows[k]); - dqsum += ABS(dq); - - /* Update identity of link with max. flow change */ - if (ABS(dq) > hbal->maxflowchange) { + // Update identity of element with max. flow change + if (ABS(dq) > hbal->maxflowchange) + { hbal->maxflowchange = ABS(dq); hbal->maxflowlink = k; - } + hbal->maxflownode = -1; + } - /* Update net flows to tanks */ + // Update net flows to fixed grade nodes if (hyd->LinkStatus[k] > CLOSED) { - if (n1 > net->Njuncs) { - hyd->NodeDemand[n1] -= hyd->LinkFlows[k]; - } - if (n2 > net->Njuncs) { - hyd->NodeDemand[n2] += hyd->LinkFlows[k]; - } + if (n1 > net->Njuncs) hyd->NodeDemand[n1] -= hyd->LinkFlows[k]; + if (n2 > net->Njuncs) hyd->NodeDemand[n2] += hyd->LinkFlows[k]; } } - - /* Update emitter flows */ - for (k = 1; k <= net->Njuncs; k++) - { - if (net->Node[k].Ke == 0.0) { - continue; - } - dq = emitflowchange(pr, k); - hyd->EmitterFlows[k] -= dq; - qsum += ABS(hyd->EmitterFlows[k]); - dqsum += ABS(dq); - } - - /* Return ratio of total flow corrections to total flow */ - if (qsum > hyd->Hacc) { - return(dqsum / qsum); - } - else { - return(dqsum); - } -} /* End of newflows */ +} -double emitflowchange(EN_Project *pr, int i) +void newemitterflows(EN_Project *pr, Hydbalance *hbal, double *qsum, + double *dqsum) /* -**-------------------------------------------------------------- -** Input: i = node index -** Output: returns change in flow at an emitter node -** Purpose: computes flow change at an emitter node -**-------------------------------------------------------------- +**---------------------------------------------------------------- +** Input: hbal = ptr. to hydraulic balance information +** qsum = sum of current system flows +** dqsum = sum of system flow changes +** Output: updates hbal, qsum and dqsum +** Purpose: updates nodal emitter flows after new nodal heads computed +**---------------------------------------------------------------- */ { - double ke, p; + double dq; + int k; + EN_Network *net = &pr->network; hydraulics_t *hyd = &pr->hydraulics; - EN_Network *n = &pr->network; - Snode *node = &n->Node[i]; - ke = MAX(CSMALL, node->Ke); - p = hyd->Qexp * ke * pow(ABS(hyd->EmitterFlows[i]), (hyd->Qexp - 1.0)); - if (p < hyd->RQtol) { - p = 1 / hyd->RQtol; + // Examine each network junction + for (k = 1; k <= net->Njuncs; k++) + { + // Skip junction if it does not have an emitter + if (net->Node[k].Ke == 0.0) continue; + + // Find emitter flow change (see hydcoeffs.c) + dq = emitflowchange(pr, k); + hyd->EmitterFlows[k] -= dq; + + // Update system flow summation + *qsum += ABS(hyd->EmitterFlows[k]); + *dqsum += ABS(dq); + + // Update identity of element with max. flow change + if (ABS(dq) > hbal->maxflowchange) + { + hbal->maxflowchange = ABS(dq); + hbal->maxflownode = k; + hbal->maxflowlink = -1; + } } - else { - p = 1.0 / p; +} + + +void newdemandflows(EN_Project *pr, Hydbalance *hbal, double *qsum, double *dqsum) +/* +**---------------------------------------------------------------- +** Input: hbal = ptr. to hydraulic balance information +** qsum = sum of current system flows +** dqsum = sum of system flow changes +** Output: updates hbal, qsum and dqsum +** Purpose: updates nodal pressure dependent demand flows after +** new nodal heads computed +**---------------------------------------------------------------- +*/ +{ + double dp, // pressure range over which demand can vary (ft) + dq, // change in demand flow (cfs) + n; // exponent in head loss v. demand function + int k; + EN_Network *net = &pr->network; + hydraulics_t *hyd = &pr->hydraulics; + + // Get demand function parameters + if (hyd->DemandModel == DDA) return; + demandparams(pr, &dp, &n); + + // Examine each junction + for (k = 1; k <= net->Njuncs; k++) + { + // Skip junctions with no positive demand + if (hyd->NodeDemand[k] <= 0.0) continue; + + // Find change in demand flow (see hydcoeffs.c) + dq = demandflowchange(pr, k, dp, n); + hyd->DemandFlows[k] -= dq; + + // Update system flow summation + *qsum += ABS(hyd->DemandFlows[k]); + *dqsum += ABS(dq); + + // Update identity of element with max. flow change + if (ABS(dq) > hbal->maxflowchange) + { + hbal->maxflowchange = ABS(dq); + hbal->maxflownode = k; + hbal->maxflowlink = -1; + } } - return(hyd->EmitterFlows[i] / hyd->Qexp - p * (hyd->NodeHead[i] - node->El)); } @@ -980,9 +590,10 @@ void checkhydbalance(EN_Project *pr, Hydbalance *hbal) Slink *link; hbal->maxheaderror = 0.0; hbal->maxheadlink = 1; - for (k = 1; k <= net->Nlinks; k++) { + headlosscoeffs(pr); + for (k = 1; k <= net->Nlinks; k++) + { if (hyd->LinkStatus[k] <= CLOSED) continue; - hlosscoeff(pr, k); if (sol->P[k] == 0.0) continue; link = &net->Link[k]; n1 = link->N1; @@ -990,7 +601,8 @@ void checkhydbalance(EN_Project *pr, Hydbalance *hbal) dh = hyd->NodeHead[n1] - hyd->NodeHead[n2]; headloss = sol->Y[k] / sol->P[k]; headerror = ABS(dh - headloss); - if (headerror > hbal->maxheaderror) { + if (headerror > hbal->maxheaderror) + { hbal->maxheaderror = headerror; hbal->maxheadlink = k; } @@ -1013,13 +625,14 @@ int hasconverged(EN_Project *pr, double *relerr, Hydbalance *hbal) if (*relerr > hyd->Hacc) return 0; checkhydbalance(pr, hbal); - if (pr->report.Statflag == FULL) { + if (pr->report.Statflag == FULL) + { reporthydbal(pr, hbal); } if (hyd->HeadErrorLimit > 0.0 && - hbal->maxheaderror > hyd->HeadErrorLimit) return 0; + hbal->maxheaderror > hyd->HeadErrorLimit) return 0; if (hyd->FlowChangeLimit > 0.0 && - hbal->maxflowchange > hyd->FlowChangeLimit) return 0; + hbal->maxflowchange > hyd->FlowChangeLimit) return 0; return 1; } @@ -1037,13 +650,21 @@ void reporthydbal(EN_Project *pr, Hydbalance *hbal) double qchange = hbal->maxflowchange * pr->Ucf[FLOW]; double herror = hbal->maxheaderror * pr->Ucf[HEAD]; int qlink = hbal->maxflowlink; + int qnode = hbal->maxflownode; int hlink = hbal->maxheadlink; - if (qlink >= 1) { + if (qlink >= 1) + { sprintf(pr->Msg, FMT66, qchange, pr->network.Link[qlink].ID); writeline(pr, pr->Msg); } - if (hlink >= 1) { - sprintf(pr->Msg, FMT67, herror, pr->network.Link[hlink].ID); + else if (qnode >= 1) + { + sprintf(pr->Msg, FMT67, qchange, pr->network.Node[qnode].ID); + writeline(pr, pr->Msg); + } + if (hlink >= 1) + { + sprintf(pr->Msg, FMT68, herror, pr->network.Link[hlink].ID); writeline(pr, pr->Msg); } } diff --git a/src/hydstatus.c b/src/hydstatus.c new file mode 100644 index 0000000..f5041c6 --- /dev/null +++ b/src/hydstatus.c @@ -0,0 +1,462 @@ +/* +********************************************************************* + +HYDSTATUS.C -- Hydraulic status updating for the EPANET Program + +******************************************************************* +*/ + +#include +#include "types.h" +#include "funcs.h" + +// External functions +int valvestatus(EN_Project *pr); +int linkstatus(EN_Project *pr); + +// Local functions +static StatType cvstatus(EN_Project *pr, StatType, double, double); +static StatType pumpstatus(EN_Project *pr, int, double); +static StatType prvstatus(EN_Project *pr, int, StatType, double, double, double); +static StatType psvstatus(EN_Project *pr, int, StatType, double, double, double); +static StatType fcvstatus(EN_Project *pr, int, StatType, double, double); +static void tankstatus(EN_Project *pr, int, int, int); + + +int valvestatus(EN_Project *pr) +/* +**----------------------------------------------------------------- +** Input: none +** Output: returns 1 if any pressure or flow control valve +** changes status, 0 otherwise +** Purpose: updates status for PRVs & PSVs whose status +** is not fixed to OPEN/CLOSED +**----------------------------------------------------------------- +*/ +{ + int change = FALSE, // Status change flag + i, k, // Valve & link indexes + n1, n2; // Start & end nodes + StatType status; // Valve status settings + double hset; // Valve head setting + Slink *link; + + EN_Network *net = &pr->network; + hydraulics_t *hyd = &pr->hydraulics; + report_options_t *rep = &pr->report; + + // Examine each valve + for (i = 1; i <= net->Nvalves; i++) + { + // Get valve's link and its index + k = net->Valve[i].Link; + link = &net->Link[k]; + + // Ignore valve if its status is fixed to OPEN/CLOSED + if (hyd->LinkSetting[k] == MISSING) continue; + + // Get start/end node indexes & save current status + n1 = link->N1; + n2 = link->N2; + status = hyd->LinkStatus[k]; + + // Evaluate valve's new status + switch (link->Type) + { + case EN_PRV: + hset = net->Node[n2].El + hyd->LinkSetting[k]; + hyd->LinkStatus[k] = prvstatus(pr, k, status, hset, + hyd->NodeHead[n1], hyd->NodeHead[n2]); + break; + case EN_PSV: + hset = net->Node[n1].El + hyd->LinkSetting[k]; + hyd->LinkStatus[k] = psvstatus(pr, k, status, hset, + hyd->NodeHead[n1], hyd->NodeHead[n2]); + break; + default: + continue; + } + + // Check for a status change + if (status != hyd->LinkStatus[k]) + { + if (rep->Statflag == FULL) + { + writestatchange(pr, k, status, hyd->LinkStatus[k]); + } + change = TRUE; + } + } + return change; +} /* End of valvestatus() */ + + +int linkstatus(EN_Project *pr) +/* +**-------------------------------------------------------------- +** Input: none +** Output: returns 1 if any link changes status, 0 otherwise +** Purpose: determines new status for pumps, CVs, FCVs & pipes +** to tanks. +**-------------------------------------------------------------- +*/ +{ + int change = FALSE, // Status change flag + k, // Link index + n1, // Start node index + n2; // End node index + double dh; // Head difference across link + StatType status; // Current status + + EN_Network *net = &pr->network; + hydraulics_t *hyd = &pr->hydraulics; + report_options_t *rep = &pr->report; + Slink *link; + + // Examine each link + for (k = 1; k <= net->Nlinks; k++) + { + link = &net->Link[k]; + n1 = link->N1; + n2 = link->N2; + dh = hyd->NodeHead[n1] - hyd->NodeHead[n2]; + + // Re-open temporarily closed links (status = XHEAD or TEMPCLOSED) + status = hyd->LinkStatus[k]; + if (status == XHEAD || status == TEMPCLOSED) + { + hyd->LinkStatus[k] = OPEN; + } + + // Check for status changes in CVs and pumps + if (link->Type == EN_CVPIPE) + { + hyd->LinkStatus[k] = cvstatus(pr, hyd->LinkStatus[k], dh, + hyd->LinkFlows[k]); + } + if (link->Type == EN_PUMP && hyd->LinkStatus[k] >= OPEN && + hyd->LinkSetting[k] > 0.0) + { + hyd->LinkStatus[k] = pumpstatus(pr, k, -dh); + } + + // Check for status changes in non-fixed FCVs + if (link->Type == EN_FCV && hyd->LinkSetting[k] != MISSING) + { + hyd->LinkStatus[k] = fcvstatus(pr, k, status, hyd->NodeHead[n1], + hyd->NodeHead[n2]); + } + + // Check for flow into (out of) full (empty) tanks + if (n1 > net->Njuncs || n2 > net->Njuncs) + { + tankstatus(pr, k, n1, n2); + } + + // Note any change in link status; do not revise link flow + if (status != hyd->LinkStatus[k]) + { + change = TRUE; + if (rep->Statflag == FULL) + { + writestatchange(pr, k, status, hyd->LinkStatus[k]); + } + } + } + return change; +} + + +StatType cvstatus(EN_Project *pr, StatType s, double dh, double q) +/* +**-------------------------------------------------- +** Input: s = current link status +** dh = head loss across link +** q = link flow +** Output: returns new link status +** Purpose: updates status of a check valve link. +**-------------------------------------------------- +*/ +{ + hydraulics_t *hyd = &pr->hydraulics; + + // Prevent reverse flow through CVs + if (ABS(dh) > hyd->Htol) + { + if (dh < -hyd->Htol) return CLOSED; + else if (q < -hyd->Qtol) return CLOSED; + else return OPEN; + } + else + { + if (q < -hyd->Qtol) return CLOSED; + else return s; + } +} + + +StatType pumpstatus(EN_Project *pr, int k, double dh) +/* +**-------------------------------------------------- +** Input: k = link index +** dh = head gain across link +** Output: returns new pump status +** Purpose: updates status of an open pump. +**-------------------------------------------------- +*/ +{ + int p; + double hmax; + hydraulics_t *hyd = &pr->hydraulics; + EN_Network *net = &pr->network; + + // Find maximum head (hmax) pump can deliver + p = findpump(net, k); + if (net->Pump[p].Ptype == CONST_HP) + { + // Use huge value for constant HP pump + hmax = BIG; + } + else + { + // Use speed-adjusted shut-off head for other pumps + hmax = SQR(hyd->LinkSetting[k]) * net->Pump[p].Hmax; + } + + // Check if currrent head gain exceeds pump's max. head + if (dh > hmax + hyd->Htol) return XHEAD; + + // No check is made to see if flow exceeds pump's max. flow + return OPEN; +} + + +StatType prvstatus(EN_Project *pr, int k, StatType s, double hset, + double h1, double h2) +/* +**----------------------------------------------------------- +** Input: k = link index +** s = current status +** hset = valve head setting +** h1 = head at upstream node +** h2 = head at downstream node +** Output: returns new valve status +** Purpose: updates status of a pressure reducing valve. +**----------------------------------------------------------- +*/ +{ + StatType status; // Valve's new status + double hml; // Head loss when fully opened + hydraulics_t *hyd = &pr->hydraulics; + + double htol = hyd->Htol; + Slink *link = &pr->network.Link[k]; + + // Head loss when fully open + hml = link->Km * SQR(hyd->LinkFlows[k]); + + // Rules for updating valve's status from current value s + status = s; + switch (s) + { + case ACTIVE: + if (hyd->LinkFlows[k] < -hyd->Qtol) status = CLOSED; + else if (h1 - hml < hset - htol) status = OPEN; + else status = ACTIVE; + break; + + case OPEN: + if (hyd->LinkFlows[k] < -hyd->Qtol) status = CLOSED; + else if (h2 >= hset + htol) status = ACTIVE; + else status = OPEN; + break; + + case CLOSED: + if (h1 >= hset + htol && h2 < hset - htol) status = ACTIVE; + else if (h1 < hset - htol && h1 > h2 + htol) status = OPEN; + else status = CLOSED; + break; + + case XPRESSURE: + if (hyd->LinkFlows[k] < -hyd->Qtol) status = CLOSED; + break; + + default: + break; + } + return status; +} + + +StatType psvstatus(EN_Project *pr, int k, StatType s, double hset, + double h1, double h2) +/* +**----------------------------------------------------------- +** Input: k = link index +** s = current status +** hset = valve head setting +** h1 = head at upstream node +** h2 = head at downstream node +** Output: returns new valve status +** Purpose: updates status of a pressure sustaining valve. +**----------------------------------------------------------- +*/ +{ + StatType status; // Valve's new status + double hml; // Head loss when fully opened + hydraulics_t *hyd = &pr->hydraulics; + + double htol = hyd->Htol; + Slink *link = &pr->network.Link[k]; + + // Head loss when fully open + hml = link->Km * SQR(hyd->LinkFlows[k]); + + // Rules for updating valve's status from current value s + status = s; + switch (s) + { + case ACTIVE: + if (hyd->LinkFlows[k] < -hyd->Qtol) status = CLOSED; + else if (h2 + hml > hset + htol) status = OPEN; + else status = ACTIVE; + break; + + case OPEN: + if (hyd->LinkFlows[k] < -hyd->Qtol) status = CLOSED; + else if (h1 < hset - htol) status = ACTIVE; + else status = OPEN; + break; + + case CLOSED: + if (h2 > hset + htol && h1 > h2 + htol) status = OPEN; + else if (h1 >= hset + htol && h1 > h2 + htol) status = ACTIVE; + else status = CLOSED; + break; + + case XPRESSURE: + if (hyd->LinkFlows[k] < -hyd->Qtol) status = CLOSED; + break; + + default: + break; + } + return status; +} + + +StatType fcvstatus(EN_Project *pr, int k, StatType s, double h1, double h2) +/* +**----------------------------------------------------------- +** Input: k = link index +** s = current status +** h1 = head at upstream node +** h2 = head at downstream node +** Output: returns new valve status +** Purpose: updates status of a flow control valve. +** +** Valve status changes to XFCV if flow reversal. +** If current status is XFCV and current flow is +** above setting, then valve becomes active. +** If current status is XFCV, and current flow +** positive but still below valve setting, then +** status remains same. +**----------------------------------------------------------- +*/ +{ + StatType status; // New valve status + hydraulics_t *hyd = &pr->hydraulics; + + status = s; + if (h1 - h2 < -hyd->Htol) + { + status = XFCV; + } + else if (hyd->LinkFlows[k] < -hyd->Qtol) + { + status = XFCV; + } + else if (s == XFCV && hyd->LinkFlows[k] >= hyd->LinkSetting[k]) + { + status = ACTIVE; + } + return status; +} + + +void tankstatus(EN_Project *pr, int k, int n1, int n2) +/* +**---------------------------------------------------------------- +** Input: k = link index +** n1 = start node of link +** n2 = end node of link +** Output: none +** Purpose: closes link flowing into full or out of empty tank +**---------------------------------------------------------------- +*/ +{ + int i, n; + double h, q; + Stank *tank; + + hydraulics_t *hyd = &pr->hydraulics; + EN_Network *net = &pr->network; + Slink *link = &net->Link[k]; + + // Return if link is closed + if (hyd->LinkStatus[k] <= CLOSED) return; + + // Make node n1 be the tank, reversing flow (q) if need be + q = hyd->LinkFlows[k]; + i = n1 - net->Njuncs; + if (i <= 0) + { + i = n2 - net->Njuncs; + if (i <= 0) return; + n = n1; + n1 = n2; + n2 = n; + q = -q; + } + + // Ignore reservoirs + tank = &net->Tank[i]; + if (tank->A == 0.0) return; + + // Find head difference across link + h = hyd->NodeHead[n1] - hyd->NodeHead[n2]; + + // If tank is full, then prevent flow into it + if (hyd->NodeHead[n1] >= tank->Hmax - hyd->Htol) + { + // Case 1: Link is a pump discharging into tank + if (link->Type == EN_PUMP) + { + if (link->N2 == n1) hyd->LinkStatus[k] = TEMPCLOSED; + } + + // Case 2: Downstream head > tank head + // (e.g., an open outflow check valve would close) + else if (cvstatus(pr, OPEN, h, q) == CLOSED) + { + hyd->LinkStatus[k] = TEMPCLOSED; + } + } + + // If tank is empty, then prevent flow out of it + if (hyd->NodeHead[n1] <= tank->Hmin + hyd->Htol) + { + // Case 1: Link is a pump discharging from tank + if (link->Type == EN_PUMP) + { + if (link->N1 == n1) hyd->LinkStatus[k] = TEMPCLOSED; + } + + // Case 2: Tank head > downstream head + // (e.g., a closed outflow check valve would open) + else if (cvstatus(pr, CLOSED, h, q) == OPEN) + { + hyd->LinkStatus[k] = TEMPCLOSED; + } + } +} diff --git a/src/inpfile.c b/src/inpfile.c index 8bf894d..9e4b130 100644 --- a/src/inpfile.c +++ b/src/inpfile.c @@ -26,14 +26,14 @@ data describing a piping network to a file in EPANET's text format. #else #include #endif + #include "hash.h" #include "text.h" #include "types.h" -#include "epanet2.h" #include "funcs.h" #include -#define EXTERN extern -#include "vars.h" +//#define EXTERN extern +//#include "vars.h" /* Defined in enumstxt.h in EPANET.C */ extern char *LinkTxt[]; @@ -624,6 +624,13 @@ int saveinpfile(EN_Project *pr, char *fname) if (hyd->FlowChangeLimit > 0.0) { fprintf(f, "\n FLOWCHANGE %-.8f", hyd->FlowChangeLimit * pr->Ucf[FLOW]); } + if (hyd->DemandModel == PDA) + { + fprintf(f, "\n DEMAND MODEL PDA"); + fprintf(f, "\n MINIMUM PRESSURE %-.4f", hyd->Pmin * pr->Ucf[PRESSURE]); + fprintf(f, "\n REQUIRED PRESSURE %-.4f", hyd->Preq * pr->Ucf[PRESSURE]); + fprintf(f, "\n PRESSURE EXPONENT %-.4f", hyd->Pexp); + } /* Write [REPORT] section */ diff --git a/src/input1.c b/src/input1.c index b59096a..46da66a 100644 --- a/src/input1.c +++ b/src/input1.c @@ -33,8 +33,6 @@ AUTHOR: L. Rossman #include "epanet2.h" #include "funcs.h" #include -#define EXTERN extern -#include "vars.h" /* --------------------- Module Global Variables ---------------------- @@ -128,8 +126,12 @@ void setdefaults(EN_Project *pr) hyd->Qtol = QTOL; /* Default flow tolerance */ hyd->Hacc = HACC; /* Default hydraulic accuracy */ - hyd->FlowChangeLimit = 0.0; /* Default flow change limit */ - hyd->HeadErrorLimit = 0.0; /* Default head error limit */ + hyd->FlowChangeLimit = 0.0; // Default flow change limit + hyd->HeadErrorLimit = 0.0; // Default head error limit + hyd->DemandModel = DDA; // Demand driven analysis + hyd->Pmin = 0.0; // Minimum demand pressure (ft) + hyd->Preq = 0.0; // Required demand pressure (ft) + hyd->Pexp = 0.5; // Pressure function exponent qu->Ctol = MISSING; /* No pre-set quality tolerance */ hyd->MaxIter = MAXITER; /* Default max. hydraulic trials */ @@ -581,6 +583,8 @@ void convertunits(EN_Project *pr) demand->Base /= pr->Ucf[DEMAND]; } } + hyd->Pmin /= pr->Ucf[PRESSURE]; + hyd->Preq /= pr->Ucf[PRESSURE]; /* Convert emitter discharge coeffs. to head loss coeff. */ ucf = pow(pr->Ucf[FLOW], hyd->Qexp) / pr->Ucf[PRESSURE]; diff --git a/src/input2.c b/src/input2.c index b0a1b13..c40d029 100644 --- a/src/input2.c +++ b/src/input2.c @@ -37,8 +37,6 @@ The following utility functions are all called from INPUT3.C #include "epanet2.h" #include "funcs.h" #include -#define EXTERN extern -#include "vars.h" #define MAXERRS 10 /* Max. input errors reported */ @@ -822,9 +820,10 @@ int match(const char *str, const char *substr) break; /* Check if substr matches remainder of str. */ - for (i = i, j = 0; substr[j]; i++, j++) + for (j = 0; substr[j]; i++, j++) if (!str[i] || UCHAR(str[i]) != UCHAR(substr[j])) return (0); + return (1); } /* end of match */ diff --git a/src/input3.c b/src/input3.c index 9c66f63..58f9ce2 100644 --- a/src/input3.c +++ b/src/input3.c @@ -32,12 +32,11 @@ All functions in this module are called from newline() in INPUT2.C. #include "text.h" #include "types.h" #include -#define EXTERN extern -#include "vars.h" /* Defined in enumstxt.h in EPANET.C */ extern char *MixTxt[]; extern char *Fldname[]; +extern char *DemandModelTxt[]; /* Defined in INPUT2.C */ @@ -1763,6 +1762,7 @@ int optionchoice(EN_Project *pr, int n) ** VERIFY filename ** UNBALANCED STOP/CONTINUE {Niter} ** PATTERN id +** DEMAND MODEL DDA/PDA/PPA **-------------------------------------------------------------- */ { @@ -1771,131 +1771,125 @@ int optionchoice(EN_Project *pr, int n) quality_t *qu = &pr->quality; parser_data_t *par = &pr->parser; out_file_t *out = &pr->out_files; - + int choice; /* Check if 1st token matches a parameter name and */ /* process the input for the matched parameter */ - if (n < 0) - return (201); - if (match(par->Tok[0], w_UNITS)) { - if (n < 1) - return (0); - else if (match(par->Tok[1], w_CFS)) - par->Flowflag = CFS; - else if (match(par->Tok[1], w_GPM)) - par->Flowflag = GPM; - else if (match(par->Tok[1], w_AFD)) - par->Flowflag = AFD; - else if (match(par->Tok[1], w_MGD)) - par->Flowflag = MGD; - else if (match(par->Tok[1], w_IMGD)) - par->Flowflag = IMGD; - else if (match(par->Tok[1], w_LPS)) - par->Flowflag = LPS; - else if (match(par->Tok[1], w_LPM)) - par->Flowflag = LPM; - else if (match(par->Tok[1], w_CMH)) - par->Flowflag = CMH; - else if (match(par->Tok[1], w_CMD)) - par->Flowflag = CMD; - else if (match(par->Tok[1], w_MLD)) - par->Flowflag = MLD; - else if (match(par->Tok[1], w_SI)) - par->Flowflag = LPS; - else - return (201); - } else if (match(par->Tok[0], w_PRESSURE)) { - if (n < 1) - return (0); - else if (match(par->Tok[1], w_PSI)) - par->Pressflag = PSI; - else if (match(par->Tok[1], w_KPA)) - par->Pressflag = KPA; - else if (match(par->Tok[1], w_METERS)) - par->Pressflag = METERS; - else - return (201); - } else if (match(par->Tok[0], w_HEADLOSS)) { - if (n < 1) - return (0); - else if (match(par->Tok[1], w_HW)) - hyd->Formflag = HW; - else if (match(par->Tok[1], w_DW)) - hyd->Formflag = DW; - else if (match(par->Tok[1], w_CM)) - hyd->Formflag = CM; - else - return (201); - } else if (match(par->Tok[0], w_HYDRAULIC)) { - if (n < 2) - return (0); - else if (match(par->Tok[1], w_USE)) - out->Hydflag = USE; - else if (match(par->Tok[1], w_SAVE)) - out->Hydflag = SAVE; - else - return (201); + if (n < 0) return (201); + if (match(par->Tok[0], w_UNITS)) + { + if (n < 1) return (0); + else if (match(par->Tok[1], w_CFS)) par->Flowflag = CFS; + else if (match(par->Tok[1], w_GPM)) par->Flowflag = GPM; + else if (match(par->Tok[1], w_AFD)) par->Flowflag = AFD; + else if (match(par->Tok[1], w_MGD)) par->Flowflag = MGD; + else if (match(par->Tok[1], w_IMGD)) par->Flowflag = IMGD; + else if (match(par->Tok[1], w_LPS)) par->Flowflag = LPS; + else if (match(par->Tok[1], w_LPM)) par->Flowflag = LPM; + else if (match(par->Tok[1], w_CMH)) par->Flowflag = CMH; + else if (match(par->Tok[1], w_CMD)) par->Flowflag = CMD; + else if (match(par->Tok[1], w_MLD)) par->Flowflag = MLD; + else if (match(par->Tok[1], w_SI)) par->Flowflag = LPS; + else return (201); + } + + else if (match(par->Tok[0], w_PRESSURE)) + { + if (n < 1) return (0); + else if (match(par->Tok[1], w_EXPONENT)) return -1; + else if (match(par->Tok[1], w_PSI)) par->Pressflag = PSI; + else if (match(par->Tok[1], w_KPA)) par->Pressflag = KPA; + else if (match(par->Tok[1], w_METERS)) par->Pressflag = METERS; + else return (201); + } + + else if (match(par->Tok[0], w_HEADLOSS)) + { + if (n < 1) return (0); + else if (match(par->Tok[1], w_HW)) hyd->Formflag = HW; + else if (match(par->Tok[1], w_DW)) hyd->Formflag = DW; + else if (match(par->Tok[1], w_CM)) hyd->Formflag = CM; + else return (201); + } + + else if (match(par->Tok[0], w_HYDRAULIC)) + { + if (n < 2) return (0); + else if (match(par->Tok[1], w_USE)) out->Hydflag = USE; + else if (match(par->Tok[1], w_SAVE)) out->Hydflag = SAVE; + else return (201); strncpy(out->HydFname, par->Tok[2], MAXFNAME); - } else if (match(par->Tok[0], w_QUALITY)) { - if (n < 1) - return (0); - else if (match(par->Tok[1], w_NONE)) - qu->Qualflag = NONE; - else if (match(par->Tok[1], w_CHEM)) - qu->Qualflag = CHEM; - else if (match(par->Tok[1], w_AGE)) - qu->Qualflag = AGE; - else if (match(par->Tok[1], w_TRACE)) - qu->Qualflag = TRACE; - else { - qu->Qualflag = CHEM; - strncpy(qu->ChemName, par->Tok[1], MAXID); - if (n >= 2) - strncpy(qu->ChemUnits, par->Tok[2], MAXID); + } + + else if (match(par->Tok[0], w_QUALITY)) + { + if (n < 1) return (0); + else if (match(par->Tok[1], w_NONE)) qu->Qualflag = NONE; + else if (match(par->Tok[1], w_CHEM)) qu->Qualflag = CHEM; + else if (match(par->Tok[1], w_AGE)) qu->Qualflag = AGE; + else if (match(par->Tok[1], w_TRACE)) qu->Qualflag = TRACE; + else + { + qu->Qualflag = CHEM; + strncpy(qu->ChemName, par->Tok[1], MAXID); + if (n >= 2) strncpy(qu->ChemUnits, par->Tok[2], MAXID); } if (qu->Qualflag == TRACE) /* Source tracing option */ { /* Copy Trace Node ID to par->Tok[0] for error reporting */ strcpy(par->Tok[0], ""); - if (n < 2) - return (212); + if (n < 2) return (212); strcpy(par->Tok[0], par->Tok[2]); qu->TraceNode = findnode(net,par->Tok[2]); - if (qu->TraceNode == 0) - return (212); + if (qu->TraceNode == 0) return (212); strncpy(qu->ChemName, u_PERCENT, MAXID); strncpy(qu->ChemUnits, par->Tok[2], MAXID); } - if (qu->Qualflag == AGE) { + if (qu->Qualflag == AGE) + { strncpy(qu->ChemName, w_AGE, MAXID); strncpy(qu->ChemUnits, u_HOURS, MAXID); } - } else if (match(par->Tok[0], w_MAP)) { - if (n < 1) - return (0); + } + + else if (match(par->Tok[0], w_MAP)) + { + if (n < 1) return (0); strncpy(pr->MapFname, par->Tok[1], MAXFNAME); /* Map file name */ - } else if (match(par->Tok[0], w_VERIFY)) { + } + + else if (match(par->Tok[0], w_VERIFY)) + { /* Backward compatibility for verification file */ - } else if (match(par->Tok[0], w_UNBALANCED)) /* Unbalanced option */ + } + + else if (match(par->Tok[0], w_UNBALANCED)) /* Unbalanced option */ { - if (n < 1) - return (0); - if (match(par->Tok[1], w_STOP)) - hyd->ExtraIter = -1; - else if (match(par->Tok[1], w_CONTINUE)) { - if (n >= 2) - hyd->ExtraIter = atoi(par->Tok[2]); - else - hyd->ExtraIter = 0; - } else - return (201); - } else if (match(par->Tok[0], w_PATTERN)) /* Pattern option */ + if (n < 1) return (0); + if (match(par->Tok[1], w_STOP)) hyd->ExtraIter = -1; + else if (match(par->Tok[1], w_CONTINUE)) + { + if (n >= 2) hyd->ExtraIter = atoi(par->Tok[2]); + else hyd->ExtraIter = 0; + } + else return (201); + } + + else if (match(par->Tok[0], w_PATTERN)) /* Pattern option */ { - if (n < 1) - return (0); + if (n < 1) return (0); strncpy(par->DefPatID, par->Tok[1], MAXID); - } else - return (-1); + } + + else if (match(par->Tok[0], w_DEMAND)) + { + if (n < 2) return 0; + if (!match(par->Tok[1], w_MODEL)) return -1; + choice = findmatch(par->Tok[2], DemandModelTxt); + if (choice < 0) return 201; + hyd->DemandModel = choice; + } + else return (-1); return (0); } /* end of optionchoice */ @@ -1916,6 +1910,9 @@ int optionvalue(EN_Project *pr, int n) ** HEADLIMIT value ** FLOWLIMIT value +** MINIMUM PRESSURE value +** REQUIRED PRESSURE value +** PRESSURE EXPONENT value ** TOLERANCE value ** SEGMENTS value (not used) @@ -1939,39 +1936,40 @@ int optionvalue(EN_Project *pr, int n) double y; /* Check for obsolete SEGMENTS keyword */ - //if (match(par->Tok[0], w_SEGMENTS)) - if (match(tok0, w_SEGMENTS)) - return (0); + if (match(tok0, w_SEGMENTS)) return (0); /* Check for missing value (which is permissible) */ if (match(tok0, w_SPECGRAV) || match(tok0, w_EMITTER) || - match(tok0, w_DEMAND)) + match(tok0, w_DEMAND) || match(tok0, w_MINIMUM) || + match(tok0, w_REQUIRED) || match(tok0, w_PRESSURE) || + match(tok0, w_PRECISION)) + { nvalue = 2; - if (n < nvalue) - return (0); + } + if (n < nvalue) return (0); /* Check for valid numerical input */ - if (!getfloat(par->Tok[nvalue], &y)) - return (213); + if (!getfloat(par->Tok[nvalue], &y)) return (213); /* Check for WQ tolerance option (which can be 0) */ - if (match(tok0, w_TOLERANCE)) { - if (y < 0.0) - return (213); + if (match(tok0, w_TOLERANCE)) + { + if (y < 0.0) return (213); qu->Ctol = y; /* Quality tolerance*/ return (0); } /* Check for Diffusivity option */ - if (match(tok0, w_DIFFUSIVITY)) { - if (y < 0.0) - return (213); + if (match(tok0, w_DIFFUSIVITY)) + { + if (y < 0.0) return (213); qu->Diffus = y; return (0); } /* Check for Damping Limit option */ - if (match(tok0, w_DAMPLIMIT)) { + if (match(tok0, w_DAMPLIMIT)) + { hyd->DampLimit = y; return (0); } @@ -1992,41 +1990,51 @@ int optionvalue(EN_Project *pr, int n) return 0; } + /* Check for pressure dependent demand params */ + else if (match(tok0, w_MINIMUM)) + { + if (y < 0.0) return 213; + hyd->Pmin = y; + return 0; + } + else if (match(tok0, w_REQUIRED)) + { + if (y < 0.0) return 213; + hyd->Preq = y; + return 0; + } + else if (match(tok0, w_PRESSURE)) + { + if (y < 0.0) return 213; + hyd->Pexp = y; + return 0; + } + /* All other options must be > 0 */ - if (y <= 0.0) - return (213); + if (y <= 0.0) return (213); /* Assign value to specified option */ - if (match(tok0, w_VISCOSITY)) - hyd->Viscos = y; /* Viscosity */ - else if (match(tok0, w_SPECGRAV)) - hyd->SpGrav = y; /* Spec. gravity */ - else if (match(tok0, w_TRIALS)) - hyd->MaxIter = (int)y; /* Max. trials */ + if (match(tok0, w_VISCOSITY)) hyd->Viscos = y; /* Viscosity */ + else if (match(tok0, w_SPECGRAV)) hyd->SpGrav = y; /* Spec. gravity */ + else if (match(tok0, w_TRIALS)) hyd->MaxIter = (int)y; /* Max. trials */ else if (match(tok0, w_ACCURACY)) /* Accuracy */ { y = MAX(y, 1.e-5); y = MIN(y, 1.e-1); hyd->Hacc = y; } - else if (match(tok0, w_HTOL)) - hyd->Htol = y; - else if (match(tok0, w_QTOL)) - hyd->Qtol = y; - else if (match(tok0, w_RQTOL)) { - if (y >= 1.0) - return (213); + else if (match(tok0, w_HTOL)) hyd->Htol = y; + else if (match(tok0, w_QTOL)) hyd->Qtol = y; + else if (match(tok0, w_RQTOL)) + { + if (y >= 1.0) return (213); hyd->RQtol = y; - } else if (match(tok0, w_CHECKFREQ)) - hyd->CheckFreq = (int)y; - else if (match(tok0, w_MAXCHECK)) - hyd->MaxCheck = (int)y; - else if (match(tok0, w_EMITTER)) - hyd->Qexp = 1.0 / y; - else if (match(tok0, w_DEMAND)) - hyd->Dmult = y; - else - return (201); + } + else if (match(tok0, w_CHECKFREQ)) hyd->CheckFreq = (int)y; + else if (match(tok0, w_MAXCHECK)) hyd->MaxCheck = (int)y; + else if (match(tok0, w_EMITTER)) hyd->Qexp = 1.0 / y; + else if (match(tok0, w_DEMAND)) hyd->Dmult = y; + else return (201); return (0); } /* end of optionvalue */ @@ -2091,7 +2099,8 @@ int getpumpcurve(EN_Project *pr, int n) return (0); } -int powercurve(double h0, double h1, double h2, double q1, double q2, double *a, double *b, double *c) +int powercurve(double h0, double h1, double h2, double q1, double q2, + double *a, double *b, double *c) /* **--------------------------------------------------------- ** Input: h0 = shutoff head diff --git a/src/output.c b/src/output.c index 39687cf..d9ec660 100644 --- a/src/output.c +++ b/src/output.c @@ -19,14 +19,12 @@ AUTHOR: L. Rossman #else #include #endif -#include "epanet2.h" + #include "funcs.h" #include "text.h" #include "types.h" #include -#define EXTERN extern #include "hash.h" -#include "vars.h" /* write x[1] to x[n] to file */ size_t f_save(REAL4 *x, int n, FILE *file) { diff --git a/src/quality.c b/src/quality.c index 03e29b6..a7286db 100644 --- a/src/quality.c +++ b/src/quality.c @@ -54,15 +54,13 @@ AUTHOR: L. Rossman #else #include #endif + #include "hash.h" #include "text.h" #include "types.h" -#include "epanet2.h" #include "funcs.h" #include -#define EXTERN extern #include "mempool.h" -#include "vars.h" /* ** Macros to identify upstream & downstream nodes of a link diff --git a/src/report.c b/src/report.c index 8d2101e..80eb45a 100644 --- a/src/report.c +++ b/src/report.c @@ -32,15 +32,13 @@ formatted string S to the report file. #else #include #endif -#include "epanet2.h" + #include "funcs.h" #include "hash.h" #include "text.h" #include "types.h" #include #include -#define EXTERN extern -#include "vars.h" #undef WINDOWS #ifdef _WIN32 @@ -50,12 +48,13 @@ formatted string S to the report file. #define MAXCOUNT 10 /* Max. # of disconnected nodes listed */ -/* Defined in enumstxt.h in EPANET.C */ +/* Defined in enumstxt.h */ extern char *NodeTxt[]; extern char *LinkTxt[]; extern char *StatTxt[]; extern char *TstatTxt[]; extern char *RptFormTxt[]; +extern char *DemandModelTxt[]; typedef REAL4 *Pfloat; void writenodetable(EN_Project *pr, Pfloat *); @@ -224,6 +223,8 @@ void writesummary(EN_Project *pr) writeline(pr, s); sprintf(s, FMT25, RptFormTxt[hyd->Formflag]); writeline(pr, s); + sprintf(s, FMT25a, DemandModelTxt[hyd->DemandModel]); + writeline(pr, s); sprintf(s, FMT26, time->Hstep * pr->Ucf[TIME], rep->Field[TIME].Units); writeline(pr, s); sprintf(s, FMT27, hyd->Hacc); diff --git a/src/rules.c b/src/rules.c index 39d6a6a..b8d0518 100644 --- a/src/rules.c +++ b/src/rules.c @@ -29,13 +29,11 @@ AUTHOR: L. Rossman #else #include #endif -#include "epanet2.h" + #include "funcs.h" #include "hash.h" #include "text.h" #include "types.h" -#define EXTERN extern -#include "vars.h" enum Rulewords { r_RULE, @@ -114,7 +112,7 @@ int takeactions(EN_Project *pr); void clearactlist(rules_t *rules); void clearrules(EN_Project *pr); void ruleerrmsg(EN_Project *pr, int); -int writeRuleinInp(EN_Project *pr, FILE *f, int RuleIdx); +//int writeRuleinInp(EN_Project *pr, FILE *f, int RuleIdx); void initrules(rules_t *rules) /* diff --git a/src/text.h b/src/text.h index 85159a9..656b3c2 100755 --- a/src/text.h +++ b/src/text.h @@ -145,6 +145,12 @@ AUTHOR: L. Rossman #define w_FLOWCHANGE "FLOWCHANGE" #define w_HEADERROR "HEADERROR" +#define w_MODEL "MODEL" +#define w_DDA "DDA" +#define w_PDA "PDA" +#define w_REQUIRED "REQ" +#define w_EXPONENT "EXP" + #define w_SECONDS "SEC" #define w_MINUTES "MIN" #define w_HOURS "HOU" @@ -323,6 +329,9 @@ AUTHOR: L. Rossman #define t_perM3 " /m3" #define t_perMGAL "/Mgal" #define t_DIFFER "DIFFERENTIAL" +#define t_FIXED "Fixed Demands" +#define t_POWER "Power Function" +#define t_ORIFICE "Orifice Flow" /* ------------------ Format Messages ------------------*/ @@ -338,17 +347,12 @@ AUTHOR: L. Rossman "* Version %d.%d *" #define LOGO6 \ "******************************************************************" -#define FMT01 "\n... EPANET Version %d.%d\n" #define FMT02 "\n o Retrieving network data" -#define FMT03 "\n Correct syntax is:\n %s \n" #define FMT04 "\n Cannot use duplicate file names." #define FMT05 "\n Cannot open input file " #define FMT06 "\n Cannot open report file " #define FMT07 "\n Cannot open output file " #define FMT08 "\n Cannot open temporary output file" -#define FMT09 "\n\n... EPANET completed.\n" -#define FMT10 "\n\n... EPANET completed. There are warnings.\n" -#define FMT11 "\n\n... EPANET completed. There are errors.\n" #define FMT14 "\n o Computing hydraulics at hour " #define FMT15 "\n o Computing water quality at hour " #define FMT16 "\n o Transferring results to file" @@ -362,6 +366,7 @@ AUTHOR: L. Rossman #define FMT23 " Number of Pumps ................... %-d" #define FMT24 " Number of Valves .................. %-d" #define FMT25 " Headloss Formula .................. %s" +#define FMT25a " Nodal Demand Model ................ %s" #define FMT26 " Hydraulic Timestep ................ %-.2f %s" #define FMT27 " Hydraulic Accuracy ................ %-.6f" @@ -420,7 +425,8 @@ AUTHOR: L. Rossman /*** End of update ***/ #define FMT66 " maximum flow change = %.4f for Link %s" -#define FMT67 " maximum head error = %.4f for Link %s\n" +#define FMT67 " maximum flow change = %.4f for Node %s" +#define FMT68 " maximum head error = %.4f for Link %s\n" /* -------------------- Energy Report Table ------------------- */ #define FMT71 "Energy Usage:" diff --git a/src/types.h b/src/types.h index be6e6fa..d887c96 100755 --- a/src/types.h +++ b/src/types.h @@ -23,6 +23,8 @@ AUTHOR: L. Rossman #include "epanet2.h" #include "hash.h" #include "mempool.h" +#include "util/errormanager.h" + /*********************************************************/ /* All floats have been re-declared as doubles (7/3/07). */ @@ -299,6 +301,11 @@ typedef enum { MAX_ENERGY_STATS } EnergyStats; +typedef enum { + DDA, // Demand Driven Analysis + PDA // Pressure Driven Analysis +} DemandModelType; + /* ------------------------------------------------------ Global Data Structures @@ -760,7 +767,8 @@ typedef struct { typedef struct { double - *NodeDemand, /* Node actual demand */ + *NodeDemand, // Node actual total outflow + *DemandFlows, // Demand outflows *EmitterFlows, /* Emitter flows */ *LinkSetting, /* Link settings */ *LinkFlows, /* Link flows */ @@ -769,10 +777,13 @@ typedef struct { Qtol, /* Flow rate tolerance */ RQtol, /* Flow resistance tolerance */ Hexp, /* Exponent in headloss formula */ - Qexp, /* Exponent in orifice formula */ + Qexp, /* Exponent in emitter formula */ + Pexp, // Exponent in demand formula + Pmin, // Pressure needed for any demand + Preq, // Pressure needed for full demand Dmult, /* Demand multiplier */ - Hacc, /* Hydraulics solution accuracy */ + Hacc, /* Hydraulics solution accuracy */ FlowChangeLimit, /* Hydraulics flow change limit */ HeadErrorLimit, /* Hydraulics head error limit */ @@ -788,7 +799,8 @@ typedef struct { int DefPat, /* Default demand pattern */ - Epat; /* Energy cost time pattern */ + Epat, /* Energy cost time pattern */ + DemandModel; // Fixed or pressure dependent StatType *LinkStatus, /* Link status */ @@ -806,8 +818,10 @@ typedef struct { Formflag; /* Hydraulic formula flag */ /* Info about hydraulic solution */ - double relativeError; - int iterations; + double RelativeError; + double MaxHeadError; + double MaxFlowChange; + int Iterations; /* Flag used to halt taking further time steps */ int Haltflag; @@ -849,7 +863,7 @@ typedef struct { /* project wrapper */ -struct EN_Project { +typedef struct EN_Project { EN_Network network; /// the network description struct hydraulics_t hydraulics; @@ -871,8 +885,10 @@ struct EN_Project { Title[MAXTITLE][MAXMSG+1], /// Problem title MapFname[MAXFNAME+1]; /// Map file name + error_handle_t* error_handle; //Simple error manager + void (* viewprog) (char *); /* Pointer to progress viewing function */ -}; +} EN_Project; #endif diff --git a/src/util/errormanager.c b/src/util/errormanager.c new file mode 100644 index 0000000..abba233 --- /dev/null +++ b/src/util/errormanager.c @@ -0,0 +1,73 @@ +//----------------------------------------------------------------------------- +// +// errormanager.c +// +// Purpose: Provides a simple interface for managing runtime error messages. +// +// Date: 08/25/2017 +// +// Author: Michael E. Tryby +// US EPA - ORD/NRMRL +//----------------------------------------------------------------------------- +#include "errormanager.h" + +error_handle_t* new_errormanager(void (*p_error_message)(int, char*, int)) +// +// Purpose: Constructs a new error handle. +// +{ + error_handle_t* error_handle; + error_handle = (error_handle_t*)calloc(1, sizeof(error_handle_t)); + + if (error_handle != NULL) + error_handle->p_msg_lookup = p_error_message; + + return error_handle; +} + +void dst_errormanager(error_handle_t* error_handle) +// +// Purpose: Destroys the error handle. +// +{ + free(error_handle); +} + +int set_error(error_handle_t* error_handle, int errorcode) +// +// Purpose: Sets an error code in the handle. +// +{ + // If the error code is 0 no action is taken and 0 is returned. + // This is a feature not a bug. + if (errorcode) + error_handle->error_status = errorcode; + + return errorcode; +} + +char* check_error(error_handle_t* error_handle) +// +// Purpose: Returns the error message or NULL. +// +// Note: Caller must free memory allocated by check_error +// +{ + char* temp = NULL; + + if (error_handle->error_status != 0) { + temp = (char*) calloc(ERR_MAXMSG, sizeof(char)); + + if (temp) + error_handle->p_msg_lookup(error_handle->error_status, temp, ERR_MAXMSG); + } + return temp; +} + +void clear_error(error_handle_t* error_handle) +// +// Purpose: Clears the error from the handle. +// +{ + error_handle->error_status = 0; +} diff --git a/src/util/errormanager.h b/src/util/errormanager.h new file mode 100644 index 0000000..36ee969 --- /dev/null +++ b/src/util/errormanager.h @@ -0,0 +1,30 @@ +/* + * errormanager.h + * + * Created on: Aug 25, 2017 + * + * Author: Michael E. Tryby + * US EPA - ORD/NRMRL + */ + +#ifndef ERRORMANAGER_H_ +#define ERRORMANAGER_H_ + +#include +#include + +#define ERR_MAXMSG 256 + +typedef struct error_s { + int error_status; + void (*p_msg_lookup)(int, char*, int); +} error_handle_t; + +error_handle_t* new_errormanager(void (*p_error_message)(int, char*, int)); +void dst_errormanager(error_handle_t* error_handle); + +int set_error(error_handle_t* error_handle, int errorcode); +char* check_error(error_handle_t* error_handle); +void clear_error(error_handle_t* error_handle); + +#endif /* ERRORMANAGER_H_ */ diff --git a/src/vars.h b/src/vars.h index 04f540c..9c4c08f 100755 --- a/src/vars.h +++ b/src/vars.h @@ -11,6 +11,6 @@ // this single global variable is used only when the library is called in "legacy mode" // with the 2.1-style API. -EXTERN EN_Project *_defaultModel; +EXTERN void *_defaultModel; #endif diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 609d0b4..be6dc5a 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -37,8 +37,7 @@ foreach(testSrc ${TEST_SRCS}) #link to Boost libraries AND your targets and dependencies target_link_libraries(${testName} ${Boost_LIBRARIES} epanet epanet-output) - - #Finally add it to test execution - + #Finally add it to test execution #Notice the WORKING_DIRECTORY and COMMAND add_test(NAME ${testName} COMMAND ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/${testName} diff --git a/tests/data/example1.out b/tests/data/example1.out new file mode 100644 index 0000000..416c98b Binary files /dev/null and b/tests/data/example1.out differ diff --git a/tests/data/net1.inp b/tests/data/net1.inp new file mode 100644 index 0000000..4df5bbf --- /dev/null +++ b/tests/data/net1.inp @@ -0,0 +1,178 @@ +[TITLE] + EPANET Example Network 1 +A simple example of modeling chlorine decay. Both bulk and +wall reactions are included. + +[JUNCTIONS] +;ID Elev Demand Pattern + 10 710 0 ; + 11 710 150 ; + 12 700 150 ; + 13 695 100 ; + 21 700 150 ; + 22 695 200 ; + 23 690 150 ; + 31 700 100 ; + 32 710 100 ; + +[RESERVOIRS] +;ID Head Pattern + 9 800 ; + +[TANKS] +;ID Elevation InitLevel MinLevel MaxLevel Diameter MinVol VolCurve + 2 850 120 100 150 50.5 0 ; + +[PIPES] +;ID Node1 Node2 Length Diameter Roughness MinorLoss Status + 10 10 11 10530 18 100 0 Open ; + 11 11 12 5280 14 100 0 Open ; + 12 12 13 5280 10 100 0 Open ; + 21 21 22 5280 10 100 0 Open ; + 22 22 23 5280 12 100 0 Open ; + 31 31 32 5280 6 100 0 Open ; + 110 2 12 200 18 100 0 Open ; + 111 11 21 5280 10 100 0 Open ; + 112 12 22 5280 12 100 0 Open ; + 113 13 23 5280 8 100 0 Open ; + 121 21 31 5280 8 100 0 Open ; + 122 22 32 5280 6 100 0 Open ; + +[PUMPS] +;ID Node1 Node2 Parameters + 9 9 10 HEAD 1 ; + +[VALVES] +;ID Node1 Node2 Diameter Type Setting MinorLoss + +[TAGS] + +[DEMANDS] +;Junction Demand Pattern Category + +[STATUS] +;ID Status/Setting + +[PATTERNS] +;ID Multipliers +;Demand Pattern + 1 1.0 1.2 1.4 1.6 1.4 1.2 + 1 1.0 0.8 0.6 0.4 0.6 0.8 + +[CURVES] +;ID X-Value Y-Value +;PUMP: Pump Curve for Pump 9 + 1 1500 250 + +[CONTROLS] + LINK 9 OPEN IF NODE 2 BELOW 110 + LINK 9 CLOSED IF NODE 2 ABOVE 140 + + +[RULES] + +[ENERGY] + Global Efficiency 75 + Global Price 0.0 + Demand Charge 0.0 + +[EMITTERS] +;Junction Coefficient + +[QUALITY] +;Node InitQual + 10 0.5 + 11 0.5 + 12 0.5 + 13 0.5 + 21 0.5 + 22 0.5 + 23 0.5 + 31 0.5 + 32 0.5 + 9 1.0 + 2 1.0 + +[SOURCES] +;Node Type Quality Pattern + +[REACTIONS] +;Type Pipe/Tank Coefficient + + +[REACTIONS] + Order Bulk 1 + Order Tank 1 + Order Wall 1 + Global Bulk -.5 + Global Wall -1 + Limiting Potential 0.0 + Roughness Correlation 0.0 + +[MIXING] +;Tank Model + +[TIMES] + Duration 24:00 + Hydraulic Timestep 1:00 + Quality Timestep 0:05 + Pattern Timestep 2:00 + Pattern Start 0:00 + Report Timestep 1:00 + Report Start 0:00 + Start ClockTime 12 am + Statistic None + +[REPORT] + Status Yes + Summary No + Page 0 + +[OPTIONS] + Units GPM + Headloss H-W + Specific Gravity 1.0 + Viscosity 1.0 + Trials 40 + Accuracy 0.001 + CHECKFREQ 2 + MAXCHECK 10 + DAMPLIMIT 0 + Unbalanced Continue 10 + Pattern 1 + Demand Multiplier 1.0 + Emitter Exponent 0.5 + Quality Chlorine mg/L + Diffusivity 1.0 + Tolerance 0.01 + +[COORDINATES] +;Node X-Coord Y-Coord + 10 20.00 70.00 + 11 30.00 70.00 + 12 50.00 70.00 + 13 70.00 70.00 + 21 30.00 40.00 + 22 50.00 40.00 + 23 70.00 40.00 + 31 30.00 10.00 + 32 50.00 10.00 + 9 10.00 70.00 + 2 50.00 90.00 + +[VERTICES] +;Link X-Coord Y-Coord + +[LABELS] +;X-Coord Y-Coord Label & Anchor Node + 6.99 73.63 "Source" + 13.48 68.13 "Pump" + 43.85 91.21 "Tank" + +[BACKDROP] + DIMENSIONS 7.00 6.00 73.00 94.00 + UNITS None + FILE + OFFSET 0.00 0.00 + +[END] diff --git a/tests/test_output.cpp b/tests/test_output.cpp index cd55f4f..78f55ed 100644 --- a/tests/test_output.cpp +++ b/tests/test_output.cpp @@ -22,8 +22,7 @@ #include "epanet_output.h" - -#define DATA_PATH "./net1.out" +#define DATA_PATH "./example1.out" using namespace std; @@ -117,7 +116,6 @@ struct Fixture{ float* array; int array_dim; - }; BOOST_AUTO_TEST_SUITE(test_output_fixture) @@ -144,6 +142,15 @@ BOOST_FIXTURE_TEST_CASE(test_getNetSize, Fixture) ENR_free((void**)&i_array); } +BOOST_FIXTURE_TEST_CASE(test_getUnits, Fixture) { + int flag; + + error = ENR_getUnits(p_handle, ENR_qualUnits, &flag); + BOOST_REQUIRE(error == 0); + + BOOST_CHECK_EQUAL(flag, ENR_MGL); +} + BOOST_FIXTURE_TEST_CASE(test_getElementName, Fixture) { char* name = new char[MAXID]; int length, index = 1; @@ -179,7 +186,6 @@ BOOST_FIXTURE_TEST_CASE(test_getNodeAttribute, Fixture) { std::vector ref_vec; ref_vec.assign(ref_array, ref_array + ref_dim); - std::vector test_vec; test_vec.assign(array, array + array_dim); diff --git a/tests/test_toolkit.cpp b/tests/test_toolkit.cpp new file mode 100644 index 0000000..6ca6322 --- /dev/null +++ b/tests/test_toolkit.cpp @@ -0,0 +1,207 @@ +// +// test_epanet_toolkit.cpp +// +// Date Created: January 24, 2018 +// +// Author: Michael E. Tryby +// US EPA - ORD/NRMRL +// + +//#define BOOST_TEST_DYN_LINK + +#define BOOST_TEST_MODULE "toolkit" +#include + +#include +#include "epanet2.h" + +// NOTE: Project Home needs to be updated to run unit test +#define DATA_PATH_INP "./net1.inp" +#define DATA_PATH_RPT "./test.rpt" +#define DATA_PATH_OUT "./test.out" + +using namespace std; + + +BOOST_AUTO_TEST_SUITE (test_toolkit) + +BOOST_AUTO_TEST_CASE (test_alloc_free) +{ + int error = 0; + EN_ProjectHandle ph = NULL; + + error = EN_createproject(&ph); + + BOOST_REQUIRE(error == 0); + BOOST_CHECK(ph != NULL); + + error = EN_deleteproject(&ph); + + BOOST_REQUIRE(error == 0); + BOOST_CHECK(ph == NULL); +} + +BOOST_AUTO_TEST_CASE (test_open_close) +{ + EN_ProjectHandle ph = NULL; + EN_createproject(&ph); + + std::string path_inp = std::string(DATA_PATH_INP); + std::string path_rpt = std::string(DATA_PATH_RPT); + std::string path_out = std::string(DATA_PATH_OUT); + + int error = EN_open(ph, path_inp.c_str(), path_rpt.c_str(), path_out.c_str()); + BOOST_REQUIRE(error == 0); + + error = EN_close(ph); + BOOST_REQUIRE(error == 0); + + EN_deleteproject(&ph); +} + +BOOST_AUTO_TEST_CASE(test_epanet) +{ + std::string path_inp = std::string(DATA_PATH_INP); + std::string path_rpt = std::string(DATA_PATH_RPT); + std::string path_out = std::string(DATA_PATH_OUT); + + int error = ENepanet(path_inp.c_str(), path_rpt.c_str(), path_out.c_str(), NULL); + BOOST_REQUIRE(error == 0); +} + +BOOST_AUTO_TEST_SUITE_END() + + +struct Fixture{ + Fixture() { + path_inp = std::string(DATA_PATH_INP); + path_rpt = std::string(DATA_PATH_RPT); + path_out = std::string(DATA_PATH_OUT); + + EN_createproject(&ph); + error = EN_open(ph, path_inp.c_str(), path_rpt.c_str(), path_out.c_str()); + + } + + ~Fixture() { + error = EN_close(ph); + EN_deleteproject(&ph); + } + + std::string path_inp; + std::string path_rpt; + std::string path_out; + + int error; + EN_ProjectHandle ph; + +}; + +BOOST_AUTO_TEST_SUITE(test_epanet_fixture) + +BOOST_FIXTURE_TEST_CASE(test_epanet, Fixture) +{ + error = EN_solveH(ph); + BOOST_REQUIRE(error == 0); + + error = EN_solveQ(ph); + BOOST_REQUIRE(error == 0); + + error = EN_report(ph); + BOOST_REQUIRE(error == 0); +} + +BOOST_FIXTURE_TEST_CASE(test_hyd_step, Fixture) +{ + int flag = 00; + long t, tstep; + + error = EN_openH(ph); + BOOST_REQUIRE(error == 0); + + error = EN_initH(ph, flag); + BOOST_REQUIRE(error == 0); + + do { + error = EN_runH(ph, &t); + BOOST_REQUIRE(error == 0); + + error = EN_nextH(ph, &tstep); + BOOST_REQUIRE(error == 0); + + } while (tstep > 0); + + error = EN_closeH(ph); + BOOST_REQUIRE(error == 0); +} + +BOOST_FIXTURE_TEST_CASE(test_qual_step, Fixture) +{ + int flag = 0; + long t, tstep; + + error = EN_solveH(ph); + BOOST_REQUIRE(error == 0); + + error = EN_openQ(ph); + BOOST_REQUIRE(error == 0); + + error = EN_initQ(ph, flag); + BOOST_REQUIRE(error == 0); + + do { + error = EN_runQ(ph, &t); + BOOST_REQUIRE(error == 0); + + error = EN_nextQ(ph, &tstep); + BOOST_REQUIRE(error == 0); + + } while (tstep > 0); + + error = EN_closeQ(ph); + BOOST_REQUIRE(error == 0); +} + +BOOST_FIXTURE_TEST_CASE(test_progressive_stepping, Fixture) +{ + int flag = EN_NOSAVE; + long t, tstep_h, tstep_q; + + error = EN_openH(ph); + BOOST_REQUIRE(error == 0); + + error = EN_initH(ph, flag); + BOOST_REQUIRE(error == 0); + + error = EN_openQ(ph); + BOOST_REQUIRE(error == 0); + + error = EN_initQ(ph, flag); + BOOST_REQUIRE(error == 0); + + do { + + + error = EN_runH(ph, &t); + BOOST_REQUIRE(error == 0); + + error = EN_runQ(ph, &t); + BOOST_REQUIRE(error == 0); + + error = EN_nextH(ph, &tstep_h); + BOOST_REQUIRE(error == 0); + + error = EN_nextQ(ph, &tstep_q); + BOOST_REQUIRE(error == 0); + + } while (tstep_h > 0); + + error = EN_closeH(ph); + BOOST_REQUIRE(error == 0); + + error = EN_closeQ(ph); + BOOST_REQUIRE(error == 0); + +} + +BOOST_AUTO_TEST_SUITE_END() diff --git a/tools/before-test.cmd b/tools/before-test.cmd index 70a7af9..0f64cd9 100644 --- a/tools/before-test.cmd +++ b/tools/before-test.cmd @@ -25,8 +25,8 @@ set SCRIPT_HOME=%~dp0 set TEST_HOME=%~1 -set EXAMPLES_VER=1.0.2-dev.1 -set BENCHMARK_VER=220dev1 +set EXAMPLES_VER=1.0.2-dev.2 +set BENCHMARK_VER=220dev2 set TESTFILES_URL=https://github.com/OpenWaterAnalytics/epanet-example-networks/archive/v%EXAMPLES_VER%.zip diff --git a/tools/epanet-output/include/epanet_output.h b/tools/epanet-output/include/epanet_output.h index 0066d16..93a8425 100644 --- a/tools/epanet-output/include/epanet_output.h +++ b/tools/epanet-output/include/epanet_output.h @@ -18,22 +18,42 @@ typedef void* ENR_Handle; typedef enum { - ENR_node = 1, - ENR_link = 2 + ENR_node = 1, + ENR_link = 2 } ENR_ElementType; typedef enum { - ENR_getSeries = 1, - ENR_getAttribute = 2, - ENR_getResult = 3, - ENR_getReacts = 4, - ENR_getEnergy = 5 -} ENR_ApiFunction; + ENR_flowUnits = 1, + ENR_pressUnits = 2, + ENR_qualUnits = 3 +} ENR_Units; typedef enum { - ENR_flowUnits = 1, - ENR_pressUnits = 2 -} ENR_Units; + ENR_CFS = 0, + ENR_GPM = 1, + ENR_MGD = 2, + ENR_IMGD = 3, + ENR_AFD = 4, + ENR_LPS = 5, + ENR_LPM = 6, + ENR_MLD = 7, + ENR_CMH = 8, + ENR_CMD = 9 +} ENR_FlowUnits; + +typedef enum { + ENR_PSI = 0, + ENR_MTR = 1, + ENR_KPA = 2 +} ENR_PressUnits; + +typedef enum { + ENR_NONE = 0, + ENR_MGL = 1, + ENR_UGL = 2, + ENR_HOURS = 3, + ENR_PRCNT = 4 +} ENR_QualUnits; typedef enum { ENR_reportStart = 1, @@ -43,21 +63,21 @@ typedef enum { }ENR_Time; typedef enum { - ENR_demand = 1, - ENR_head = 2, - ENR_pressure = 3, - ENR_quality = 4 + ENR_demand = 1, + ENR_head = 2, + ENR_pressure = 3, + ENR_quality = 4 } ENR_NodeAttribute; typedef enum { - ENR_flow = 1, - ENR_velocity = 2, - ENR_headloss = 3, - ENR_avgQuality = 4, - ENR_status = 5, - ENR_setting = 6, - ENR_rxRate = 7, - ENR_frctnFctr = 8 + ENR_flow = 1, + ENR_velocity = 2, + ENR_headloss = 3, + ENR_avgQuality = 4, + ENR_status = 5, + ENR_setting = 6, + ENR_rxRate = 7, + ENR_frctnFctr = 8 } ENR_LinkAttribute; diff --git a/tools/epanet-output/src/epanet_output.c b/tools/epanet-output/src/epanet_output.c index 486ef81..741304f 100644 --- a/tools/epanet-output/src/epanet_output.c +++ b/tools/epanet-output/src/epanet_output.c @@ -57,7 +57,8 @@ #define MINNREC 14 // Minimum allowable number of records #define PROLOGUE 884 // Preliminary fixed length section of header -#define MAXID_P1 32 // Max. # characters in ID name +#define MAXID_P1 32 // EPANET max characters in ID name PLUS 1 +#define MAXMSG_P1 80 // EPANET max characters in message text PLUS 1 #define NELEMENTTYPES 5 // Number of element types #define NENERGYRESULTS 6 // Number of energy results @@ -303,6 +304,8 @@ int DLLEXPORT ENR_getUnits(ENR_Handle p_handle, ENR_Units code, int* unitFlag) */ { int errorcode = 0; + F_OFF offset; + char temp[MAXID_P1]; data_t* p_data; *unitFlag = -1; @@ -315,15 +318,37 @@ int DLLEXPORT ENR_getUnits(ENR_Handle p_handle, ENR_Units code, int* unitFlag) switch (code) { case ENR_flowUnits: - fseek(p_data->file, 9*WORDSIZE, SEEK_SET); + _fseek(p_data->file, 9*WORDSIZE, SEEK_SET); fread(unitFlag, WORDSIZE, 1, p_data->file); break; case ENR_pressUnits: - fseek(p_data->file, 10*WORDSIZE, SEEK_SET); + _fseek(p_data->file, 10*WORDSIZE, SEEK_SET); fread(unitFlag, WORDSIZE, 1, p_data->file); break; + case ENR_qualUnits: + offset = 7*WORDSIZE; + _fseek(p_data->file, offset, SEEK_SET); + fread(unitFlag, WORDSIZE, 1, p_data->file); + + if (*unitFlag == 0) *unitFlag = ENR_NONE; + + else if (*unitFlag == 1) { + offset = 15*WORDSIZE + 3*MAXMSG_P1 + 2*(MAXFNAME+1) + MAXID_P1; + _fseek(p_data->file, offset, SEEK_SET); + fread(temp, MAXID_P1, 1, p_data->file); + + if (!strcmp(temp, "mg/L")) *unitFlag = ENR_MGL; + else *unitFlag = ENR_UGL; + } + + else if (*unitFlag == 2) *unitFlag = ENR_HOURS; + + else *unitFlag = ENR_PRCNT; + + break; + default: errorcode = 421; } } @@ -378,6 +403,12 @@ int DLLEXPORT ENR_getTimes(ENR_Handle p_handle, ENR_Time code, int* time) return set_error(p_data->error_handle, errorcode); } +int DLLEXPORT ENR_getChemData(ENR_Handle p_handle, char** name, int* length) + +{ + return 0; +} + int DLLEXPORT ENR_getElementName(ENR_Handle p_handle, ENR_ElementType type, int elementIndex, char** name, int* length) /*------------------------------------------------------------------------ @@ -815,7 +846,7 @@ void errorLookup(int errcode, char* dest_msg, int dest_len) default: msg = ERRERR; } - strncpy(dest_msg, msg, MAXMSG); + strncpy(dest_msg, msg, MSG_MAXLEN); } int validateFile(ENR_Handle p_handle) diff --git a/tools/epanet-output/src/messages.h b/tools/epanet-output/src/messages.h index 78725a1..bc04a7d 100644 --- a/tools/epanet-output/src/messages.h +++ b/tools/epanet-output/src/messages.h @@ -10,7 +10,7 @@ #ifndef MESSAGES_H_ #define MESSAGES_H_ /*------------------- Error Messages --------------------*/ -#define MAXMSG 53 +#define MSG_MAXLEN 53 #define WARN10 "Warning: model run issued warnings" diff --git a/tools/epanet-output/test/epanet_output_test.py b/tools/epanet-output/test/epanet_output_test.py deleted file mode 100644 index fa23d62..0000000 --- a/tools/epanet-output/test/epanet_output_test.py +++ /dev/null @@ -1,102 +0,0 @@ -# -# epanet_output_test.py -# -# Created: 11/8/2017 -# Author: Michael E. Tryby -# US EPA - ORD/NRMRL -# -# Unit testing for EPANET Output API using pytest. -# - -import pytest -import numpy as np - -import epanet_output as oapi - -from data import OUTPUT_FILE_EXAMPLE1 - -@pytest.fixture() -def enr_handle(request): - _handle = oapi.enr_init() - oapi.enr_open(_handle, OUTPUT_FILE_EXAMPLE1) - - def enr_close(): - oapi.enr_close() - - request.addfinalizer(enr_close) - return _handle - - -def test_get_times(enr_handle): - num_periods = oapi.enr_get_times(enr_handle, oapi.Time.NUM_PERIODS) - assert num_periods == 25 - - -# def test_get_size(file_path): -# handle = oapi.enr_init() -# oapi.enr_open(handle, file_path) -# -# size = oapi.enr_get_net_size(handle) -# -# print(size) -# -# handle = oapi.enr_close() -# -# def test_get_names(file_path): -# handle = oapi.enr_init() -# oapi.enr_open(handle, file_path) -# -# name = oapi.enr_get_element_name(handle, oapi.ElementType.NODE, 10) -# -# print(name) -# -# handle = oapi.enr_close() -# -# def test_get_energy(file_path): -# handle = oapi.enr_init() -# oapi.enr_open(handle, file_path) -# -# result = oapi.enr_get_energy_usage(handle, 1) -# -# print(result) -# -# handle = oapi.enr_close() -# -# def test_get_react(file_path): -# handle = oapi.enr_init() -# oapi.enr_open(handle, file_path) -# -# result = oapi.enr_get_net_reacts(handle) -# -# print(result) -# -# handle = oapi.enr_close() -# -def test_get_node_attribute(enr_handle): - ref_array = np.array([ 1., 0.44407997, 0.43766347, 0.42827705, 0.41342604, - 0.42804748, 0.44152543, 0.40502965, 0.38635802, 1., 0.96745253]) - - array = oapi.enr_get_node_attribute(enr_handle, 1, oapi.NodeAttribute.QUALITY) - assert len(array) == 11 - assert np.allclose(array, ref_array) - -def test_get_link_attribute(enr_handle): - ref_array = np.array([ 1848.58117676, 1220.42736816, 130.11161804, - 187.68930054, 119.88839722, 40.46448898, -748.58111572, 478.15377808, - 191.73458862, 30.11160851, 140.4644928, 59.53551483, 1848.58117676]) - - array = oapi.enr_get_link_attribute(enr_handle, 1, oapi.LinkAttribute.FLOW) - assert len(array) == 13 - assert np.allclose(array, ref_array) - -# if __name__ == "__main__": -# -# file_path = "M:\\net mydocuments\\EPA Projects\\EPAnet Examples\\net1.out" -# test_get_times(file_path) -# test_get_size(file_path) -# test_get_names(file_path) -# test_get_energy(file_path) -# test_get_react(file_path) -# test_get_node_attribute(file_path) -# test_get_link_attribute(file_path) -# \ No newline at end of file diff --git a/tools/run-nrtest.cmd b/tools/run-nrtest.cmd index e7f55a4..8efb44b 100644 --- a/tools/run-nrtest.cmd +++ b/tools/run-nrtest.cmd @@ -18,12 +18,12 @@ setlocal set NRTEST_SCRIPT_PATH=%~1 set TEST_SUITE_PATH=%~2 -set BENCHMARK_VER=220dev1 +set BENCHMARK_VER=220dev2 set NRTEST_EXECUTE_CMD=python %NRTEST_SCRIPT_PATH%\nrtest execute set TEST_APP_PATH=apps\epanet-%3.json -set TESTS=tests\examples tests\exeter tests\large tests\network_one tests\small tests\tanks tests\valves +set TESTS=tests\examples tests\exeter tests\large tests\network_one tests\press_depend tests\small tests\tanks tests\valves set TEST_OUTPUT_PATH=benchmark\epanet-%3 set NRTEST_COMPARE_CMD=python %NRTEST_SCRIPT_PATH%\nrtest compare diff --git a/win_build/WinSDK/Makefile.bat b/win_build/WinSDK/Makefile.bat index 22fb38f..91ffc95 100644 --- a/win_build/WinSDK/Makefile.bat +++ b/win_build/WinSDK/Makefile.bat @@ -21,9 +21,9 @@ Find /i "x86" < checkOS.tmp > StringCheck.tmp If %ERRORLEVEL% == 1 ( CALL "%SDK_PATH%bin\"SetEnv.cmd /x64 /release rem : create EPANET2.DLL - cl -o epanet2.dll epanet.c hash.c hydraul.c hydcoeffs.c hydsolver.c inpfile.c input1.c input2.c input3.c mempool.c output.c quality.c report.c rules.c smatrix.c genmmd.c /I ..\include /I ..\run /link /DLL + cl -o epanet2.dll epanet.c util\errormanager.c hash.c hydraul.c hydcoeffs.c hydstatus.c hydsolver.c inpfile.c input1.c input2.c input3.c mempool.c output.c quality.c report.c rules.c smatrix.c genmmd.c /I ..\include /I ..\run /link /DLL rem : create EPANET2.EXE - cl -o epanet2.exe epanet.c ..\run\main.c hash.c hydraul.c hydcoeffs.c hydsolver.c inpfile.c input1.c input2.c input3.c mempool.c output.c quality.c report.c rules.c smatrix.c genmmd.c /I ..\include /I ..\run /I ..\src /link + cl -o epanet2.exe epanet.c util\errormanager.c ..\run\main.c hash.c hydraul.c hydcoeffs.c hydstatus.c hydsolver.c inpfile.c input1.c input2.c input3.c mempool.c output.c quality.c report.c rules.c smatrix.c genmmd.c /I ..\include /I ..\run /I ..\src /link md "%Build_PATH%"\64bit move /y "%SRC_PATH%"\*.dll "%Build_PATH%"\64bit move /y "%SRC_PATH%"\*.exe "%Build_PATH%"\64bit @@ -35,9 +35,9 @@ rem : 32 bit with DEF CALL "%SDK_PATH%bin\"SetEnv.cmd /x86 /release echo "32 bit with epanet2.def mapping" rem : create EPANET2.DLL -cl -o epanet2.dll epanet.c hash.c hydraul.c hydcoeffs.c hydsolver.c inpfile.c input1.c input2.c input3.c mempool.c output.c quality.c report.c rules.c smatrix.c genmmd.c /I ..\include /I ..\run /link /DLL /def:..\win_build\WinSDK\epanet2.def /MAP +cl -o epanet2.dll epanet.c util\errormanager.c hash.c hydraul.c hydcoeffs.c hydstatus.c hydsolver.c inpfile.c input1.c input2.c input3.c mempool.c output.c quality.c report.c rules.c smatrix.c genmmd.c /I ..\include /I ..\run /link /DLL /def:..\win_build\WinSDK\epanet2.def /MAP rem : create EPANET2.EXE -cl -o epanet2.exe epanet.c ..\run\main.c hash.c hydraul.c hydcoeffs.c hydsolver.c inpfile.c input1.c input2.c input3.c mempool.c output.c quality.c report.c rules.c smatrix.c genmmd.c /I ..\include /I ..\run /I ..\src /link +cl -o epanet2.exe epanet.c util\errormanager.c ..\run\main.c hash.c hydraul.c hydcoeffs.c hydstatus.c hydsolver.c inpfile.c input1.c input2.c input3.c mempool.c output.c quality.c report.c rules.c smatrix.c genmmd.c /I ..\include /I ..\run /I ..\src /link md "%Build_PATH%"\32bit move /y "%SRC_PATH%"\*.dll "%Build_PATH%"\32bit move /y "%SRC_PATH%"\*.exe "%Build_PATH%"\32bit @@ -51,4 +51,3 @@ del "%SRC_PATH%"\*.map del "%SRC_PATH%"\*.tmp cd "%Build_PATH%" - diff --git a/win_build/WinSDK/epanet2.def b/win_build/WinSDK/epanet2.def index e6a674a..46c82c2 100644 --- a/win_build/WinSDK/epanet2.def +++ b/win_build/WinSDK/epanet2.def @@ -93,4 +93,7 @@ EXPORTS ENdeletelink = _ENdeletelink@4 ENdeletenode = _ENdeletenode@4 ENsetlinktype = _ENsetlinktype@8 - ENgetcurvetype = _ENgetcurvetype@8 \ No newline at end of file + ENgetdemandmodel = _ENgetdemandmodel@16 + ENsetdemandmodel = _ENsetdemandmodel@16 + ENgetcurvetype = _ENgetcurvetype@8 +