11#!/usr/bin/env python
22"""Python EpanetToolkit interface
33
4- not yet implemented:
5- - ENgetqualtype
6- - ENsetqualtype
7- - ENstepQ
8-
9- - ENresetreport
10- - ENsetreport
11- - ENsetstatusreport
12- - ENwriteline
13-
14- - ENsavehydfile
15- - ENusehydfile
16-
17- added functions:
18- + ENsimtime"""
4+ added function ENsimtime"""
195
206import ctypes
217import platform
2410_plat = platform .system ()
2511if _plat == 'Linux' :
2612 _lib = ctypes .CDLL ("libepanet.so.2" )
27- elif _plat == 'Windows' :
28- _lib = ctypes .windll .epanet2
13+ elif _plat == 'Windows' :
14+ try :
15+ # if epanet2.dll compiled with __cdecl (as in OpenWaterAnalytics)
16+ _lib = ctypes .CDLL ("epanet2.dll" )
17+ _lib .ENgetversion (ctypes .byref (ctypes .c_int ()))
18+ except ValueError :
19+ # if epanet2.dll compiled with __stdcall (as in EPA original DLL)
20+ try :
21+ _lib = ctypes .windll .epanet2
22+ _lib .ENgetversion (ctypes .byref (ctypes .c_int ()))
23+ except ValueError :
24+ raise Exception ("epanet2.dll not suitable" )
25+
2926else :
3027 Exception ('Platform ' + _plat + ' unsupported (not yet)' )
31-
28+
3229
3330_current_simulation_time = ctypes .c_long ()
3431
3936
4037
4138
42- def ENepanet (nomeinp , nomerpt = 'report.txt ' , nomebin = '' , vfunc = None ):
39+ def ENepanet (nomeinp , nomerpt = '' , nomebin = '' , vfunc = None ):
4340 """Runs a complete EPANET simulation.
4441
4542 Arguments:
4643 nomeinp: name of the input file
4744 nomerpt: name of an output report file
4845 nomebin: name of an optional binary output file
4946 vfunc : pointer to a user-supplied function which accepts a character string as its argument."""
50- ierr = _lib .ENepanet (ctypes .c_char_p (nomeinp ), ctypes .c_char_p (nomerpt ), ctypes .c_char_p (nomebin ), vfunc )
47+ if vfunc is not None :
48+ CFUNC = ctypes .CFUNCTYPE (ctypes .c_void_p , ctypes .c_char_p )
49+ callback = CFUNC (vfunc )
50+ else :
51+ callback = None
52+ ierr = _lib .ENepanet (ctypes .c_char_p (nomeinp ), ctypes .c_char_p (nomerpt ), ctypes .c_char_p (nomebin ), callback )
5153 if ierr != 0 : raise ENtoolkitError (ierr )
5254
5355
54- def ENopen (nomeinp , nomerpt = 'report.txt ' , nomebin = '' ):
56+ def ENopen (nomeinp , nomerpt = '' , nomebin = '' ):
5557 """Opens the Toolkit to analyze a particular distribution system
5658
5759 Arguments:
@@ -300,6 +302,24 @@ def ENgettimeparam(paramcode):
300302 ierr = _lib .ENgettimeparam (paramcode , ctypes .byref (j ))
301303 if ierr != 0 : raise ENtoolkitError (ierr )
302304 return j .value
305+
306+ def ENgetqualtype (qualcode ):
307+ """Retrieves the type of water quality analysis called for
308+ returns qualcode: Water quality analysis codes are as follows:
309+ EN_NONE 0 No quality analysis
310+ EN_CHEM 1 Chemical analysis
311+ EN_AGE 2 Water age analysis
312+ EN_TRACE 3 Source tracing
313+ tracenode: index of node traced in a source tracing
314+ analysis (value will be 0 when qualcode
315+ is not EN_TRACE)"""
316+ qualcode = ctypes .c_int ()
317+ tracenode = ctypes .c_int ()
318+ ierr = _lib .ENgetqualtype (ctypes .byref (qualcode ),
319+ ctypes .byref (tracenode ))
320+ if ierr != 0 : raise ENtoolkitError (ierr )
321+ return qualcode .value , tracenode .value
322+
303323
304324
305325#-------Retrieving other network information--------
@@ -308,7 +328,7 @@ def ENgetcontrol(cindex, ctype, lindex, setting, nindex, level ):
308328 Arguments:
309329 cindex: control statement index
310330 ctype: control type code EN_LOWLEVEL (Low Level Control)
311- EN_HILEVEL (High Level Control)
331+ EN_HILEVEL (High Level Control)
312332 EN_TIMER (Timer Control)
313333 EN_TIMEOFDAY (Time-of-Day Control)
314334 lindex: index of link being controlled
@@ -415,7 +435,9 @@ def ENsetlinkvalue(index, paramcode, value):
415435 a simulation is being run (within the ENrunH - ENnextH loop).
416436
417437 value:parameter value"""
418- ierr = _lib .ENsetlinkvalue (ctypes .c_int (index ), ctypes .c_int (paramcode ), ctypes .c_float (value ))
438+ ierr = _lib .ENsetlinkvalue (ctypes .c_int (index ),
439+ ctypes .c_int (paramcode ),
440+ ctypes .c_float (value ))
419441 if ierr != 0 : raise ENtoolkitError (ierr )
420442
421443
@@ -449,13 +471,25 @@ def ENsetpatternvalue( index, period, value):
449471 period: period within time pattern
450472 value: multiplier factor for the period"""
451473 #int ENsetpatternvalue( int index, int period, float value )
452- ierr = _lib .ENsetpatternvalue ( ctypes .c_int (index ), ctypes .c_int (period ), ctypes .c_float (value ) )
474+ ierr = _lib .ENsetpatternvalue ( ctypes .c_int (index ),
475+ ctypes .c_int (period ),
476+ ctypes .c_float (value ) )
453477 if ierr != 0 : raise ENtoolkitError (ierr )
454478
455479
456480
457-
458-
481+ def ENsetqualtype (qualcode , chemname , chemunits , tracenode ):
482+ """Sets the type of water quality analysis called for.
483+ Arguments:
484+ qualcode: water quality analysis code
485+ chemname: name of the chemical being analyzed
486+ chemunits: units that the chemical is measured in
487+ tracenode: ID of node traced in a source tracing analysis """
488+ ierr = _lib .ENsetqualtype ( ctypes .c_int (qualcode ),
489+ ctypes .c_char_p (chemname ),
490+ ctypes .c_char_p (chemunits ),
491+ ctypes .c_char_p (tracenode ))
492+ if ierr != 0 : raise ENtoolkitError (ierr )
459493
460494
461495def ENsettimeparam (paramcode , timevalue ):
@@ -496,14 +530,23 @@ def ENsetoption( optioncode, value):
496530 if ierr != 0 : raise ENtoolkitError (ierr )
497531
498532
533+ #----- Saving and using hydraulic analysis results files -------
534+ def ENsavehydfile (fname ):
535+ """Saves the current contents of the binary hydraulics file to a file."""
536+ ierr = _lib .ENsavehydfile (ctypes .c_char_p (fname ))
537+ if ierr != 0 : raise ENtoolkitError (ierr )
499538
539+ def ENusehydfile (fname ):
540+ """Uses the contents of the specified file as the current binary hydraulics file"""
541+ ierr = _lib .ENusehydfile (ctypes .c_char_p (fname ))
542+ if ierr != 0 : raise ENtoolkitError (ierr )
500543
501544
502545
503546#----------Running a hydraulic analysis --------------------------
504547def ENsolveH ():
505- """Runs a complete hydraulic simulation with results for all time periods written to the
506- binary Hydraulics file."""
548+ """Runs a complete hydraulic simulation with results
549+ for all time periods written to the binary Hydraulics file."""
507550 ierr = _lib .ENsolveH ()
508551 if ierr != 0 : raise ENtoolkitError (ierr )
509552
@@ -514,7 +557,8 @@ def ENopenH():
514557
515558
516559def ENinitH (flag = None ):
517- """Initializes storage tank levels, link status and settings, and the simulation clock time prior
560+ """Initializes storage tank levels, link status and settings,
561+ and the simulation clock time prior
518562to running a hydraulic analysis.
519563
520564 flag EN_NOSAVE [+EN_SAVE] [+EN_INITFLOW] """
@@ -523,7 +567,8 @@ def ENinitH(flag=None):
523567
524568
525569def ENrunH ():
526- """Runs a single period hydraulic analysis, retrieving the current simulation clock time t"""
570+ """Runs a single period hydraulic analysis,
571+ retrieving the current simulation clock time t"""
527572 ierr = _lib .ENrunH (ctypes .byref (_current_simulation_time ))
528573 if ierr >= 100 :
529574 raise ENtoolkitError (ierr )
@@ -553,7 +598,8 @@ def ENcloseH():
553598
554599#----------Running a quality analysis --------------------------
555600def ENsolveQ ():
556- """Runs a complete water quality simulation with results at uniform reporting intervals written to EPANET's binary Output file."""
601+ """Runs a complete water quality simulation with results
602+ at uniform reporting intervals written to EPANET's binary Output file."""
557603 ierr = _lib .ENsolveQ ()
558604 if ierr != 0 : raise ENtoolkitError (ierr )
559605
@@ -564,29 +610,43 @@ def ENopenQ():
564610
565611
566612def ENinitQ (flag = None ):
567- """Initializes water quality and the simulation clock time prior to running a water quality analysis.
613+ """Initializes water quality and the simulation clock
614+ time prior to running a water quality analysis.
568615
569616 flag EN_NOSAVE | EN_SAVE """
570617 ierr = _lib .ENinitQ (flag )
571618 if ierr != 0 : raise ENtoolkitError (ierr )
572619
573620def ENrunQ ():
574- """Makes available the hydraulic and water quality results that occur at the start of the next time period of a water quality analysis, where the start of the period is returned in t."""
621+ """Makes available the hydraulic and water quality results
622+ that occur at the start of the next time period of a water quality analysis,
623+ where the start of the period is returned in t."""
575624 ierr = _lib .ENrunQ (ctypes .byref (_current_simulation_time ))
576625 if ierr >= 100 :
577626 raise ENtoolkitError (ierr )
578627 elif ierr > 0 :
579628 return ENgeterror (ierr )
580629
581630def ENnextQ ():
582- """Advances the water quality simulation to the start of the next hydraulic time period."""
631+ """Advances the water quality simulation
632+ to the start of the next hydraulic time period."""
583633 _deltat = ctypes .c_long ()
584634 ierr = _lib .ENnextQ (ctypes .byref (_deltat ))
585635 if ierr != 0 : raise ENtoolkitError (ierr )
586636 return _deltat .value
637+
638+
639+ def ENstepQ ():
640+ """Advances the water quality simulation one water quality time step.
641+ The time remaining in the overall simulation is returned in tleft."""
642+ tleft = ctypes .c_long ()
643+ ierr = _lib .ENnextQ (ctypes .byref (tleft ))
644+ if ierr != 0 : raise ENtoolkitError (ierr )
645+ return tleft .value
587646
588647def ENcloseQ ():
589- """Closes the water quality analysis system, freeing all allocated memory."""
648+ """Closes the water quality analysis system,
649+ freeing all allocated memory."""
590650 ierr = _lib .ENcloseQ ()
591651 if ierr != 0 : raise ENtoolkitError (ierr )
592652#--------------------------------------------
@@ -596,30 +656,65 @@ def ENcloseQ():
596656
597657
598658def ENsaveH ():
599- """Transfers results of a hydraulic simulation from the binary Hydraulics file to the binary
600- Output file, where results are only reported at uniform reporting intervals."""
659+ """Transfers results of a hydraulic simulation
660+ from the binary Hydraulics file to the binary
661+ Output file, where results are only reported at
662+ uniform reporting intervals."""
601663 ierr = _lib .ENsaveH ()
602664 if ierr != 0 : raise ENtoolkitError (ierr )
603665
604666
605667def ENsaveinpfile (fname ):
606- """Writes all current network input data to a file using the format of an EPANET input file."""
668+ """Writes all current network input data to a file
669+ using the format of an EPANET input file."""
607670 ierr = _lib .ENsaveinpfile ( ctypes .c_char_p (fname ))
608671 if ierr != 0 : raise ENtoolkitError (ierr )
609672
610673
611674def ENreport ():
612- """Writes a formatted text report on simulation results to the Report file."""
675+ """Writes a formatted text report on simulation results
676+ to the Report file."""
613677 ierr = _lib .ENreport ()
614678 if ierr != 0 : raise ENtoolkitError (ierr )
615679
680+ def ENresetreport ():
681+ """Clears any report formatting commands
682+
683+ that either appeared in the [REPORT] section of the
684+ EPANET Input file or were issued with the
685+ ENsetreport function"""
686+ ierr = _lib .ENresetreport ()
687+ if ierr != 0 : raise ENtoolkitError (ierr )
688+
689+ def ENsetreport (command ):
690+ """Issues a report formatting command.
691+
692+ Formatting commands are the same as used in the
693+ [REPORT] section of the EPANET Input file."""
694+ ierr = _lib .ENsetreport (ctypes .c_char_p (command ))
695+ if ierr != 0 : raise ENtoolkitError (ierr )
696+
697+ def ENsetstatusreport (statuslevel ):
698+ """Sets the level of hydraulic status reporting.
699+
700+ statuslevel: level of status reporting
701+ 0 - no status reporting
702+ 1 - normal reporting
703+ 2 - full status reporting"""
704+ ierr = _lib .ENsetstatusreport (ctypes .c_int (statuslevel ))
705+ if ierr != 0 : raise ENtoolkitError (ierr )
616706
617707def ENgeterror (errcode ):
618708 """Retrieves the text of the message associated with a particular error or warning code."""
619709 errmsg = ctypes .create_string_buffer (_err_max_char )
620710 _lib .ENgeterror ( errcode ,ctypes .byref (errmsg ), _err_max_char )
621711 return errmsg .value
622712
713+ def ENwriteline (line ):
714+ """Writes a line of text to the EPANET report file."""
715+ ierr = _lib .ENwriteline (ctypes .c_char_p (line ))
716+ if ierr != 0 : raise ENtoolkitError (ierr )
717+
623718
624719class ENtoolkitError (Exception ):
625720 def __init__ (self , ierr ):
@@ -630,6 +725,50 @@ def __init__(self, ierr):
630725 self .message = 'ENtoolkit Undocumented Error ' + str (ierr )+ ': look at text.h in epanet sources'
631726 def __str__ (self ):
632727 return self .message
728+
729+
730+ #------ functions added from OpenWaterAnalytics ----------------------------------
731+ # functions not present in original Epanet2 toolkit from US EPA
732+ # it may change in future versions
733+ #----------------------------------------------------------------------------------
734+ if hasattr (_lib ,"ENgetcurve" ):
735+ def ENgetcurve (curveIndex ):
736+ curveid = ctypes .create_string_buffer (_max_label_len )
737+ nValues = ctypes .c_int ()
738+ xValues = ctypes .POINTER (ctypes .c_float )()
739+ yValues = ctypes .POINTER (ctypes .c_float )()
740+ ierr = _lib .ENgetcurve (curveIndex ,
741+ ctypes .byref (curveid ),
742+ ctypes .byref (nValues ),
743+ ctypes .byref (xValues ),
744+ ctypes .byref (yValues )
745+ )
746+ # strange behavior of ENgetcurve: it returns also curveID
747+ # better split in two distinct functions ....
748+ if ierr != 0 : raise ENtoolkitError (ierr )
749+ curve = []
750+ for i in range (nValues .value ):
751+ curve .append ( (xValues [i ],yValues [i ]) )
752+ return curve
753+
754+ def ENgetcurveid (curveIndex ):
755+ curveid = ctypes .create_string_buffer (_max_label_len )
756+ nValues = ctypes .c_int ()
757+ xValues = ctypes .POINTER (ctypes .c_float )()
758+ yValues = ctypes .POINTER (ctypes .c_float )()
759+ ierr = _lib .ENgetcurve (curveIndex ,
760+ ctypes .byref (curveid ),
761+ ctypes .byref (nValues ),
762+ ctypes .byref (xValues ),
763+ ctypes .byref (yValues )
764+ )
765+ # strange behavior of ENgetcurve: it returns also curveID
766+ # better split in two distinct functions ....
767+ if ierr != 0 : raise ENtoolkitError (ierr )
768+ return curveid .value
769+
770+ #-----end of functions added from OpenWaterAnalytics ----------------------------------
771+
633772
634773EN_ELEVATION = 0 # /* Node parameters */
635774EN_BASEDEMAND = 1
0 commit comments