diff --git a/build/CMake/CMakeLists.txt b/build/CMake/CMakeLists.txt index caa4fa2..54fbc32 100644 --- a/build/CMake/CMakeLists.txt +++ b/build/CMake/CMakeLists.txt @@ -20,4 +20,4 @@ target_link_libraries (runepanet LINK_PUBLIC epanet m) # the binary hydraulics file API include_directories(../../tools/outputapi) -add_library(epanet_bin_out STATIC ../../tools/outputapi/outputapi.c) +add_library(ENBinaryOut SHARED ../../tools/outputapi/outputapi.c) diff --git a/tests/network_tests/net1/net1.enb b/tests/network_tests/net1/net1.enb index e69de29..489729c 100644 Binary files a/tests/network_tests/net1/net1.enb and b/tests/network_tests/net1/net1.enb differ diff --git a/tests/network_tests/net1/net1.inp b/tests/network_tests/net1/net1.inp index e69de29..4df5bbf 100644 --- a/tests/network_tests/net1/net1.inp +++ b/tests/network_tests/net1/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_networks.sh b/tests/test_networks.sh index 3e3d07a..a6527b8 100755 --- a/tests/test_networks.sh +++ b/tests/test_networks.sh @@ -7,14 +7,14 @@ for d in network_tests/*/ ; do officialBinFile=${netfile%.*}.enb candidateBinFile=${netfile%.*}-candidate.enb echo "testing $netfile with known good binary output $officialBinFile" - if true ## path/to/runepanet $netfile ${netfile%.*}-candidate.rpt $candidateBinFile + if ../build/CMake/buildproducts/bin/runepanet $netfile ${netfile%.*}-candidate.rpt $candidateBinFile then echo "epanet run for $netfile SUCCESS" else echo "epanet run for $netfile FAILED" returnValue=1 fi - if python compare_enb.py $officialBinFile $candidateBinFile + if python ENBinaryOutDiff.py $officialBinFile $candidateBinFile then echo "binary output for $netfile PASSED" else @@ -27,4 +27,9 @@ done return $returnValue } +cp ../build/CMake/buildproducts/bin/libENBinaryOut.* ./libENBinaryOut.so +cp ../tools/outputapi/*.py ./ + + + test_networks diff --git a/tools/outputapi/ENBinaryOutDiff.py b/tools/outputapi/ENBinaryOutDiff.py new file mode 100644 index 0000000..0b6e06f --- /dev/null +++ b/tools/outputapi/ENBinaryOutDiff.py @@ -0,0 +1 @@ +''' Compares Two EPANET binary output files. Author: Bryant E. McDonnell Date: 12/16/2015 Compares the absolute value of two values against a given threshold value. ******************* Command Line Arguments: python <*.out 1> <*.out 2> Returns True / False (Pass / Fail, respectively) ******************* ''' import sys import os from math import log from ENOutputWrapper import * def BinCompare(args): # Some Error Checking for Command Line Arguments.... if len(args) < 3: raise Exception("Not Enough Input Arguments: python <*.out 1> <*.out 2>") # if not args[1].endswith('.out') or not args[2].endswith('.out'): # raise Exception("Wrong file extension: python <*.out 1> <*.out 2>") print(sys.argv[1],sys.argv[2]) dllLoc = '' if (sys.platform == 'linux2' or sys.platform == 'darwin'): dllLoc = os.getcwd() + '/libENBinaryOut.so' else: raise Exception("only implemented on mac/linux") BinFile1 = OutputObject(dllLoc) BinFile1.OpenOutputFile(args[1]) BinFile1.get_NetSize() BinFile1.get_Times() BinFile2 = OutputObject(dllLoc) BinFile2.OpenOutputFile(args[2]) ####ENR_NodeAttribute; ##ENR_demand = 0 ##ENR_head = 1 ##ENR_pressure = 2 ##ENR_quality = 3 NumberOfNodeAttr = 4 ####ENR_LinkAttribute; ##ENR_flow = 0 ##ENR_velocity = 1 ##ENR_headloss = 2 ##ENR_avgQuality = 3 ##ENR_status = 4 ##ENR_setting = 5 ##ENR_rxRate = 6 ##ENT_frctnFctr = 7 NumberOfLinkAttr = 8 NumberOfPeriods = BinFile1.numPeriods # Set Tolerances for each attribute # demand, head, pressure, quality NodeAttributeTolerances = [1e-6, 1e-6, 1e-6, 1e-6] # flow, velocity, headloss, avgQuality, status, setting, rxRate, frctnFctr LinkAttributeTolerances = [1e-6, 1e-6, 1e-6, 1e-6, 1e-6, 1e-6, 1e-6, 1e-6] #Compare Node Attributes for nodeAttrInd in range(NumberOfNodeAttr): for TSind in range(NumberOfPeriods): #Get 1 attribute for all nodes at time t NodeAttributeOut1 = BinFile1.get_NodeAttribute(nodeAttrInd, TSind) #Get 1 attribute for all nodes at time t NodeAttributeOut2 = BinFile2.get_NodeAttribute(nodeAttrInd, TSind) for Nodeind, NodeAttrVal in enumerate(NodeAttributeOut1): if abs(NodeAttrVal - NodeAttributeOut2[Nodeind]) > 0: diff = abs(NodeAttrVal - NodeAttributeOut2[Nodeind] ) if diff > NodeAttributeTolerances[nodeAttrInd]: return False #Compare Link Attributes for linkAttrInd in range(NumberOfLinkAttr): for TSind in range(NumberOfPeriods): #Get 1 attribute for all links at time t LinkAttributeOut1 = BinFile1.get_NodeAttribute(linkAttrInd, TSind) #Get 1 attribute for all links at time t LinkAttributeOut2 = BinFile2.get_NodeAttribute(linkAttrInd, TSind) for linkind, LinkAttrVal in enumerate(LinkAttributeOut1): if abs(LinkAttrVal - LinkAttributeOut2[linkind]) > 0: diff = abs(LinkAttrVal - LinkAttributeOut2[linkind] ) if diff > LinkAttributeTolerances[linkAttrInd]: return False return True if __name__ == '__main__': if(BinCompare(sys.argv)): sys.exit(0) else: sys.exit(1) \ No newline at end of file diff --git a/tools/outputapi/ENOutputWrapper.py b/tools/outputapi/ENOutputWrapper.py index a3dcb59..8f934d4 100644 --- a/tools/outputapi/ENOutputWrapper.py +++ b/tools/outputapi/ENOutputWrapper.py @@ -1 +1 @@ -''' Wrapper for EPANET Output API. Author: Bryant E. McDonnell Date: 12/7/2015 Language: Anglais ''' from ctypes import * from _ENOutputToolkit import * #Used just to pull the Pointer of the ENResultsAPI struct class _Opaque(Structure): ''' Used soley for passing the pointer to the enrapi struct to API ''' pass class OutputObject: def __init__(self, dllLoc): ''' Instantiate python Wrapper Object and build Wrapper functions. ''' try: self.DLL = CDLL(dllLoc) except: raise Exception('Failed to Open Linked Library') ###ENResultsAPI* DLLEXPORT ENR_alloc(void); self._enrapiFunc = self.DLL.ENR_alloc self._enrapiFunc.restype = POINTER(_Opaque) ###int DLLEXPORT ENR_open(ENResultsAPI* enrapi, const char* path); self._OpenFunc = self.DLL.ENR_open ###int DLLEXPORT ENR_getNetSize(ENResultsAPI* enrapi, ENR_ElementCount code, int* count); self._GetNetSize = self.DLL.ENR_getNetSize ###int DLLEXPORT ENR_getUnits(ENResultsAPI* enrapi, ENR_Unit code, int* unitFlag); self._GetUnits = self.DLL.ENR_getUnits self._GetUnits.argtypes = [POINTER(_Opaque), c_int, POINTER(c_int)] self._GetUnits.restype = c_int ###int DLLEXPORT ENR_getTimes(ENResultsAPI* enrapi, ENR_Time code, int* time) self._getTimes = self.DLL.ENR_getTimes ###float* ENR_newOutValueSeries(ENResultsAPI* enrapi, int seriesStart, ### int seriesLength, int* length, int* errcode); self._newOutValueSeries = self.DLL.ENR_newOutValueSeries self._newOutValueSeries.argtypes = [POINTER(_Opaque), c_int, c_int, POINTER(c_int), POINTER(c_int)] self._newOutValueSeries.restype = POINTER(c_float) ###float* ENR_newOutValueArray(ENResultsAPI* enrapi, ENR_ApiFunction func, ### ENR_ElementType type, int* length, int* errcode); self._newOutValueArray = self.DLL.ENR_newOutValueArray self._newOutValueArray.argtypes = [POINTER(_Opaque), c_int, c_int, POINTER(c_int), POINTER(c_int)] self._newOutValueArray.restype = POINTER(c_float) ###int DLLEXPORT ENR_getNodeSeries(ENResultsAPI* enrapi, int nodeIndex, ENR_NodeAttribute attr, ### int timeIndex, int length, float* outValueSeries, int* len); self._getNodeSeries = self.DLL.ENR_getNodeSeries ###int DLLEXPORT ENR_getLinkSeries(ENResultsAPI* enrapi, int linkIndex, ENR_LinkAttribute attr, ### int timeIndex, int length, float* outValueSeries); self._getLinkSeries = self.DLL.ENR_getLinkSeries ###int DLLEXPORT ENR_getNodeAttribute(ENResultsAPI* enrapi, int timeIndex, ### ENR_NodeAttribute attr, float* outValueArray); self._getNodeAttribute = self.DLL.ENR_getNodeAttribute ###int DLLEXPORT ENT_getLinkAttribute(ENResultsAPI* enrapi, int timeIndex, ### ENR_LinkAttribute attr, float* outValueArray); self._getLinkAttribute = self.DLL.ENR_getLinkAttribute ###int DLLEXPORT ENR_getNodeResult(ENResultsAPI* enrapi, int timeIndex, int nodeIndex, ### float* outValueArray); self._getNodeResult = self.DLL.ENR_getNodeResult ###int DLLEXPORT ENR_getLinkResult(ENResultsAPI* enrapi, int timeIndex, int linkIndex, ### float* outValueArray); self._getLinkResult = self.DLL.ENR_getLinkResult ###int DLLEXPORT ENR_free(float *array); self._free = self.DLL.ENR_free ###int DLLEXPORT ENR_close(ENResultsAPI* enrapi); self._CloseOut = self.DLL.ENR_close ###int DLLEXPORT ENR_errMessage(int errcode, char* errmsg, int n); self._RetErrMessage = self.DLL.ENR_errMessage def OpenOutputFile(self, binfile): ''' 1) Initializes the opaque pointer to enrapi struct. 2) Opens the output file. ''' self.enrapi = self._enrapiFunc() ret = self._OpenFunc(self.enrapi, binfile) if ret != 0: self.CloseOutputFile(self.enrapi) raise Exception('Failed to open OutputFile') ##Update Function at a later date ## def RaiseError(self, ErrNo): ## ErMsg = c_char_p(256) ## ## self._RetErrMessage(ErrNo , ErMsg, 256) ## ## print ErMsg def get_Units(self): ''' Purpose: Returns pressure and flow units ''' unit = c_int() self._GetUnits(self.enrapi, ENR_flowUnits, unit) self.flowUnits = unit.value unit = c_int() self._GetUnits(self.enrapi, ENR_pressUnits, unit) self.pressUnits = unit.value def get_NetSize(self): ''' Populates object attributes with the water object counts ''' count = c_int() self._GetNetSize(self.enrapi, ENR_nodeCount, byref(count)) self.nodeCount = count.value count = c_int() self._GetNetSize(self.enrapi, ENR_tankCount, byref(count)) self.tankCount = count.value count = c_int() self._GetNetSize(self.enrapi, ENR_linkCount, byref(count)) self.linkCount = count.value count = c_int() self._GetNetSize(self.enrapi, ENR_pumpCount, byref(count)) self.pumpCount = count.value count = c_int() self._GetNetSize(self.enrapi, ENR_valveCount, byref(count)) self.valveCount = count.value def get_Times(self): ''' Purpose: Returns report and simulation time related parameters. ''' temp = c_int() self._getTimes(self.enrapi, ENR_reportStart, byref(temp)) self.reportStart = temp.value temp = c_int() self._getTimes(self.enrapi, ENR_reportStep, byref(temp)) self.reportStep = temp.value temp = c_int() self._getTimes(self.enrapi, ENR_simDuration, byref(temp)) self.simDuration = temp.value temp = c_int() self._getTimes(self.enrapi, ENR_numPeriods, byref(temp)) self.numPeriods = temp.value def get_NodeSeries(self, NodeInd, NodeAttr, SeriesStartInd = 0, SeriesLen = -1): ''' Purpose: Get time series results for particular attribute. Specify series start and length using seriesStart and seriesLength respectively. SeriesLen = -1 Default input: Gets data from Series Start Ind to end ''' if not hasattr(self, 'numPeriods'): self.get_Times() if SeriesLen > self.numPeriods : raise Exception("Outside Number of TimeSteps") elif SeriesLen == -1: SeriesLen = self.numPeriods sLength = c_int() ErrNo1 = c_int() SeriesPtr = self._newOutValueSeries(self.enrapi, SeriesStartInd,\ SeriesLen, byref(sLength), byref(ErrNo1)) ErrNo2 = self._getNodeSeries(self.enrapi, NodeInd, NodeAttr, \ SeriesStartInd, sLength.value, SeriesPtr) BldArray = [SeriesPtr[i] for i in range(sLength.value)] self._free(SeriesPtr) return BldArray def get_LinkSeries(self, LinkInd, LinkAttr, SeriesStartInd = 0, SeriesLen = -1): ''' Purpose: Get time series results for particular attribute. Specify series start and length using seriesStart and seriesLength respectively. SeriesLen = -1 Default input: Gets data from Series Start Ind to end ''' if not hasattr(self, 'numPeriods'): self.get_Times() if SeriesLen > self.numPeriods : raise Exception("Outside Number of TimeSteps") elif SeriesLen == -1: SeriesLen = self.numPeriods sLength = c_int() ErrNo1 = c_int() SeriesPtr = self._newOutValueSeries(self.enrapi, SeriesStartInd,\ SeriesLen, byref(sLength), byref(ErrNo1)) ErrNo2 = self._getLinkSeries(self.enrapi, LinkInd, LinkAttr, \ SeriesStartInd, sLength.value, SeriesPtr) BldArray = [SeriesPtr[i] for i in range(sLength.value)] ret = self._free(SeriesPtr) return BldArray def get_NodeAttribute(self, NodeAttr, TimeInd): ''' Purpose: For all nodes at given time, get a particular attribute ''' if not hasattr(self, 'nodeCount'): self.get_NetSize() alength = c_int() ErrNo1 = c_int() ValArrayPtr = self._newOutValueArray(self.enrapi, ENR_getAttribute,\ ENR_node, byref(alength), byref(ErrNo1)) ErrNo2 = self._getNodeAttribute(self.enrapi, TimeInd, NodeAttr, ValArrayPtr) BldArray = [ValArrayPtr[i] for i in range(alength.value)] self._free(ValArrayPtr) return BldArray def get_LinkAttribute(self, LinkAttr, TimeInd): ''' Purpose: For all links at given time, get a particular attribute ''' if not hasattr(self, 'linkCount'): self.get_NetSize() alength = c_int() ErrNo1 = c_int() ValArrayPtr = self._newOutValueArray(self.enrapi, ENR_getAttribute,\ ENR_link, byref(alength), byref(ErrNo1)) ErrNo2 = self._getLinkAttribute(self.enrapi, TimeInd, LinkAttr, ValArrayPtr) BldArray = [ValArrayPtr[i] for i in range(alength.value)] self._free(ValArrayPtr) return BldArray def get_NodeResult(self, NodeInd, TimeInd): ''' Purpose: For a node at given time, get all attributes ''' alength = c_int() ErrNo1 = c_int() ValArrayPtr = self._newOutValueArray(self.enrapi, ENR_getResult,\ ENR_node, byref(alength), byref(ErrNo1)) ErrNo2 = self._getNodeResult(self.enrapi, TimeInd, NodeInd, ValArrayPtr) BldArray = [ValArrayPtr[i] for i in range(alength.value)] self._free(ValArrayPtr) return BldArray def get_LinkResult(self, LinkInd, TimeInd): ''' Purpose: For a link at given time, get all attributes ''' alength = c_int() ErrNo1 = c_int() ValArrayPtr = self._newOutValueArray(self.enrapi, ENR_getResult,\ ENR_link, byref(alength), byref(ErrNo1)) ErrNo2 = self._getLinkResult(self.enrapi, TimeInd, LinkInd, ValArrayPtr) BldArray = [ValArrayPtr[i] for i in range(alength.value)] self._free(ValArrayPtr) return BldArray def CloseOutputFile(self): ''' Call to close binary file. ''' ret = self._CloseOut(self.enrapi) if ret != 0: raise Exception('Failed to Close *.out file') if __name__ in "__main__": dllLoc = 'outputAPI.dll' binfile = 'C:\\PROJECTCODE\\EPANEToutputAPI\\Net3.out' print("Testing Wrapper...") print("Test Network 3...") print("-->Instantiating DLL Object / Opening DLL") self = OutputObject(dllLoc) print("...Opened DLL") print("\n-->Reading Binary File") self.OpenOutputFile(binfile) print("...Opened Bin File") print("\n-->get_NodeSeries") LENTest = -1 demands = self.get_NodeSeries(0,ENR_demand, 0, LENTest) head = self.get_NodeSeries(0,ENR_head, 0, LENTest) pressure = self.get_NodeSeries(0,ENR_pressure, 0, LENTest) quality = self.get_NodeSeries(0,ENR_quality, 0, LENTest) print('\t'.join(["ind","demand","head","pressure","quality"])) for ind, val in enumerate(demands): print ind, val, head[ind], pressure[ind], quality[ind] print("\n-->get_LinkSeries") flow = self.get_LinkSeries(0,ENR_flow) velocity =self.get_LinkSeries(0,ENR_velocity) headloss =self.get_LinkSeries(0,ENR_headloss) avgQuality=self.get_LinkSeries(0,ENR_avgQuality) status=self.get_LinkSeries(0,ENR_status) setting=self.get_LinkSeries(0,ENR_setting) rxRate=self.get_LinkSeries(0,ENR_rxRate) frctnFctr =self.get_LinkSeries(0,ENT_frctnFctr) print('\t'.join(["ind","flow","velocity","headloss","avgQuality",\ "status","setting","rxRate","frctnFctr"])) for ind, val in enumerate(flow): print ind, val, velocity[ind],headloss[ind],avgQuality[ind],status[ind],setting[ind],rxRate[ind],frctnFctr[ind] print("\n-->get_NodeAttribute") demand_1 = self.get_NodeAttribute(ENR_demand,0) head_1 = self.get_NodeAttribute(ENR_head,0) pressure_1 = self.get_NodeAttribute(ENR_pressure,0) quality_1 = self.get_NodeAttribute(ENR_quality,0) print('\t'.join(["ind","demand","head","pressure","quality"])) for ind, val in enumerate(demand_1): print ind, val, head_1[ind], pressure_1[ind], quality_1[ind] print("\n-->get_LinkAttribute") flow1 = self.get_LinkAttribute(ENR_flow,0) velocity1 =self.get_LinkAttribute(ENR_velocity,0) headloss1 =self.get_LinkAttribute(ENR_headloss,0) avgQuality1=self.get_LinkAttribute(ENR_avgQuality,0) status1=self.get_LinkAttribute(ENR_status,0) setting1=self.get_LinkAttribute(ENR_setting,0) rxRate1=self.get_LinkAttribute(ENR_rxRate,0) frctnFctr1 =self.get_LinkAttribute(ENT_frctnFctr,0) print('\t'.join(["ind","flow","velocity","headloss","avgQuality",\ "status","setting","rxRate","frctnFctr"])) for ind, val in enumerate(flow1): print ind, val, velocity1[ind],headloss1[ind],avgQuality1[ind],status1[ind],setting1[ind],rxRate1[ind],frctnFctr1[ind] print("\n-->Object Counts") print("Nodes") print(self.nodeCount) print("Nodes") print(self.nodeCount) print("Tanks") print(self.tankCount) print("Links") print(self.linkCount) print("Pumps") print(self.pumpCount) print("Values") print(self.valveCount) print("\n-->get_NodeResult") print(self.get_NodeResult(96,24)) print("\n-->get_LinkResult") print(self.get_LinkResult(0,0)) print("\n-->get_Units") self.get_Units() print("...flow unit code") print(self.flowUnits) print("...pressure unit code") print(self.pressUnits) print("\n-->Closing Binary File") self.CloseOutputFile() print("...Closed Binary File") \ No newline at end of file +''' Wrapper for EPANET Output API. Author: Bryant E. McDonnell Date: 12/7/2015 Language: Anglais ''' from ctypes import * ##ENR_ElementType; ENR_node = 1 ENR_link = 2 ##ENR_ApiFunction; ENR_getSeries = 1 ENR_getAttribute = 2 ENR_getResult = 3 ##ENR_ElementCount; ENR_nodeCount = 1 ENR_tankCount = 2 ENR_linkCount = 3 ENR_pumpCount = 4 ENR_valveCount = 5 ##ENR_Unit; ENR_flowUnits = 1 ENR_pressUnits = 2 ##ENR_Time; ENR_reportStart = 1 ENR_reportStep = 2 ENR_simDuration = 3 ENR_numPeriods = 4 ##ENR_NodeAttribute; ENR_demand = 0 ENR_head = 1 ENR_pressure = 2 ENR_quality = 3 ##ENR_LinkAttribute; ENR_flow = 0 ENR_velocity = 1 ENR_headloss = 2 ENR_avgQuality = 3 ENR_status = 4 ENR_setting = 5 ENR_rxRate = 6 ENT_frctnFctr = 7 #Used just to pull the Pointer of the ENResultsAPI struct class _Opaque(Structure): ''' Used soley for passing the pointer to the enrapi struct to API ''' pass class OutputObject: def __init__(self, dllLoc): ''' Instantiate python Wrapper Object and build Wrapper functions. ''' try: self.DLL = CDLL(dllLoc) except: raise Exception('Failed to Open Linked Library') ###ENResultsAPI* DLLEXPORT ENR_alloc(void); self._enrapiFunc = self.DLL.ENR_alloc self._enrapiFunc.restype = POINTER(_Opaque) ###int DLLEXPORT ENR_open(ENResultsAPI* enrapi, const char* path); self._OpenFunc = self.DLL.ENR_open ###int DLLEXPORT ENR_getNetSize(ENResultsAPI* enrapi, ENR_ElementCount code, int* count); self._GetNetSize = self.DLL.ENR_getNetSize ###int DLLEXPORT ENR_getUnits(ENResultsAPI* enrapi, ENR_Unit code, int* unitFlag); self._GetUnits = self.DLL.ENR_getUnits self._GetUnits.argtypes = [POINTER(_Opaque), c_int, POINTER(c_int)] self._GetUnits.restype = c_int ###int DLLEXPORT ENR_getTimes(ENResultsAPI* enrapi, ENR_Time code, int* time) self._getTimes = self.DLL.ENR_getTimes ###float* ENR_newOutValueSeries(ENResultsAPI* enrapi, int seriesStart, ### int seriesLength, int* length, int* errcode); self._newOutValueSeries = self.DLL.ENR_newOutValueSeries self._newOutValueSeries.argtypes = [POINTER(_Opaque), c_int, c_int, POINTER(c_int), POINTER(c_int)] self._newOutValueSeries.restype = POINTER(c_float) ###float* ENR_newOutValueArray(ENResultsAPI* enrapi, ENR_ApiFunction func, ### ENR_ElementType type, int* length, int* errcode); self._newOutValueArray = self.DLL.ENR_newOutValueArray self._newOutValueArray.argtypes = [POINTER(_Opaque), c_int, c_int, POINTER(c_int), POINTER(c_int)] self._newOutValueArray.restype = POINTER(c_float) ###int DLLEXPORT ENR_getNodeSeries(ENResultsAPI* enrapi, int nodeIndex, ENR_NodeAttribute attr, ### int timeIndex, int length, float* outValueSeries, int* len); self._getNodeSeries = self.DLL.ENR_getNodeSeries ###int DLLEXPORT ENR_getLinkSeries(ENResultsAPI* enrapi, int linkIndex, ENR_LinkAttribute attr, ### int timeIndex, int length, float* outValueSeries); self._getLinkSeries = self.DLL.ENR_getLinkSeries ###int DLLEXPORT ENR_getNodeAttribute(ENResultsAPI* enrapi, int timeIndex, ### ENR_NodeAttribute attr, float* outValueArray); self._getNodeAttribute = self.DLL.ENR_getNodeAttribute ###int DLLEXPORT ENT_getLinkAttribute(ENResultsAPI* enrapi, int timeIndex, ### ENR_LinkAttribute attr, float* outValueArray); self._getLinkAttribute = self.DLL.ENR_getLinkAttribute ###int DLLEXPORT ENR_getNodeResult(ENResultsAPI* enrapi, int timeIndex, int nodeIndex, ### float* outValueArray); self._getNodeResult = self.DLL.ENR_getNodeResult ###int DLLEXPORT ENR_getLinkResult(ENResultsAPI* enrapi, int timeIndex, int linkIndex, ### float* outValueArray); self._getLinkResult = self.DLL.ENR_getLinkResult ###int DLLEXPORT ENR_free(float *array); self._free = self.DLL.ENR_free ###int DLLEXPORT ENR_close(ENResultsAPI* enrapi); self._CloseOut = self.DLL.ENR_close ###int DLLEXPORT ENR_errMessage(int errcode, char* errmsg, int n); self._RetErrMessage = self.DLL.ENR_errMessage def OpenOutputFile(self, binfile): ''' 1) Initializes the opaque pointer to enrapi struct. 2) Opens the output file. ''' self.enrapi = self._enrapiFunc() ret = self._OpenFunc(self.enrapi, binfile) if ret != 0: self.CloseOutputFile() raise Exception('Failed to open OutputFile') ##Update Function at a later date ## def RaiseError(self, ErrNo): ## ErMsg = c_char_p(256) ## ## self._RetErrMessage(ErrNo , ErMsg, 256) ## ## print ErMsg def get_Units(self): ''' Purpose: Returns pressure and flow units ''' unit = c_int() self._GetUnits(self.enrapi, ENR_flowUnits, unit) self.flowUnits = unit.value unit = c_int() self._GetUnits(self.enrapi, ENR_pressUnits, unit) self.pressUnits = unit.value def get_NetSize(self): ''' Populates object attributes with the water object counts ''' count = c_int() self._GetNetSize(self.enrapi, ENR_nodeCount, byref(count)) self.nodeCount = count.value count = c_int() self._GetNetSize(self.enrapi, ENR_tankCount, byref(count)) self.tankCount = count.value count = c_int() self._GetNetSize(self.enrapi, ENR_linkCount, byref(count)) self.linkCount = count.value count = c_int() self._GetNetSize(self.enrapi, ENR_pumpCount, byref(count)) self.pumpCount = count.value count = c_int() self._GetNetSize(self.enrapi, ENR_valveCount, byref(count)) self.valveCount = count.value def get_Times(self): ''' Purpose: Returns report and simulation time related parameters. ''' temp = c_int() self._getTimes(self.enrapi, ENR_reportStart, byref(temp)) self.reportStart = temp.value temp = c_int() self._getTimes(self.enrapi, ENR_reportStep, byref(temp)) self.reportStep = temp.value temp = c_int() self._getTimes(self.enrapi, ENR_simDuration, byref(temp)) self.simDuration = temp.value temp = c_int() self._getTimes(self.enrapi, ENR_numPeriods, byref(temp)) self.numPeriods = temp.value def get_NodeSeries(self, NodeInd, NodeAttr, SeriesStartInd = 0, SeriesLen = -1): ''' Purpose: Get time series results for particular attribute. Specify series start and length using seriesStart and seriesLength respectively. SeriesLen = -1 Default input: Gets data from Series Start Ind to end ''' if not hasattr(self, 'numPeriods'): self.get_Times() if SeriesLen > self.numPeriods : raise Exception("Outside Number of TimeSteps") elif SeriesLen == -1: SeriesLen = self.numPeriods sLength = c_int() ErrNo1 = c_int() SeriesPtr = self._newOutValueSeries(self.enrapi, SeriesStartInd,\ SeriesLen, byref(sLength), byref(ErrNo1)) ErrNo2 = self._getNodeSeries(self.enrapi, NodeInd, NodeAttr, \ SeriesStartInd, sLength.value, SeriesPtr) BldArray = [SeriesPtr[i] for i in range(sLength.value)] self._free(SeriesPtr) return BldArray def get_LinkSeries(self, LinkInd, LinkAttr, SeriesStartInd = 0, SeriesLen = -1): ''' Purpose: Get time series results for particular attribute. Specify series start and length using seriesStart and seriesLength respectively. SeriesLen = -1 Default input: Gets data from Series Start Ind to end ''' if not hasattr(self, 'numPeriods'): self.get_Times() if SeriesLen > self.numPeriods : raise Exception("Outside Number of TimeSteps") elif SeriesLen == -1: SeriesLen = self.numPeriods sLength = c_int() ErrNo1 = c_int() SeriesPtr = self._newOutValueSeries(self.enrapi, SeriesStartInd,\ SeriesLen, byref(sLength), byref(ErrNo1)) ErrNo2 = self._getLinkSeries(self.enrapi, LinkInd, LinkAttr, \ SeriesStartInd, sLength.value, SeriesPtr) BldArray = [SeriesPtr[i] for i in range(sLength.value)] ret = self._free(SeriesPtr) return BldArray def get_NodeAttribute(self, NodeAttr, TimeInd): ''' Purpose: For all nodes at given time, get a particular attribute ''' if not hasattr(self, 'nodeCount'): self.get_NetSize() alength = c_int() ErrNo1 = c_int() ValArrayPtr = self._newOutValueArray(self.enrapi, ENR_getAttribute,\ ENR_node, byref(alength), byref(ErrNo1)) ErrNo2 = self._getNodeAttribute(self.enrapi, TimeInd, NodeAttr, ValArrayPtr) BldArray = [ValArrayPtr[i] for i in range(alength.value)] self._free(ValArrayPtr) return BldArray def get_LinkAttribute(self, LinkAttr, TimeInd): ''' Purpose: For all links at given time, get a particular attribute ''' if not hasattr(self, 'linkCount'): self.get_NetSize() alength = c_int() ErrNo1 = c_int() ValArrayPtr = self._newOutValueArray(self.enrapi, ENR_getAttribute,\ ENR_link, byref(alength), byref(ErrNo1)) ErrNo2 = self._getLinkAttribute(self.enrapi, TimeInd, LinkAttr, ValArrayPtr) BldArray = [ValArrayPtr[i] for i in range(alength.value)] self._free(ValArrayPtr) return BldArray def get_NodeResult(self, NodeInd, TimeInd): ''' Purpose: For a node at given time, get all attributes ''' alength = c_int() ErrNo1 = c_int() ValArrayPtr = self._newOutValueArray(self.enrapi, ENR_getResult,\ ENR_node, byref(alength), byref(ErrNo1)) ErrNo2 = self._getNodeResult(self.enrapi, TimeInd, NodeInd, ValArrayPtr) BldArray = [ValArrayPtr[i] for i in range(alength.value)] self._free(ValArrayPtr) return BldArray def get_LinkResult(self, LinkInd, TimeInd): ''' Purpose: For a link at given time, get all attributes ''' alength = c_int() ErrNo1 = c_int() ValArrayPtr = self._newOutValueArray(self.enrapi, ENR_getResult,\ ENR_link, byref(alength), byref(ErrNo1)) ErrNo2 = self._getLinkResult(self.enrapi, TimeInd, LinkInd, ValArrayPtr) BldArray = [ValArrayPtr[i] for i in range(alength.value)] self._free(ValArrayPtr) return BldArray def CloseOutputFile(self): ''' Call to close binary file. ''' ret = self._CloseOut(self.enrapi) if ret != 0: raise Exception('Failed to Close *.out file') if __name__ in "__main__": dllLoc = 'outputAPI.dll' binfile = 'C:\\PROJECTCODE\\EPANEToutputAPI\\Net3.out' print("Testing Wrapper...") print("Test Network 3...") print("-->Instantiating DLL Object / Opening DLL") self = OutputObject(dllLoc) print("...Opened DLL") print("\n-->Reading Binary File") self.OpenOutputFile(binfile) print("...Opened Bin File") print("\n-->get_NodeSeries") LENTest = -1 demands = self.get_NodeSeries(0,ENR_demand, 0, LENTest) head = self.get_NodeSeries(0,ENR_head, 0, LENTest) pressure = self.get_NodeSeries(0,ENR_pressure, 0, LENTest) quality = self.get_NodeSeries(0,ENR_quality, 0, LENTest) print('\t'.join(["ind","demand","head","pressure","quality"])) for ind, val in enumerate(demands): print ind, val, head[ind], pressure[ind], quality[ind] print("\n-->get_LinkSeries") flow = self.get_LinkSeries(0,ENR_flow) velocity =self.get_LinkSeries(0,ENR_velocity) headloss =self.get_LinkSeries(0,ENR_headloss) avgQuality=self.get_LinkSeries(0,ENR_avgQuality) status=self.get_LinkSeries(0,ENR_status) setting=self.get_LinkSeries(0,ENR_setting) rxRate=self.get_LinkSeries(0,ENR_rxRate) frctnFctr =self.get_LinkSeries(0,ENT_frctnFctr) print('\t'.join(["ind","flow","velocity","headloss","avgQuality",\ "status","setting","rxRate","frctnFctr"])) for ind, val in enumerate(flow): print ind, val, velocity[ind],headloss[ind],avgQuality[ind],status[ind],setting[ind],rxRate[ind],frctnFctr[ind] print("\n-->get_NodeAttribute") demand_1 = self.get_NodeAttribute(ENR_demand,0) head_1 = self.get_NodeAttribute(ENR_head,0) pressure_1 = self.get_NodeAttribute(ENR_pressure,0) quality_1 = self.get_NodeAttribute(ENR_quality,0) print('\t'.join(["ind","demand","head","pressure","quality"])) for ind, val in enumerate(demand_1): print ind, val, head_1[ind], pressure_1[ind], quality_1[ind] print("\n-->get_LinkAttribute") flow1 = self.get_LinkAttribute(ENR_flow,0) velocity1 =self.get_LinkAttribute(ENR_velocity,0) headloss1 =self.get_LinkAttribute(ENR_headloss,0) avgQuality1=self.get_LinkAttribute(ENR_avgQuality,0) status1=self.get_LinkAttribute(ENR_status,0) setting1=self.get_LinkAttribute(ENR_setting,0) rxRate1=self.get_LinkAttribute(ENR_rxRate,0) frctnFctr1 =self.get_LinkAttribute(ENT_frctnFctr,0) print('\t'.join(["ind","flow","velocity","headloss","avgQuality",\ "status","setting","rxRate","frctnFctr"])) for ind, val in enumerate(flow1): print ind, val, velocity1[ind],headloss1[ind],avgQuality1[ind],status1[ind],setting1[ind],rxRate1[ind],frctnFctr1[ind] print("\n-->Object Counts") print("Nodes") print(self.nodeCount) print("Nodes") print(self.nodeCount) print("Tanks") print(self.tankCount) print("Links") print(self.linkCount) print("Pumps") print(self.pumpCount) print("Values") print(self.valveCount) print("\n-->get_NodeResult") print(self.get_NodeResult(96,24)) print("\n-->get_LinkResult") print(self.get_LinkResult(0,0)) print("\n-->get_Units") self.get_Units() print("...flow unit code") print(self.flowUnits) print("...pressure unit code") print(self.pressUnits) print("\n-->Closing Binary File") self.CloseOutputFile() print("...Closed Binary File") \ No newline at end of file diff --git a/tools/outputapi/OutDiff.py b/tools/outputapi/OutDiff.py deleted file mode 100644 index c7046dc..0000000 --- a/tools/outputapi/OutDiff.py +++ /dev/null @@ -1 +0,0 @@ -''' Compares Two EPANET binary output files. Author: Bryant E. McDonnell Date: 12/16/2015 Compares the absolute value of two values against a given threshold value. ******************* Command Line Arguments: python <*.out 1> <*.out 2> Returns True / False (Pass / Fail, respectively) ******************* ''' import sys import os from math import log from ENOutputWrapper import * def BinCompare(args): # Some Error Checking for Command Line Arguments.... if len(args) < 3: raise Exception("Not Enough Input Arguments: python <*.out 1> <*.out 2>") if args[1].endswith('.out') == False or args[2].endswith('.out') == False: raise Exception("python <*.out 1> <*.out 2>") print(sys.argv[1],sys.argv[2]) dllLoc = os.getcwd() + '/' + 'outputAPI.dll' BinFile1 = OutputObject(dllLoc) BinFile1.OpenOutputFile(args[1]) BinFile1.get_NetSize() BinFile1.get_Times() BinFile2 = OutputObject(dllLoc) BinFile2.OpenOutputFile(args[2]) ####ENR_NodeAttribute; ##ENR_demand = 0 ##ENR_head = 1 ##ENR_pressure = 2 ##ENR_quality = 3 NumberOfNodeAttr = 4 ####ENR_LinkAttribute; ##ENR_flow = 0 ##ENR_velocity = 1 ##ENR_headloss = 2 ##ENR_avgQuality = 3 ##ENR_status = 4 ##ENR_setting = 5 ##ENR_rxRate = 6 ##ENT_frctnFctr = 7 NumberOfLinkAttr = 8 NumberOfPeriods = BinFile1.numPeriods # Set Tolerances for each attribute # demand, head, pressure, quality NodeAttributeTolerances = [1e-6, 1e-6, 1e-6, 1e-6] # flow, velocity, headloss, avgQuality, status, setting, rxRate, frctnFctr LinkAttributeTolerances = [1e-6, 1e-6, 1e-6, 1e-6, 1e-6, 1e-6, 1e-6, 1e-6] #Compare Node Attributes for nodeAttrInd in range(NumberOfNodeAttr): for TSind in range(NumberOfPeriods): #Get 1 attribute for all nodes at time t NodeAttributeOut1 = BinFile1.get_NodeAttribute(nodeAttrInd, TSind) #Get 1 attribute for all nodes at time t NodeAttributeOut2 = BinFile2.get_NodeAttribute(nodeAttrInd, TSind) for Nodeind, NodeAttrVal in enumerate(NodeAttributeOut1): if abs(NodeAttrVal - NodeAttributeOut2[Nodeind]) > 0: diff = abs(NodeAttrVal - NodeAttributeOut2[Nodeind] ) if diff > NodeAttributeTolerances[nodeAttrInd]: return False #Compare Link Attributes for linkAttrInd in range(NumberOfLinkAttr): for TSind in range(NumberOfPeriods): #Get 1 attribute for all links at time t LinkAttributeOut1 = BinFile1.get_NodeAttribute(linkAttrInd, TSind) #Get 1 attribute for all links at time t LinkAttributeOut2 = BinFile2.get_NodeAttribute(linkAttrInd, TSind) for linkind, LinkAttrVal in enumerate(LinkAttributeOut1): if abs(LinkAttrVal - LinkAttributeOut2[linkind]) > 0: diff = abs(LinkAttrVal - LinkAttributeOut2[linkind] ) if diff > LinkAttributeTolerances[linkAttrInd]: return False return True if __name__ == '__main__': print(BinCompare(sys.argv)) \ No newline at end of file diff --git a/tools/outputapi/_ENOutputToolkit.py b/tools/outputapi/_ENOutputToolkit.py deleted file mode 100644 index 7687828..0000000 --- a/tools/outputapi/_ENOutputToolkit.py +++ /dev/null @@ -1 +0,0 @@ -##ENR_ElementType; ENR_node = 1 ENR_link = 2 ##ENR_ApiFunction; ENR_getSeries = 1 ENR_getAttribute = 2 ENR_getResult = 3 ##ENR_ElementCount; ENR_nodeCount = 1 ENR_tankCount = 2 ENR_linkCount = 3 ENR_pumpCount = 4 ENR_valveCount = 5 ##ENR_Unit; ENR_flowUnits = 1 ENR_pressUnits = 2 ##ENR_Time; ENR_reportStart = 1 ENR_reportStep = 2 ENR_simDuration = 3 ENR_numPeriods = 4 ##ENR_NodeAttribute; ENR_demand = 0 ENR_head = 1 ENR_pressure = 2 ENR_quality = 3 ##ENR_LinkAttribute; ENR_flow = 0 ENR_velocity = 1 ENR_headloss = 2 ENR_avgQuality = 3 ENR_status = 4 ENR_setting = 5 ENR_rxRate = 6 ENT_frctnFctr = 7 \ No newline at end of file