"""Python EpanetToolkit interface added function ENsimtime""" import ctypes import platform import datetime import os import warnings class EPANET2(object): def __init__(self, charset='UTF8'): _plat= platform.system() if _plat=='Darwin': dll_path = os.path.join(os.path.dirname(__file__), "lib/libepanet.dylib") self._lib = ctypes.cdll.LoadLibrary(dll_path) ctypes.c_float = ctypes.c_double elif _plat=='Linux': dll_path = os.path.join(os.path.dirname(__file__), "lib/libepanet.so") self._lib = ctypes.CDLL(dll_path) ctypes.c_float = ctypes.c_double elif _plat=='Windows': ctypes.c_float = ctypes.c_double try: # if epanet2.dll compiled with __cdecl (as in OpenWaterAnalytics) dll_path = os.path.join(os.path.dirname(__file__), "lib/epanet2.dll") self._lib = ctypes.CDLL(dll_path) except ValueError: # if epanet2.dll compiled with __stdcall (as in EPA original DLL) try: self._lib = ctypes.windll.epanet2 self._lib.EN_getversion(self.ph, ctypes.byref(ctypes.c_int())) except ValueError: raise Exception("epanet2.dll not suitable") else: raise Exception('Platform '+ _plat +' unsupported (not yet)') self.charset = charset self._current_simulation_time= ctypes.c_long() self.ph = ctypes.c_void_p() self._lib.EN_createproject.argtypes = [ctypes.c_void_p] self._lib.EN_createproject(ctypes.byref(self.ph)) self._max_label_len= 32 self._err_max_char= 80 def ENepanet(self,nomeinp, nomerpt='', nomebin='', vfunc=None): """Runs a complete EPANET simulation. Arguments: nomeinp: name of the input file nomerpt: name of an output report file nomebin: name of an optional binary output file vfunc : pointer to a user-supplied function which accepts a character string as its argument.""" if vfunc is not None: CFUNC = ctypes.CFUNCTYPE(ctypes.c_void_p, ctypes.c_char_p) callback= CFUNC(vfunc) else: callback= None ierr= self._lib.EN_epanet(self.ph, ctypes.c_char_p(nomeinp.encode()), ctypes.c_char_p(nomerpt.encode()), ctypes.c_char_p(nomebin.encode()), callback) if ierr!=0: raise ENtoolkitError(self, ierr) def ENopen(self, nomeinp, nomerpt='', nomebin=''): """Opens the Toolkit to analyze a particular distribution system Arguments: nomeinp: name of the input file nomerpt: name of an output report file nomebin: name of an optional binary output file """ ierr= self._lib.EN_open(self.ph, ctypes.c_char_p(nomeinp.encode()), ctypes.c_char_p(nomerpt.encode()), ctypes.c_char_p(nomebin.encode())) if ierr!=0: raise ENtoolkitError(self, ierr) def ENdeleteproject(self): """Closes down the Toolkit system (including all files being processed)""" ierr= self._lib.EN_deleteproject(ctypes.byref(self.ph)) if ierr!=0: raise ENtoolkitError(self, ierr) def ENgetnodeindex(self, nodeid): """Retrieves the index of a node with a specified ID. Arguments: nodeid: node ID label""" j= ctypes.c_int() ierr= self._lib.EN_getnodeindex(self.ph, ctypes.c_char_p(nodeid.encode(self.charset)), ctypes.byref(j)) if ierr!=0: raise ENtoolkitError(self, ierr) return j.value def ENgetnodeid(self, index): """Retrieves the ID label of a node with a specified index. Arguments: index: node index""" label = ctypes.create_string_buffer(self._max_label_len) ierr= self._lib.EN_getnodeid(self.ph, index, ctypes.byref(label)) if ierr!=0: raise ENtoolkitError(self, ierr) return label.value.decode(self.charset) def ENgetnodetype(self, index): """Retrieves the node-type code for a specific node. Arguments: index: node index""" j= ctypes.c_int() ierr= self._lib.EN_getnodetype(self.ph, index, ctypes.byref(j)) if ierr!=0: raise ENtoolkitError(self, ierr) return j.value def ENgetcoord(self, index): """Retrieves the coordinates (x,y) for a specific node. Arguments: index: node index""" x= ctypes.c_float() y= ctypes.c_float() ierr= self._lib.EN_getcoord(self.ph, index, ctypes.byref(x), ctypes.byref(y)) if ierr!=0: raise ENtoolkitError(self, ierr) return (x.value,y.value) def ENgetnodevalue(self, index, paramcode): """Retrieves the value of a specific node parameter. Arguments: index: node index paramcode: Node parameter codes consist of the following constants: EN_ELEVATION Elevation EN_BASEDEMAND ** Base demand EN_PATTERN ** Demand pattern index EN_EMITTER Emitter coeff. EN_INITQUAL Initial quality EN_SOURCEQUAL Source quality EN_SOURCEPAT Source pattern index EN_SOURCETYPE Source type (See note below) EN_TANKLEVEL Initial water level in tank EN_DEMAND * Actual demand EN_HEAD * Hydraulic head EN_PRESSURE * Pressure EN_QUALITY * Actual quality EN_SOURCEMASS * Mass flow rate per minute of a chemical source * computed values) ** primary demand category is last on demand list The following parameter codes apply only to storage tank nodes: EN_INITVOLUME Initial water volume EN_MIXMODEL Mixing model code (see below) EN_MIXZONEVOL Inlet/Outlet zone volume in a 2-compartment tank EN_TANKDIAM Tank diameter EN_MINVOLUME Minimum water volume EN_VOLCURVE Index of volume versus depth curve (0 if none assigned) EN_MINLEVEL Minimum water level EN_MAXLEVEL Maximum water level EN_MIXFRACTION Fraction of total volume occupied by the inlet/outlet zone in a 2-compartment tank EN_TANK_KBULK Bulk reaction rate coefficient""" j= ctypes.c_float() ierr= self._lib.EN_getnodevalue(self.ph, index, paramcode, ctypes.byref(j)) if ierr!=0: raise ENtoolkitError(self, ierr) return j.value ##------ def ENgetlinkindex(self, linkid): """Retrieves the index of a link with a specified ID. Arguments: linkid: link ID label""" j= ctypes.c_int() ierr= self._lib.EN_getlinkindex(self.ph, ctypes.c_char_p(linkid.encode(self.charset)), ctypes.byref(j)) if ierr!=0: raise ENtoolkitError(self, ierr) return j.value def ENgetlinkid(self, index): """Retrieves the ID label of a link with a specified index. Arguments: index: link index""" label = ctypes.create_string_buffer(self._max_label_len) ierr= self._lib.EN_getlinkid(self.ph, index, ctypes.byref(label)) if ierr!=0: raise ENtoolkitError(self, ierr) return label.value.decode(self.charset) def ENgetlinktype(self, index): """Retrieves the link-type code for a specific link. Arguments: index: link index""" j= ctypes.c_int() ierr= self._lib.EN_getlinktype(self.ph, index, ctypes.byref(j)) if ierr!=0: raise ENtoolkitError(self, ierr) return j.value def ENgetlinknodes(self, index): """Retrieves the indexes of the end nodes of a specified link. Arguments: index: link index""" j1= ctypes.c_int() j2= ctypes.c_int() ierr= self._lib.EN_getlinknodes(self.ph, index,ctypes.byref(j1),ctypes.byref(j2)) if ierr!=0: raise ENtoolkitError(self, ierr) return j1.value,j2.value def ENgetlinkvalue(self, index, paramcode): """Retrieves the value of a specific link parameter. Arguments: index: link index paramcode: Link parameter codes consist of the following constants: EN_DIAMETER Diameter EN_LENGTH Length EN_ROUGHNESS Roughness coeff. EN_MINORLOSS Minor loss coeff. EN_INITSTATUS Initial link status (0 = closed, 1 = open) EN_INITSETTING Roughness for pipes, initial speed for pumps, initial setting for valves EN_KBULK Bulk reaction coeff. EN_KWALL Wall reaction coeff. EN_FLOW * Flow rate EN_VELOCITY * Flow velocity EN_HEADLOSS * Head loss EN_STATUS * Actual link status (0 = closed, 1 = open) EN_SETTING * Roughness for pipes, actual speed for pumps, actual setting for valves EN_ENERGY * Energy expended in kwatts * computed values""" j= ctypes.c_float() ierr= self._lib.EN_getlinkvalue(self.ph, index, paramcode, ctypes.byref(j)) if ierr!=0: raise ENtoolkitError(self, ierr) return j.value #------ def ENgetpatternid(self, index): """Retrieves the ID label of a particular time pattern. Arguments: index: pattern index""" label = ctypes.create_string_buffer(self._max_label_len) ierr= self._lib.EN_getpatternid(self.ph, index, ctypes.byref(label)) if ierr!=0: raise ENtoolkitError(self, ierr) return label.value.decode(self.charset) def ENgetpatternindex(self, patternid): """Retrieves the index of a particular time pattern. Arguments: id: pattern ID label""" j= ctypes.c_int() ierr= self._lib.EN_getpatternindex(self.ph, ctypes.c_char_p(patternid.encode(self.charset)), ctypes.byref(j)) if ierr!=0: raise ENtoolkitError(self, ierr) return j.value def ENgetpatternlen(self, index): """Retrieves the number of time periods in a specific time pattern. Arguments: index:pattern index""" j= ctypes.c_int() ierr= self._lib.EN_getpatternlen(self.ph, index, ctypes.byref(j)) if ierr!=0: raise ENtoolkitError(self, ierr) return j.value def ENgetpatternvalue(self, index, period): """Retrieves the multiplier factor for a specific time period in a time pattern. Arguments: index: time pattern index period: period within time pattern""" j= ctypes.c_float() ierr= self._lib.EN_getpatternvalue(self.ph, index, period, ctypes.byref(j)) if ierr!=0: raise ENtoolkitError(self, ierr) return j.value def ENgetcount(self, countcode): """Retrieves the number of network components of a specified type. Arguments: countcode: component code EN_NODECOUNT EN_TANKCOUNT EN_LINKCOUNT EN_PATCOUNT EN_CURVECOUNT EN_CONTROLCOUNT""" j= ctypes.c_int() ierr= self._lib.EN_getcount(self.ph, countcode, ctypes.byref(j)) if ierr!=0: raise ENtoolkitError(self, ierr) return j.value def ENgetflowunits(self): """Retrieves a code number indicating the units used to express all flow rates.""" j= ctypes.c_int() ierr= self._lib.EN_getflowunits(self.ph, ctypes.byref(j)) if ierr!=0: raise ENtoolkitError(self, ierr) return j.value def ENgettimeparam(self, paramcode): """Retrieves the value of a specific analysis time parameter. Arguments: paramcode: EN_DURATION EN_HYDSTEP EN_QUALSTEP EN_PATTERNSTEP EN_PATTERNSTART EN_REPORTSTEP EN_REPORTSTART EN_RULESTEP EN_STATISTIC EN_PERIODS""" j= ctypes.c_int() ierr= self._lib.EN_gettimeparam(self.ph, paramcode, ctypes.byref(j)) if ierr!=0: raise ENtoolkitError(self, ierr) return j.value def ENgetqualtype(self, qualcode): """Retrieves the type of water quality analysis called for returns qualcode: Water quality analysis codes are as follows: EN_NONE 0 No quality analysis EN_CHEM 1 Chemical analysis EN_AGE 2 Water age analysis EN_TRACE 3 Source tracing tracenode: index of node traced in a source tracing analysis (value will be 0 when qualcode is not EN_TRACE)""" qualcode= ctypes.c_int() tracenode= ctypes.c_int() ierr= self._lib.EN_getqualtype(self.ph, ctypes.byref(qualcode), ctypes.byref(tracenode)) if ierr!=0: raise ENtoolkitError(self, ierr) return qualcode.value, tracenode.value #-------Retrieving other network information-------- def ENgetcontrol(self, cindex, ctype, lindex, setting, nindex, level ): """Retrieves the parameters of a simple control statement. Arguments: cindex: control statement index ctype: control type code EN_LOWLEVEL (Low Level Control) EN_HILEVEL (High Level Control) EN_TIMER (Timer Control) EN_TIMEOFDAY (Time-of-Day Control) lindex: index of link being controlled setting: value of the control setting nindex: index of controlling node level: value of controlling water level or pressure for level controls or of time of control action (in seconds) for time-based controls""" #int ENgetcontrol(int cindex, int* ctype, int* lindex, float* setting, int* nindex, float* level ) ierr= self._lib.EN_getcontrol(self.ph, ctypes.c_int(cindex), ctypes.c_int(ctype), ctypes.c_int(lindex), ctypes.c_float(setting), ctypes.c_int(nindex), ctypes.c_float(level) ) if ierr!=0: raise ENtoolkitError(self, ierr) def ENgetoption(self, optioncode): """Retrieves the value of a particular analysis option. Arguments: optioncode: EN_TRIALS EN_ACCURACY EN_TOLERANCE EN_EMITEXPON EN_DEMANDMULT""" j= ctypes.c_int() ierr= self._lib.EN_getoption(self.ph, optioncode, ctypes.byref(j)) if ierr!=0: raise ENtoolkitError(self, ierr) return j.value def ENgetversion(self): """Retrieves the current version number of the Toolkit.""" j= ctypes.c_int() ierr= self._lib.EN_getversion(self.ph, ctypes.byref(j)) if ierr!=0: raise ENtoolkitError(self, ierr) return j.value #---------Setting new values for network parameters------------- def ENaddcontrol(self, ctype, lindex, setting, nindex, level ): """Sets the parameters of a simple control statement. Arguments: ctype: control type code EN_LOWLEVEL (Low Level Control) EN_HILEVEL (High Level Control) EN_TIMER (Timer Control) EN_TIMEOFDAY (Time-of-Day Control) lindex: index of link being controlled setting: value of the control setting nindex: index of controlling node level: value of controlling water level or pressure for level controls or of time of control action (in seconds) for time-based controls""" #int ENsetcontrol(int cindex, int* ctype, int* lindex, float* setting, int* nindex, float* level ) cindex = ctypes.c_int() ierr= self._lib.EN_addcontrol(self.ph, ctypes.byref(cindex), ctypes.c_int(ctype), ctypes.c_int(lindex), ctypes.c_float(setting), ctypes.c_int(nindex), ctypes.c_float(level)) if ierr!=0: raise ENtoolkitError(self, ierr) return cindex def ENsetcontrol(self, cindex, ctype, lindex, setting, nindex, level ): """Sets the parameters of a simple control statement. Arguments: cindex: control statement index ctype: control type code EN_LOWLEVEL (Low Level Control) EN_HILEVEL (High Level Control) EN_TIMER (Timer Control) EN_TIMEOFDAY (Time-of-Day Control) lindex: index of link being controlled setting: value of the control setting nindex: index of controlling node level: value of controlling water level or pressure for level controls or of time of control action (in seconds) for time-based controls""" #int ENsetcontrol(int cindex, int* ctype, int* lindex, float* setting, int* nindex, float* level ) ierr= self._lib.EN_setcontrol(self.ph, ctypes.c_int(cindex), ctypes.c_int(ctype), ctypes.c_int(lindex), ctypes.c_float(setting), ctypes.c_int(nindex), ctypes.c_float(level) ) if ierr!=0: raise ENtoolkitError(self, ierr) def ENsetnodevalue(self, index, paramcode, value): """Sets the value of a parameter for a specific node. Arguments: index: node index paramcode: Node parameter codes consist of the following constants: EN_ELEVATION Elevation EN_BASEDEMAND ** Base demand EN_PATTERN ** Demand pattern index EN_EMITTER Emitter coeff. EN_INITQUAL Initial quality EN_SOURCEQUAL Source quality EN_SOURCEPAT Source pattern index EN_SOURCETYPE Source type (See note below) EN_TANKLEVEL Initial water level in tank ** primary demand category is last on demand list The following parameter codes apply only to storage tank nodes EN_TANKDIAM Tank diameter EN_MINVOLUME Minimum water volume EN_MINLEVEL Minimum water level EN_MAXLEVEL Maximum water level EN_MIXMODEL Mixing model code EN_MIXFRACTION Fraction of total volume occupied by the inlet/outlet EN_TANK_KBULK Bulk reaction rate coefficient value:parameter value""" ierr= self._lib.EN_setnodevalue(self.ph, ctypes.c_int(index), ctypes.c_int(paramcode), ctypes.c_float(value)) if ierr!=0: raise ENtoolkitError(self, ierr) def ENsetlinkvalue(self, index, paramcode, value): """Sets the value of a parameter for a specific link. Arguments: index: link index paramcode: Link parameter codes consist of the following constants: EN_DIAMETER Diameter EN_LENGTH Length EN_ROUGHNESS Roughness coeff. EN_MINORLOSS Minor loss coeff. EN_INITSTATUS * Initial link status (0 = closed, 1 = open) EN_INITSETTING * Roughness for pipes, initial speed for pumps, initial setting for valves EN_KBULK Bulk reaction coeff. EN_KWALL Wall reaction coeff. EN_STATUS * Actual link status (0 = closed, 1 = open) EN_SETTING * Roughness for pipes, actual speed for pumps, actual setting for valves * Use EN_INITSTATUS and EN_INITSETTING to set the design value for a link's status or setting that exists prior to the start of a simulation. Use EN_STATUS and EN_SETTING to change these values while a simulation is being run (within the ENrunH - ENnextH loop). value:parameter value""" ierr= self._lib.EN_setlinkvalue(self.ph, ctypes.c_int(index), ctypes.c_int(paramcode), ctypes.c_float(value)) if ierr!=0: raise ENtoolkitError(self, ierr) # ---- EPYNET Extensions ---- # def ENinit(self, rptfile, binfile, units_code, headloss_code): ierr = self._lib.EN_init(self.ph, ctypes.c_char_p(rptfile), ctypes.c_char_p(binfile), ctypes.c_int(units_code), ctypes.c_int(headloss_code)) if ierr!=0: raise ENtoolkitError(self, ierr) def ENaddnode(self, node_id, node_type_code): index = ctypes.c_int() ierr= self._lib.EN_addnode(self.ph, ctypes.c_char_p(node_id.encode(self.charset)), ctypes.c_int(node_type_code), ctypes.byref(index)) if ierr!=0: raise ENtoolkitError(self, ierr) return index def ENdeletenode(self, node_index, conditional=0): ierr= self._lib.EN_deletenode(self.ph, ctypes.c_int(node_index), ctypes.c_int(conditional)) if ierr!=0: raise ENtoolkitError(self, ierr) def ENdeletelink(self, link_index, conditional=0): ierr= self._lib.EN_deletelink(self.ph, ctypes.c_int(link_index), ctypes.c_int(conditional)) if ierr!=0: raise ENtoolkitError(self, ierr) def ENaddlink(self, link_id, link_type_code, from_node_id, to_node_id): index = ctypes.c_int() ierr= self._lib.EN_addlink(self.ph, ctypes.c_char_p(link_id.encode(self.charset)), ctypes.c_int(link_type_code), ctypes.c_char_p(from_node_id.encode(self.charset)), ctypes.c_char_p(to_node_id.encode(self.charset)), ctypes.byref(index)) if ierr!=0: raise ENtoolkitError(self, ierr) def ENsetheadcurveindex(self, pump_index, curve_index): ierr = self._lib.EN_setheadcurveindex(self.ph, ctypes.c_int(pump_index), ctypes.c_int(curve_index)) if ierr!=0: raise ENtoolkitError(self, ierr) def ENgetheadcurveindex(self, pump_index): j= ctypes.c_int() ierr = self._lib.EN_getheadcurveindex(self.ph, ctypes.c_int(pump_index), ctypes.byref(j)) if ierr!=0: raise ENtoolkitError(self, ierr) return j.value def ENaddcurve(self, curve_id): ierr = self._lib.EN_addcurve(self.ph, ctypes.c_char_p(curve_id.encode(self.charset))) if ierr!=0: raise ENtoolkitError(self, ierr) def ENsetcurvevalue(self, curve_index,point_index, x ,y): ierr = self._lib.EN_setcurvevalue(self.ph, ctypes.c_int(curve_index), ctypes.c_int(point_index), ctypes.c_float(x), ctypes.c_float(y)) if ierr!=0: raise ENtoolkitError(self, ierr) def ENsetcoord(self, index, x, y): ierr= self._lib.EN_setcoord(self.ph, ctypes.c_int(index), ctypes.c_float(x), ctypes.c_float(y)) if ierr!=0: raise ENtoolkitError(self, ierr) def ENaddpattern(self, patternid): """Adds a new time pattern to the network. Arguments: id: ID label of pattern""" ierr= self._lib.EN_addpattern(self.ph, ctypes.c_char_p(patternid.encode(self.charset))) if ierr!=0: raise ENtoolkitError(self, ierr) def ENsetpattern(self, index, factors): """Sets all of the multiplier factors for a specific time pattern. Arguments: index: time pattern index factors: multiplier factors list for the entire pattern""" # int ENsetpattern( int index, float* factors, int nfactors ) nfactors= len(factors) cfactors_type= ctypes.c_float* nfactors cfactors= cfactors_type() for i in range(nfactors): cfactors[i]= float(factors[i] ) ierr= self._lib.EN_setpattern(self.ph, ctypes.c_int(index), cfactors, ctypes.c_int(nfactors) ) if ierr!=0: raise ENtoolkitError(self, ierr) def ENsetpatternvalue(self, index, period, value): """Sets the multiplier factor for a specific period within a time pattern. Arguments: index: time pattern index period: period within time pattern value: multiplier factor for the period""" #int ENsetpatternvalue( int index, int period, float value ) ierr= self._lib.EN_setpatternvalue(self.ph, ctypes.c_int(index), ctypes.c_int(period), ctypes.c_float(value) ) if ierr!=0: raise ENtoolkitError(self, ierr) def ENsetqualtype(self, qualcode, chemname, chemunits, tracenode): """Sets the type of water quality analysis called for. Arguments: qualcode: water quality analysis code chemname: name of the chemical being analyzed chemunits: units that the chemical is measured in tracenode: ID of node traced in a source tracing analysis """ ierr= self._lib.EN_setqualtype(self.ph, ctypes.c_int(qualcode), ctypes.c_char_p(chemname.encode(self.charset)), ctypes.c_char_p(chemunits.encode(self.charset)), ctypes.c_char_p(tracenode.encode(self.charset))) if ierr!=0: raise ENtoolkitError(self, ierr) def ENsettimeparam(self, paramcode, timevalue): """Sets the value of a time parameter. Arguments: paramcode: time parameter code EN_DURATION EN_HYDSTEP EN_QUALSTEP EN_PATTERNSTEP EN_PATTERNSTART EN_REPORTSTEP EN_REPORTSTART EN_RULESTEP EN_STATISTIC EN_PERIODS timevalue: value of time parameter in seconds The codes for EN_STATISTIC are: EN_NONE none EN_AVERAGE averaged EN_MINIMUM minimums EN_MAXIMUM maximums EN_RANGE ranges""" ierr= self._lib.EN_settimeparam(self.ph, ctypes.c_int(paramcode), ctypes.c_int(timevalue)) if ierr!=0: raise ENtoolkitError(self, ierr) def ENsetoption(self, optioncode, value): """Sets the value of a particular analysis option. Arguments: optioncode: option code EN_TRIALS EN_ACCURACY EN_TOLERANCE EN_EMITEXPON EN_DEMANDMULT value: option value""" ierr= self._lib.EN_setoption(self.ph, ctypes.c_int(paramcode), ctypes.c_float(value)) if ierr!=0: raise ENtoolkitError(self, ierr) #----- Saving and using hydraulic analysis results files ------- def ENsavehydfile(self, fname): """Saves the current contents of the binary hydraulics file to a file.""" ierr= self._lib.EN_savehydfile(self.ph, ctypes.c_char_p(fname.encode())) if ierr!=0: raise ENtoolkitError(self, ierr) def ENusehydfile(self, fname): """Uses the contents of the specified file as the current binary hydraulics file""" ierr= self._lib.EN_usehydfile(self.ph, ctypes.c_char_p(fname.encode())) if ierr!=0: raise ENtoolkitError(self, ierr) #----------Running a hydraulic analysis -------------------------- def ENsolveH(self): """Runs a complete hydraulic simulation with results for all time periods written to the binary Hydraulics file.""" ierr= self._lib.EN_solveH(self.ph, ) if ierr!=0: raise ENtoolkitError(self, ierr) def ENopenH(self): """Opens the hydraulics analysis system""" ierr= self._lib.EN_openH(self.ph, ) def ENinitH(self, flag=None): """Initializes storage tank levels, link status and settings, and the simulation clock time prior to running a hydraulic analysis. flag EN_NOSAVE [+EN_SAVE] [+EN_INITFLOW] """ ierr= self._lib.EN_initH(self.ph, flag) if ierr!=0: raise ENtoolkitError(self, ierr) def ENrunH(self): """Runs a single period hydraulic analysis, retrieving the current simulation clock time t""" ierr= self._lib.EN_runH(self.ph, ctypes.byref(self._current_simulation_time)) if ierr>=100: raise ENtoolkitError(self, ierr) elif ierr>0: warnings.warn(self.ENgeterror(ierr)) return self.ENgeterror(ierr) def ENabort(self): self._lib.EN_abort(self.ph, ) def ENsimtime(self): """retrieves the current simulation time t as datetime.timedelta instance""" return datetime.timedelta(seconds= self._current_simulation_time.value ) def ENnextH(self): """Determines the length of time until the next hydraulic event occurs in an extended period simulation.""" _deltat= ctypes.c_long() ierr= self._lib.EN_nextH(self.ph, ctypes.byref(_deltat)) if ierr!=0: raise ENtoolkitError(self, ierr) return _deltat.value def ENcloseH(self): """Closes the hydraulic analysis system, freeing all allocated memory.""" ierr= self._lib.EN_closeH(self.ph, ) if ierr!=0: raise ENtoolkitError(self, ierr) #-------------------------------------------- #----------Running a quality analysis -------------------------- def ENsolveQ(self): """Runs a complete water quality simulation with results at uniform reporting intervals written to EPANET's binary Output file.""" ierr= self._lib.EN_solveQ(self.ph, ) if ierr!=0: raise ENtoolkitError(self, ierr) def ENopenQ(self): """Opens the water quality analysis system""" ierr= self._lib.EN_openQ(self.ph, ) def ENinitQ(self, flag=None): """Initializes water quality and the simulation clock time prior to running a water quality analysis. flag EN_NOSAVE | EN_SAVE """ ierr= self._lib.EN_initQ(self.ph, flag) if ierr!=0: raise ENtoolkitError(self, ierr) def ENrunQ(self): """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.""" ierr= self._lib.EN_runQ(self.ph, ctypes.byref(self._current_simulation_time)) if ierr>=100: raise ENtoolkitError(self, ierr) elif ierr>0: return self.ENgeterror(ierr) def ENnextQ(self): """Advances the water quality simulation to the start of the next hydraulic time period.""" _deltat= ctypes.c_long() ierr= self._lib.EN_nextQ(self.ph, ctypes.byref(_deltat)) if ierr!=0: raise ENtoolkitError(self, ierr) return _deltat.value def ENstepQ(self): """Advances the water quality simulation one water quality time step. The time remaining in the overall simulation is returned in tleft.""" tleft= ctypes.c_long() ierr= self._lib.EN_nextQ(self.ph, ctypes.byref(tleft)) if ierr!=0: raise ENtoolkitError(self, ierr) return tleft.value def ENcloseQ(self): """Closes the water quality analysis system, freeing all allocated memory.""" ierr= self._lib.EN_closeQ(self.ph, ) if ierr!=0: raise ENtoolkitError(self, ierr) #-------------------------------------------- def ENsaveH(self): """Transfers results of a hydraulic simulation from the binary Hydraulics file to the binary Output file, where results are only reported at uniform reporting intervals.""" ierr= self._lib.EN_saveH(self.ph, ) if ierr!=0: raise ENtoolkitError(self, ierr) def ENsaveinpfile(self, fname): """Writes all current network input data to a file using the format of an EPANET input file.""" ierr= self._lib.EN_saveinpfile(self.ph, ctypes.c_char_p(fname.encode())) if ierr!=0: raise ENtoolkitError(self, ierr) def ENreport(self): """Writes a formatted text report on simulation results to the Report file.""" ierr= self._lib.EN_report(self.ph, ) if ierr!=0: raise ENtoolkitError(self, ierr) def ENresetreport(self): """Clears any report formatting commands that either appeared in the [REPORT] section of the EPANET Input file or were issued with the ENsetreport function""" ierr= self._lib.EN_resetreport(self.ph, ) if ierr!=0: raise ENtoolkitError(self, ierr) def ENsetreport(self, command): """Issues a report formatting command. Formatting commands are the same as used in the [REPORT] section of the EPANET Input file.""" ierr= self._lib.EN_setreport(self.ph, ctypes.c_char_p(command.encode(self.charset))) if ierr!=0: raise ENtoolkitError(self, ierr) def ENsetstatusreport(self, statuslevel): """Sets the level of hydraulic status reporting. statuslevel: level of status reporting 0 - no status reporting 1 - normal reporting 2 - full status reporting""" ierr= self._lib.EN_setstatusreport(self.ph, ctypes.c_int(statuslevel)) if ierr!=0: raise ENtoolkitError(self, ierr) def ENgeterror(self, errcode): """Retrieves the text of the message associated with a particular error or warning code.""" errmsg= ctypes.create_string_buffer(self._err_max_char) self._lib.ENgeterror(errcode,ctypes.byref(errmsg), self._err_max_char ) return errmsg.value.decode(self.charset) def ENwriteline(self, line ): """Writes a line of text to the EPANET report file.""" ierr= self._lib.EN_writeline(self.ph, ctypes.c_char_p(line.encode(self.charset) )) if ierr!=0: raise ENtoolkitError(self, ierr) def ENgetcurve(self, curveIndex): curveid = ctypes.create_string_buffer(self._max_label_len) nValues = ctypes.c_int() xValues= (ctypes.c_float*100)() yValues= (ctypes.c_float*100)() ierr= self._lib.EN_getcurve(self.ph, curveIndex, ctypes.byref(curveid), ctypes.byref(nValues), xValues, yValues ) # strange behavior of ENgetcurve: it returns also curveID # better split in two distinct functions .... if ierr!=0: raise ENtoolkitError(self, ierr) curve= [] for i in range(nValues.value): curve.append( (xValues[i],yValues[i]) ) return curve def ENsetcurve(self, curveIndex, values): nValues = len(values) Values_type = ctypes.c_float* nValues xValues = Values_type() yValues = Values_type() for i in range(nValues): xValues[i] = float(values[i][0]) yValues[i] = float(values[i][1]) ierr = self._lib.EN_setcurve(self.ph, curveIndex, xValues, yValues, nValues) if ierr!=0: raise ENtoolkitError(self, ierr) def ENgetcurveid(self, curveIndex): curveid = ctypes.create_string_buffer(self._max_label_len) nValues = ctypes.c_int() xValues= (ctypes.c_float * 100)() yValues= (ctypes.c_float * 100)() ierr= self._lib.EN_getcurve(self.ph, curveIndex, ctypes.byref(curveid), ctypes.byref(nValues), xValues, yValues) # strange behavior of ENgetcurve: it returns also curveID # better split in two distinct functions .... if ierr!=0: raise ENtoolkitError(self, ierr) return curveid.value.decode(self.charset) def ENgetcurveindex(self, curveId): j= ctypes.c_int() ierr= self._lib.EN_getcurveindex(self.ph, ctypes.c_char_p(curveId.encode(self.charset)), ctypes.byref(j)) if ierr!=0: raise ENtoolkitError(self, ierr) return j.value def ENgetcurvelen(self, curveIndex): j= ctypes.c_int() ierr= self._lib.EN_getcurvelen(self.ph, ctypes.c_int(curveIndex), ctypes.byref(j)) if ierr!=0: raise ENtoolkitError(self, ierr) return j.value def ENgetcurvevalue(self, curveIndex, point): x = ctypes.c_float() y = ctypes.c_float() ierr= self._lib.EN_getcurvevalue(self.ph, ctypes.c_int(curveIndex), ctypes.c_int(point-1), ctypes.byref(x), ctypes.byref(y)) if ierr!=0: raise ENtoolkitError(self, ierr) return x.value, y.value EN_ELEVATION = 0 # /* Node parameters */ EN_BASEDEMAND = 1 EN_PATTERN = 2 EN_EMITTER = 3 EN_INITQUAL = 4 EN_SOURCEQUAL = 5 EN_SOURCEPAT = 6 EN_SOURCETYPE = 7 EN_TANKLEVEL = 8 EN_DEMAND = 9 EN_HEAD = 10 EN_PRESSURE = 11 EN_QUALITY = 12 EN_SOURCEMASS = 13 EN_INITVOLUME = 14 EN_MIXMODEL = 15 EN_MIXZONEVOL = 16 EN_TANKDIAM = 17 EN_MINVOLUME = 18 EN_VOLCURVE = 19 EN_MINLEVEL = 20 EN_MAXLEVEL = 21 EN_MIXFRACTION = 22 EN_TANK_KBULK = 23 EN_DIAMETER = 0 # /* Link parameters */ EN_LENGTH = 1 EN_ROUGHNESS = 2 EN_MINORLOSS = 3 EN_INITSTATUS = 4 EN_INITSETTING = 5 EN_KBULK = 6 EN_KWALL = 7 EN_FLOW = 8 EN_VELOCITY = 9 EN_HEADLOSS = 10 EN_STATUS = 11 EN_SETTING = 12 EN_ENERGY = 13 EN_DURATION = 0 # /* Time parameters */ EN_HYDSTEP = 1 EN_QUALSTEP = 2 EN_PATTERNSTEP = 3 EN_PATTERNSTART = 4 EN_REPORTSTEP = 5 EN_REPORTSTART = 6 EN_RULESTEP = 7 EN_STATISTIC = 8 EN_PERIODS = 9 EN_NODECOUNT = 0 # /* Component counts */ EN_TANKCOUNT = 1 EN_LINKCOUNT = 2 EN_PATCOUNT = 3 EN_CURVECOUNT = 4 EN_CONTROLCOUNT = 5 EN_JUNCTION = 0 # /* Node types */ EN_RESERVOIR = 1 EN_TANK = 2 EN_CVPIPE = 0 # /* Link types */ EN_PIPE = 1 EN_PUMP = 2 EN_PRV = 3 EN_PSV = 4 EN_PBV = 5 EN_FCV = 6 EN_TCV = 7 EN_GPV = 8 EN_NONE = 0 # /* Quality analysis types */ EN_CHEM = 1 EN_AGE = 2 EN_TRACE = 3 EN_CONCEN = 0 # /* Source quality types */ EN_MASS = 1 EN_SETPOINT = 2 EN_FLOWPACED = 3 EN_CFS = 0 # /* Flow units types */ EN_GPM = 1 EN_MGD = 2 EN_IMGD = 3 EN_AFD = 4 EN_LPS = 5 EN_LPM = 6 EN_MLD = 7 EN_CMH = 8 EN_CMD = 9 EN_HW = 0 EN_DW = 1 EN_CM = 2 EN_TRIALS = 0 # /* Misc. options */ EN_ACCURACY = 1 EN_TOLERANCE = 2 EN_EMITEXPON = 3 EN_DEMANDMULT = 4 EN_LOWLEVEL = 0 # /* Control types */ EN_HILEVEL = 1 EN_TIMER = 2 EN_TIMEOFDAY = 3 EN_AVERAGE = 1 # /* Time statistic types. */ EN_MINIMUM = 2 EN_MAXIMUM = 3 EN_RANGE = 4 EN_MIX1 = 0 # /* Tank mixing models */ EN_MIX2 = 1 EN_FIFO = 2 EN_LIFO = 3 EN_NOSAVE = 0 # /* Save-results-to-file flag */ EN_SAVE = 1 EN_INITFLOW = 10 # /* Re-initialize flow flag */ FlowUnits= { EN_CFS :"cfs" , EN_GPM :"gpm" , EN_MGD :"a-f/d" , EN_IMGD:"mgd" , EN_AFD :"Imgd" , EN_LPS :"L/s" , EN_LPM :"Lpm" , EN_MLD :"m3/h" , EN_CMH :"m3/d" , EN_CMD :"ML/d" } class ENtoolkitError(Exception): def __init__(self, epanet2, ierr): self.warning= ierr < 100 self.args= (ierr,) self.message = epanet2.ENgeterror(ierr) if self.message=='' and ierr!=0: self.message='ENtoolkit Undocumented Error '+str(ierr)+': look at text.h in epanet sources' def __str__(self): return self.message