From 691c65878b87ddf851805b194e562cead53d5980 Mon Sep 17 00:00:00 2001 From: Michael Tryby Date: Fri, 12 Jan 2018 09:46:32 -0500 Subject: [PATCH] Initial commit of toolkit SWIG wrapper work --- include/epanet2.h | 37 ++-- src/epanet.c | 54 ++++-- src/input2.c | 3 +- src/vars.h | 2 +- tools/epanet-toolkit/setup.py | 40 ++++ tools/epanet-toolkit/src/epanet_toolkit.i | 71 +++++++ tools/epanet-toolkit/test/data/Net1.inp | 178 ++++++++++++++++++ .../test/epanet_toolkit_test.py | 49 +++++ tools/epanet-toolkit/test/main.py | 13 ++ .../test/test_epanet_toolkit.cpp | 34 ++++ 10 files changed, 453 insertions(+), 28 deletions(-) create mode 100644 tools/epanet-toolkit/setup.py create mode 100644 tools/epanet-toolkit/src/epanet_toolkit.i create mode 100644 tools/epanet-toolkit/test/data/Net1.inp create mode 100644 tools/epanet-toolkit/test/epanet_toolkit_test.py create mode 100644 tools/epanet-toolkit/test/main.py create mode 100644 tools/epanet-toolkit/test/test_epanet_toolkit.cpp diff --git a/include/epanet2.h b/include/epanet2.h index c9fe158..9392137 100644 --- a/include/epanet2.h +++ b/include/epanet2.h @@ -43,7 +43,7 @@ #ifndef DLLEXPORT #ifdef WINDOWS #ifdef __cplusplus - #define DLLEXPORT extern "C" __declspec(dllexport) + #define DLLEXPORT __declspec(dllexport) #else #define DLLEXPORT __declspec(dllexport) __stdcall #endif // __cplusplus @@ -266,6 +266,7 @@ extern "C" { /** @brief The EPANET Project wrapper object */ + typedef void* EN_ProjectHandle; typedef struct EN_Project EN_Project; typedef struct EN_Pattern EN_Pattern; typedef struct EN_Curve EN_Curve; @@ -1117,14 +1118,22 @@ extern "C" { 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_alloc(EN_ProjectHandle *ph); + int DLLEXPORT EN_free(EN_ProjectHandle ph); + + 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_ProjectHandle ph, const char *inpFile, + const char *rptFile, const 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_close(EN_ProjectHandle ph); + int DLLEXPORT EN_solveH(EN_ProjectHandle ph); + int DLLEXPORT EN_saveH(EN_Project *p); int DLLEXPORT EN_openH(EN_Project *p); int DLLEXPORT EN_initH(EN_Project *p, int EN_SaveOption); @@ -1133,7 +1142,9 @@ extern "C" { 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_solveQ(EN_ProjectHandle ph); + 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); @@ -1141,7 +1152,9 @@ extern "C" { 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_report(EN_ProjectHandle ph); + 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); @@ -1214,10 +1227,6 @@ extern "C" { int DLLEXPORT EN_deletelink(EN_Project *p, int linkIndex); - - - - #if defined(__cplusplus) } #endif diff --git a/src/epanet.c b/src/epanet.c index 6059fc0..be1801a 100644 --- a/src/epanet.c +++ b/src/epanet.c @@ -151,30 +151,40 @@ 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(char *f1, char *f2, char *f3, void (*pviewprog)(char *)) +{ int errcode = 0; + EN_Project* _p; + + _p = (EN_Project*)_defaultModel; + ERRCODE(EN_alloc(&_defaultModel)); ERRCODE(EN_open(_defaultModel, f1, f2, f3)); - _defaultModel->viewprog = pviewprog; - if (_defaultModel->out_files.Hydflag != USE) { + + _p->viewprog = pviewprog; + if (_p->out_files.Hydflag != USE) { ERRCODE(EN_solveH(_defaultModel)); } + ERRCODE(EN_solveQ(_defaultModel)); ERRCODE(EN_report(_defaultModel)); EN_close(_defaultModel); EN_free(_defaultModel); + return (errcode); } + int DLLEXPORT ENopen(char *f1, char *f2, char *f3) { int errcode = 0; ERRCODE(EN_alloc(&_defaultModel)); EN_open(_defaultModel, f1, f2, f3); return (errcode); } + int DLLEXPORT ENsaveinpfile(char *filename) { return EN_saveinpfile(_defaultModel, filename); } + int DLLEXPORT ENclose() { return EN_close(_defaultModel); } int DLLEXPORT ENsolveH() { return EN_solveH(_defaultModel); } int DLLEXPORT ENsaveH() { return EN_saveH(_defaultModel); } @@ -446,16 +456,21 @@ int DLLEXPORT ENdeletenode(int index) { */ /// allocate a project pointer -int DLLEXPORT EN_alloc(EN_Project **p) +int DLLEXPORT EN_alloc(EN_ProjectHandle *ph) { EN_Project *project = calloc(1, sizeof(EN_Project)); - *p = project; + *ph = project; + return 0; } -int DLLEXPORT EN_free(EN_Project *p) +int DLLEXPORT EN_free(EN_ProjectHandle ph) { + EN_Project* p; + p = (EN_Project*)ph; + free(p); + return 0; } @@ -518,7 +533,7 @@ int DLLEXPORT EN_init(EN_Project *pr, char *f2, char *f3, return (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 @@ -537,6 +552,9 @@ int DLLEXPORT EN_open(EN_Project *p, char *f1, char *f2, char *f3) _fpreset(); #endif + EN_Project* p; + p = (EN_Project*)ph; + /* Set system flags */ p->Openflag = FALSE; p->hydraulics.OpenHflag = FALSE; @@ -605,7 +623,7 @@ int DLLEXPORT EN_saveinpfile(EN_Project *p, char *filename) return (saveinpfile(p, filename)); } -int DLLEXPORT EN_close(EN_Project *p) +int DLLEXPORT EN_close(EN_ProjectHandle ph) /*---------------------------------------------------------------- ** Input: none ** Output: none @@ -615,6 +633,9 @@ int DLLEXPORT EN_close(EN_Project *p) */ { out_file_t *out; + EN_Project* p; + + p = (EN_Project*)ph; if (p->Openflag) { writetime(p, FMT105); @@ -667,7 +688,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 @@ -678,6 +699,9 @@ int DLLEXPORT EN_solveH(EN_Project *p) { int errcode; long t, tstep; + EN_Project* p; + + p = (EN_Project*)ph; /* Open hydraulics solver */ errcode = EN_openH(p); @@ -922,9 +946,12 @@ 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; + + p = (EN_Project*)ph; /* Open WQ solver */ errcode = EN_openQ(p); @@ -1070,8 +1097,11 @@ int DLLEXPORT EN_writeline(EN_Project *p, char *line) { return (0); } -int DLLEXPORT EN_report(EN_Project *p) { +int DLLEXPORT EN_report(EN_ProjectHandle ph) { int errcode; + EN_Project* p; + + p = (EN_Project*)ph; /* Check if results saved to binary output file */ if (!p->save_options.SaveQflag) diff --git a/src/input2.c b/src/input2.c index 2e54645..90c68aa 100644 --- a/src/input2.c +++ b/src/input2.c @@ -821,9 +821,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/vars.h b/src/vars.h index 04f540c..88a940a 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/tools/epanet-toolkit/setup.py b/tools/epanet-toolkit/setup.py new file mode 100644 index 0000000..c3743c0 --- /dev/null +++ b/tools/epanet-toolkit/setup.py @@ -0,0 +1,40 @@ +# -*- coding: utf-8 -*- +# +# setup.py - Setup up script for en_toolkit python extension +# +# Created: 11/27/2017 +# Author: Michael E. Tryby +# US EPA - ORD/NRMRL +# +# Requires: +# Platform C language compiler +# SWIG +# + +try: + from setuptools import setup, Extension + from setuptools.command.build_ext import build_ext +except ImportError: + from distutils.core import setup, Extension + from distutils.command.build_ext import build_ext + +setup( + name = "epanet-toolkit", + version = "0.0.0", + ext_modules = [ + Extension("_epanet_toolkit", + include_dirs = ['lib\\'], + libraries = ['epanet'], + library_dirs = ['lib\\'], + sources = ['src\\epanet_toolkit.i'], + swig_opts=['-modern'], + language = 'C' + ) + ], + package_dir = {'':'src'}, + py_modules = ['epanet_toolkit'], + + install_requires = [ + 'enum34' + ] +) diff --git a/tools/epanet-toolkit/src/epanet_toolkit.i b/tools/epanet-toolkit/src/epanet_toolkit.i new file mode 100644 index 0000000..40f7698 --- /dev/null +++ b/tools/epanet-toolkit/src/epanet_toolkit.i @@ -0,0 +1,71 @@ +/* + * epanet_toolkit.i - SWIG interface description file for EPANET toolkit + * + * Created: 11/27/2017 + * Author: Michael E. Tryby + * US EPA - ORD/NRMRL + * + * Build command: + * $ swig -I../../../include -python epanet_toolkit.i + * +*/ + +%module epanet_toolkit +%{ +#include "epanet2.h" + +#define SWIG_FILE_WITH_INIT +%} + + +/* DEFINE AND TYPEDEF MUST BE INCLUDED */ +typedef void* EN_ProjectHandle; + + +#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 + #else + #define DLLEXPORT + #endif + #else + #define DLLEXPORT + #endif +#endif + + +/* TYPEMAPS FOR OPAQUE POINTER */ +/* Used for functions that output a new opaque pointer */ +%typemap(in, numinputs=0) EN_ProjectHandle* ph (EN_ProjectHandle retval) +{ + /* OUTPUT in */ + retval = NULL; + $1 = &retval; +} +/* used for functions that take in an opaque pointer (or NULL) +and return a (possibly) different pointer */ +%typemap(argout) EN_PorjectHandle* ph +{ + /* OUTPUT argout */ + %append_output(SWIG_NewPointerObj(SWIG_as_voidptr(retval$argnum), $1_descriptor, 0)); +} +/* No need for special IN typemap for opaque pointers, it works anyway */ + + +/* NO EXCEPTION HANDLING FOR THESE FUNCTIONS */ +int DLLEXPORT EN_alloc(EN_ProjectHandle* ph); +int DLLEXPORT EN_open(EN_ProjectHandle ph, char *inpFile, char *rptFile, char *binOutFile); +int DLLEXPORT EN_solveH(EN_ProjectHandle ph); +int DLLEXPORT EN_solveQ(EN_ProjectHandle ph); +int DLLEXPORT EN_report(EN_ProjectHandle ph); +int DLLEXPORT EN_close(EN_ProjectHandle ph); +int DLLEXPORT EN_free(EN_ProjectHandle ph); diff --git a/tools/epanet-toolkit/test/data/Net1.inp b/tools/epanet-toolkit/test/data/Net1.inp new file mode 100644 index 0000000..4df5bbf --- /dev/null +++ b/tools/epanet-toolkit/test/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/tools/epanet-toolkit/test/epanet_toolkit_test.py b/tools/epanet-toolkit/test/epanet_toolkit_test.py new file mode 100644 index 0000000..7a2e8ce --- /dev/null +++ b/tools/epanet-toolkit/test/epanet_toolkit_test.py @@ -0,0 +1,49 @@ +# -*- coding: utf-8 -*- + +# +# epanet_toolkit_test.py - Unit tests for epanet toolkit API +# +# Created: Dec. 4, 2017 +# Author: Michael E. Tryby +# US EPA - ORD/NRMRL +# + +from unittest import TestCase +import inspect + +import epanet_toolkit as ent + + +class TestToolkitMethods(TestCase): + + def test_alloc(self): + pass + + def test_free(self): + pass + + def test_open(self): + pass + + def test_close(self): + pass + + +class TestToolkit(TestCase): + + def setUp(self): + pass + + def tearDown(self): + pass + + def test_solveH(self): + pass + + def test_solveQ(self): + pass + + def test_report(self): + pass + + \ No newline at end of file diff --git a/tools/epanet-toolkit/test/main.py b/tools/epanet-toolkit/test/main.py new file mode 100644 index 0000000..41b7dd6 --- /dev/null +++ b/tools/epanet-toolkit/test/main.py @@ -0,0 +1,13 @@ + +import epanet_toolkit as tlkt + +def epanet(): + [error, handle] = tlkt.EN_alloc() + + [error, handle] = tlkt.EN_free(handle) + + +if __name__ == "__main__": + + epanet() + diff --git a/tools/epanet-toolkit/test/test_epanet_toolkit.cpp b/tools/epanet-toolkit/test/test_epanet_toolkit.cpp new file mode 100644 index 0000000..ddca787 --- /dev/null +++ b/tools/epanet-toolkit/test/test_epanet_toolkit.cpp @@ -0,0 +1,34 @@ + +#include +#include + +#include "epanet2.h" + +// NOTE: Project Home needs to be updated to run unit test +#define PROJECT_HOME "C:/Users/mtryby/Workspace/GitRepo/michaeltryby/epanet" +#define DATA_PATH "/tools/epanet-toolkit/test/data/" + +using namespace std; + + +int main(int argc, char *argv[]) +{ + int error = 0; + EN_ProjectHandle project = NULL; + + char inputFile[10] = "Net1.inp"; + char reportFile[10] = "net1.rpt"; + char outputFile[10] = "net1.out"; + + error = EN_alloc(&project); + error = EN_open(project, inputFile, reportFile, outputFile); + + error = EN_solveH(project); + error = EN_solveQ(project); + error = EN_report(project); + + error = EN_close(project); + error = EN_free(project); + + return error; +}