From 9efa97a1b842eb69a5a5cbb274e240d18a7c1dc2 Mon Sep 17 00:00:00 2001 From: Caleb Date: Wed, 20 Nov 2024 15:34:22 -0500 Subject: [PATCH] WIP; New cython bindings for expanded API for unit and reg tests; docs #204, #162, #163, #184, #180 --- python/epaswmm/CMakeLists.txt | 8 +- python/epaswmm/solver/CMakeLists.txt | 18 + python/epaswmm/solver/__init__.py | 20 + python/epaswmm/solver/solver.pxd | 281 +++++++++++ python/epaswmm/solver/solver.pyx | 717 +++++++++++++++++++++++++++ 5 files changed, 1038 insertions(+), 6 deletions(-) create mode 100644 python/epaswmm/solver/CMakeLists.txt create mode 100644 python/epaswmm/solver/__init__.py create mode 100644 python/epaswmm/solver/solver.pxd create mode 100644 python/epaswmm/solver/solver.pyx diff --git a/python/epaswmm/CMakeLists.txt b/python/epaswmm/CMakeLists.txt index c6e2f107..44879cd4 100644 --- a/python/epaswmm/CMakeLists.txt +++ b/python/epaswmm/CMakeLists.txt @@ -2,19 +2,15 @@ # Created by: Caleb Buahin (EPA/ORD/CESER/WID) # Created on: 2024-11-19 -# Add Cython targets for array and epaswmm -add_cython_target(array array.pyx CXX PY3) +# Add Cython targets for epaswmm add_cython_target(epaswmm epaswmm.pyx CXX PY3) # Create Python extension modules -add_library(array MODULE ${array}) add_library(epaswmm MODULE ${epaswmm}) -python_extension_module(array) python_extension_module(epaswmm) -# Install the array and epaswmm modules -install(TARGETS array LIBRARY DESTINATION epaswmm) +# Install the epaswmm module install(TARGETS epaswmm LIBRARY DESTINATION epaswmm) # Include the current source directory diff --git a/python/epaswmm/solver/CMakeLists.txt b/python/epaswmm/solver/CMakeLists.txt new file mode 100644 index 00000000..85d2e1dd --- /dev/null +++ b/python/epaswmm/solver/CMakeLists.txt @@ -0,0 +1,18 @@ +# CMake configuration for epaswmm library +# +# Created by: Caleb Buahin (EPA/ORD/CESER/WID) +# Created on: 2024-11-19 +# + +add_cython_target(solver solver.pyx CXX PY3) +add_library(solver MODULE ${solver}) +target_link_libraries(solver swmm5) + +python_extension_module(solver) +install(TARGETS solver LIBRARY DESTINATION epaswmm/solver) + +target_include_directories( + solver PUBLIC + ${CMAKE_CURRENT_SOURCE_DIR} + $ +) diff --git a/python/epaswmm/solver/__init__.py b/python/epaswmm/solver/__init__.py new file mode 100644 index 00000000..e6be28f2 --- /dev/null +++ b/python/epaswmm/solver/__init__.py @@ -0,0 +1,20 @@ +# Description: __init__.py file for the epaswmm.solver package. +# Created by: Caleb Buahin (EPA/ORD/CESER/WID) +# Created on: 2024-11-19 + +from .solver import ( + SWMMObjects, + SWMMNodeTypes, + SWMMRainGageProperties, + SWMMSubcatchmentProperties, + SWMMNodeProperties, + SWMMLinkProperties, + SWMMSystemProperties, + SWMMFlowUnits, + SWMMAPIErrors, + run_solver, + decode_swmm_datetime, + version, + SolverState, + Solver, +) diff --git a/python/epaswmm/solver/solver.pxd b/python/epaswmm/solver/solver.pxd new file mode 100644 index 00000000..6ce6dd60 --- /dev/null +++ b/python/epaswmm/solver/solver.pxd @@ -0,0 +1,281 @@ +# Description: Cython module for epaswmm solver +# Created by: Caleb Buahin (EPA/ORD/CESER/WID) +# Created on: 2024-11-19 + +# cython: language_level=3 +cdef extern from "swmm5.h": + # SWMM object type enumeration + ctypedef enum swmm_Object: + swmm_GAGE # Rain gage + swmm_SUBCATCH # Subcatchment + swmm_NODE # Junction + swmm_LINK # Conduit + swmm_AQUIFER # Aquifers + swmm_SNOWPACK # Snowpack + swmm_UNIT_HYDROGRAPH # Unit hydrographs + swmm_LID # Low impact development + swmm_STREET # Streets + swmm_INLET # Inlets + swmm_TRANSECT # Transects + smmm_XSECTION_SHAPE # Cross-section shape + swmm_CONTROL_RULE # Control rules + swmm_POLLUTANT # Pollutants + swmm_LANDUSE # Land uses + swmm_CURVE # Curve + swmm_TIMESERIES # Time series + swmm_TIME_PATTERN # Time pattern + swmm_SYSTEM # General + + # SWMM node type enumeration + ctypedef enum swmm_NodeType: + swmm_JUNCTION # Junction node + swmm_OUTFALL # Outfall node + swmm_STORAGE # Storage node + swmm_DIVIDER # Divider node + + # SWMM link type enumeration + ctypedef enum swmm_LinkType: + swmm_CONDUIT # Conduit link + swmm_PUMP # Pump link + swmm_ORIFICE # Orifice link + swmm_WEIR # Weir link + swmm_OUTLET # Outlet link + + # SWMM Rain Gage properties + ctypedef enum swmm_GageProperty: + swmm_GAGE_TOTAL_PRECIPITATION # Total precipitation + swmm_GAGE_RAINFALL # Snow depth + swmm_GAGE_SNOWFALL # Snowfall + + # SWMM Subcatchment properties + ctypedef enum swmm_SubcatchProperty: + swmm_SUBCATCH_AREA # Area + swmm_SUBCATCH_RAINGAGE # Rain gage + swmm_SUBCATCH_RAINFALL # Rainfall + swmm_SUBCATCH_EVAP # Evaporation + swmm_SUBCATCH_INFIL # Infiltration + swmm_SUBCATCH_RUNOFF # Runoff + swmm_SUBCATCH_RPTFLAG # Reporting flag + swmm_SUBCATCH_POLLUTANT_BUILDUP # Pollutant buildup + swmm_SUBCATCH_POLLUTANT_PONDED_CONCENTRATION # Pollutant ponded concentration + swmm_SUBCATCH_POLLUTANT_RUNOFF_CONCENTRATION # Pollutant runoff concentration + swmm_SUBCATCH_POLLUTANT_TOTAL_LOAD # Pollutant total load + + # SWMM Node properties + ctypedef enum swmm_NodeProperty: + swmm_NODE_TYPE # Node type + swmm_NODE_ELEV # Elevation + swmm_NODE_MAXDEPTH # Max. depth + swmm_NODE_DEPTH # Depth + swmm_NODE_HEAD # Hydraulic head + swmm_NODE_VOLUME # Volume + swmm_NODE_LATFLOW # Lateral inflow + swmm_NODE_INFLOW # Total inflow + swmm_NODE_OVERFLOW # Flooding + swmm_NODE_RPTFLAG # Reporting flag + swmm_NODE_POLLUTANT_CONCENTRATION # Pollutant concentration + swmm_NODE_POLLUTANT_INFLOW_CONCENTRATION # Pollutant inflow concentration + + # SWMM Link properties + ctypedef enum swmm_LinkProperty: + swmm_LINK_TYPE # Link type + swmm_LINK_NODE1 # Start node + swmm_LINK_NODE2 # End node + swmm_LINK_LENGTH # Length + swmm_LINK_SLOPE # Slope + swmm_LINK_FULLDEPTH # Full depth + swmm_LINK_FULLFLOW # Full flow + swmm_LINK_SETTING # Setting + swmm_LINK_TIMEOPEN # Time open + swmm_LINK_TIMECLOSED # Time closed + swmm_LINK_FLOW # Flow + swmm_LINK_DEPTH # Depth + swmm_LINK_VELOCITY # Velocity + swmm_LINK_TOPWIDTH # Top width + swmm_LINK_RPTFLAG # Reporting flag + swmm_LINK_POLLUTANT_CONCENTRATION # Pollutant concentration + swmm_LINK_POLLUTANT_LOAD # Pollutant load + + # SWMM System properties + ctypedef enum swmm_SystemProperty: + swmm_STARTDATE # The start date of the simulation. + swmm_CURRENTDATE # The current date in the simulation. + swmm_ELAPSEDTIME # The elapsed time since the start of the simulation. + swmm_ROUTESTEP # The routing step size. + swmm_MAXROUTESTEP # The maximum routing step size. + swmm_REPORTSTEP # The reporting step size. + swmm_TOTALSTEPS # The total number of steps in the simulation. + swmm_NOREPORT # Flag indicating whether reporting is disabled. + swmm_FLOWUNITS # The flow units used in the simulation. + swmm_ENDDATE # The end date of the simulation. + swmm_REPORTSTART # The start date of the reporting period. + swmm_UNITSYSTEM # The unit system used in the simulation. + swmm_SURCHARGEMETHOD # The surcharge method used in the simulation. + swmm_ALLOWPONDING # Flag indicating whether ponding is allowed. + swmm_INERTIADAMPING # The inertia damping factor used in the simulation. + swmm_NORMALFLOWLTD # The normal flow limited flag. + swmm_SKIPSTEADYSTATE # Flag indicating whether steady state periods are skipped. + swmm_IGNORERAINFALL # Flag indicating whether rainfall is ignored. + swmm_IGNORERDII # Flag indicating whether RDII is ignored. + swmm_IGNORESNOWMELT # Flag indicating whether snowmelt is ignored. + swmm_IGNOREGWATER # Flag indicating whether groundwater is ignored. + swmm_IGNOREROUTING # Flag indicating whether routing is ignored. + swmm_IGNOREQUALITY # Flag indicating whether water quality is ignored. + swmm_RULESTEP # The rule step size. + swmm_SWEEPSTART # The start date of the sweep start. + swmm_SWEEPEND # The end date of the sweep end. + swmm_MAXTRIALS # The maximum number of trials. + swmm_NUMTHREADS # The number of threads used in the simulation. + swmm_MINROUTESTEP # The minimum routing step size. + swmm_LENGTHENINGSTEP # The lengthening step size. + swmm_STARTDRYDAYS # The number of start dry days. + swmm_COURANTFACTOR # The Courant factor. + swmm_MINSURFAREA # The minimum surface area. + swmm_MINSLOPE # The minimum slope. + swmm_RUNOFFERROR # The runoff error. + swmm_FLOWERROR # The flow error. + swmm_QUALERROR # The quality error. + swmm_HEADTOL # The head tolerance. + swmm_SYSFLOWTOL # The system flow tolerance. + swmm_LATFLOWTOL # The lateral flow tolerance. + + # SWMM flow units enumeration + ctypedef enum swmm_FlowUnitsProperty: + swmm_CFS # Cubic feet per second + swmm_GPM # Gallons per minute + swmm_MGD # Million gallons per day + swmm_CMS # Cubic meters per second + swmm_LPS # Liters per second + swmm_MLD # Million liters per day + + # SWMM API function return error codes + ctypedef enum swmm_API_Errors: + ERR_API_NOT_OPEN # API not open + ERR_API_NOT_STARTED # API not started + ERR_API_NOT_ENDED # API not ended + ERR_API_OBJECT_TYPE # Invalid object type + ERR_API_OBJECT_INDEX # Invalid object index + ERR_API_OBJECT_NAME # Invalid object name + ERR_API_PROPERTY_TYPE # Invalid property type + ERR_API_PROPERTY_VALUE # Invalid property value + ERR_API_TIME_PERIOD # Invalid time period + ERR_API_HOTSTART_FILE_OPEN # Error opening hotstart file + ERR_API_HOTSTART_FILE_FORMAT # Invalid hotstart file format + + + # SWMM API function prototypes + # param: inp_file: input file name + # param: rpt_file: report file name + # parm: out_file: output file name + # Returns: error code (0 if successful) + cdef int swmm_run(char* inp_file, char* rpt_file, char* out_file) + + # Open a SWMM input file + # param: inp_file: input file name + # param: rpt_file: report file name + # parm: out_file: output file name + # Returns: error code (0 if successful) + cdef int swmm_open(char* inp_file, char* rpt_file, char* out_file) + + # Starts a SWMM simulation + # param: save_flag = TRUE if simulation results saved to binary file + # Returns: error code + cdef int swmm_start(int save_flag) + + # Performs a single time step of a SWMM simulation + # param: elapsed_time: elapsed time in seconds + # Returns: error code + cdef int swmm_step(double* elapsed_time) + + # Performs a single stride of a SWMM simulation + # param: strideStep: number of steps to perform + # param: elapsedTime: elapsed time in seconds + # Returns: error code + cdef int swmm_stride(int strideStep, double *elapsedTime) + + # Uses provide hot start file to initialize the simulation + # param: hotStartFile: hot start file name + # Returns: error code + cdef int swmm_useHotStart(const char* hotStartFile) + + # Saves the current simulation state to a hot start file + # param: hotStartFile: hot start file name + # Returns: error code + cdef int swmm_saveHotStart(const char* hotStartFile) + + # Ends a SWMM simulation + # Returns: error code + cdef int swmm_end() + + # Writes a report to the report file + # Returns: error code + cdef int swmm_report() + + # Closes a SWMM simulation + # Returns: error code + cdef int swmm_close() + + # Retrieves the mass balance errors + # param: runoffErr: runoff error + # param: flowErr: flow error + # param: qualErr: quality error + cdef int swmm_getMassBalErr(float *runoffErr, float *flowErr, float *qualErr) + + # Gets the version of the SWMM engine + # Returns: version number + cdef int swmm_getVersion() + + # Retrieves the error message from the SWMM engine + # param: errMsg: error message + # param: msgLen: length of the error message + # Returns: error code + cdef int swmm_getError(char *errMsg, int msgLen) + + # Retrieves the error message from the SWMM engine + # param: error_code: error code + # param: outErrMsg: error message + # Returns: error code + cdef int swmm_getErrorFromCode(int error_code, char *outErrMsg[1024]) + + # Retrieves the number of warnings from the SWMM engine + cdef int swmm_getWarnings() + + # Retrieves the number of objects of a given type + # param: objType: object type + cdef int swmm_getCount(int objType) + + # Retrieves the name of an object of a given type and index + # param: objType: object type + # param: index: object index + # param: name: object name + # param: size: size of the object name + cdef int swmm_getName(int objType, int index, char *name, int size) + + # Retrieves the index of an object of a given type and name + # param: objType: object type + # param: name: object name + cdef int swmm_getIndex(int objType, const char *name) + + # Retrieves the value of a property for an object of a given type and index + # param: property: property type + # param: index: object index + cdef double swmm_getValue(int property, int index) + + # Sets the value of a property for an object of a given type and index + # param: property: property type + # param: index: object index + # param: value: property value + cdef int swmm_setValue(int property, int index, double value) + + # Retrieves the value of a property for an object of a given type and index + # param: property: property type + # param: index: object index + # param: period: time period + cdef double swmm_getSavedValue(int property, int index, int period) + + # Writes a line to the SWMM report file + # param: line: line to write + cdef void swmm_writeLine(const char *line) + + # Decodes a SWMM datetime into a datetime object + cdef void swmm_decodeDate(double date, int *year, int *month, int *day, int *hour, int *minute, int *second, int *dayOfWeek) \ No newline at end of file diff --git a/python/epaswmm/solver/solver.pyx b/python/epaswmm/solver/solver.pyx new file mode 100644 index 00000000..a4d0d870 --- /dev/null +++ b/python/epaswmm/solver/solver.pyx @@ -0,0 +1,717 @@ +# Description: Cython module for epaswmm solver +# Created by: Caleb Buahin (EPA/ORD/CESER/WID) +# Created on: 2024-11-19 + +# cython: language_level=3 + +# python and cython imports +from enum import Enum +from typing import List, Tuple, Union, Optional, Dict, Set +from cpython.datetime cimport datetime, timedelta +from libc.stdlib cimport free, malloc + +# external imports + +# local python and cython imports +# from epaswmm.array import Array1d +cimport epaswmm.solver.solver as solver +cimport epaswmm.epaswmm as cepaswmm +# cython: language_level=3 + +from epaswmm.solver.solver cimport ( + swmm_Object, + swmm_NodeType, + swmm_LinkType, + swmm_GageProperty, + swmm_SubcatchProperty, + swmm_NodeProperty, + swmm_LinkProperty, + swmm_SystemProperty, + swmm_FlowUnitsProperty, + swmm_API_Errors, + swmm_run, + swmm_open, + swmm_start, + swmm_step, + swmm_stride, + swmm_useHotStart, + swmm_saveHotStart, + swmm_end, + swmm_report, + swmm_close, + swmm_getMassBalErr, + swmm_getVersion, + swmm_getError, + swmm_getErrorFromCode, + swmm_getWarnings, + swmm_getCount, + swmm_getName, + swmm_getIndex, + swmm_getValue, + swmm_setValue, + swmm_getSavedValue, + swmm_writeLine, + swmm_decodeDate +) + +class SWMMObjects(Enum): + """ + Enumeration of SWMM objects. + + :ivar SUBCATCH: Subcatchment object + :type SUBCATCH: int + :ivar NODE: Node object + :type NODE: int + :ivar LINK: Link object + :type LINK: int + :ivar AQUIFER: Aquifer object + :type AQUIFER: int + :ivar SNOWPACK: Snowpack object + :type SNOWPACK: int + :ivar UNIT_HYDROGRAPH: Unit hydrograph object + :type UNIT_HYDROGRAPH: int + :ivar LID: LID object + :type LID: int + :ivar STREET: Street object + :type STREET: int + :ivar INLET: Inlet object + :type INLET: int + :ivar TRANSECT: Transect object + :type TRANSECT: int + :ivar XSECTION_SHAPE: Cross-section shape object + :type XSECTION_SHAPE: int + :ivar CONTROL_RULE: Control rule object + :type CONTROL_RULE: int + :ivar POLLUTANT: Pollutant object + :type POLLUTANT: int + :ivar LANDUSE: Land use object + :type LANDUSE: int + :ivar CURVE: Curve object + :type CURVE: int + :ivar TIMESERIES: Time series object + :type TIMESERIES: int + :ivar TIME_PATTERN: Time pattern object + :type TIME_PATTERN: int + :ivar SYSTEM: System object + :type SYSTEM: int + """ + RAIN_GAGE = swmm_Object.swmm_GAGE + SUBCATCH = swmm_Object.swmm_SUBCATCH + NODE = swmm_Object.swmm_NODE + LINK = swmm_Object.swmm_LINK + AQUIFER = swmm_Object.swmm_AQUIFER + SNOWPACK = swmm_Object.swmm_SNOWPACK + UNIT_HYDROGRAPH = swmm_Object.swmm_UNIT_HYDROGRAPH + LID = swmm_Object.swmm_LID + STREET = swmm_Object.swmm_STREET + INLET = swmm_Object.swmm_INLET + TRANSECT = swmm_Object.swmm_TRANSECT + XSECTION_SHAPE = swmm_Object.smmm_XSECTION_SHAPE + CONTROL_RULE = swmm_Object.swmm_CONTROL_RULE + POLLUTANT = swmm_Object.swmm_POLLUTANT + LANDUSE = swmm_Object.swmm_LANDUSE + CURVE = swmm_Object.swmm_CURVE + TIMESERIES = swmm_Object.swmm_TIMESERIES + TIME_PATTERN = swmm_Object.swmm_TIME_PATTERN + SYSTEM = swmm_Object.swmm_SYSTEM + +class SWMMNodeTypes(Enum): + """ + Enumeration of SWMM node types. + + :ivar JUNCTION: Junction node + :type JUNCTION: int + :ivar OUTFALL: Outfall node + :type OUTFALL: int + :ivar STORAGE: Storage node + :type STORAGE: int + :ivar DIVIDER: Divider node + :type DIVIDER: int + """ + JUNCTION = swmm_NodeType.swmm_JUNCTION + OUTFALL = swmm_NodeType.swmm_OUTFALL + STORAGE = swmm_NodeType.swmm_STORAGE + DIVIDER = swmm_NodeType.swmm_DIVIDER + +class SWMMRainGageProperties(Enum): + """ + Enumeration of SWMM raingage properties. + + :ivar GAGE_TOTAL_PRECIPITATION: Total precipitation + :type GAGE_TOTAL_PRECIPITATION: int + :ivar GAGE_SNOW_DEPTH: Snow depth + :type GAGE_SNOW_DEPTH: int + :ivar GAGE_SNOWFALL: Snowfall + :type GAGE_SNOWFALL: int + """ + GAGE_TOTAL_PRECIPITATION = swmm_GageProperty.swmm_GAGE_TOTAL_PRECIPITATION # Total precipitation + GAGE_RAINFALL = swmm_GageProperty.swmm_GAGE_RAINFALL # Rainfall + GAGE_SNOWFALL = swmm_GageProperty.swmm_GAGE_SNOWFALL # Snowfall + +class SWMMSubcatchmentProperties(Enum): + """ + Enumeration of SWMM subcatchment properties. + + :ivar AREA: Area + :type AREA: int + :ivar RAINGAGE: Raingage + :type RAINGAGE: int + :ivar RAINFALL: Rainfall + :type RAINFALL: int + :ivar EVAPORATION: Evaporation + :type EVAPORATION: int + :ivar INFILTRATION: Infiltration + :type INFILTRATION: int + :ivar RUNOFF: Runoff + :type RUNOFF: int + :ivar REPORT_FLAG: Report flag + :type REPORT_FLAG: int + :ivar POLLUTANT_BUILDUP: Pollutant buildup + :type POLLUTANT_BUILDUP: int + :ivar POLLUTANT_PONDED_CONCENTRATION: Pollutant ponded concentration + :type POLLUTANT_PONDED_CONCENTRATION: int + :ivar POLLUTANT_TOTAL_LOAD: Pollutant total load + :type POLLUTANT_TOTAL_LOAD: int + """ + AREA = swmm_SubcatchProperty.swmm_SUBCATCH_AREA + RAINGAGE = swmm_SubcatchProperty.swmm_SUBCATCH_RAINGAGE + RAINFALL = swmm_SubcatchProperty.swmm_SUBCATCH_RAINFALL + EVAPORATION = swmm_SubcatchProperty.swmm_SUBCATCH_EVAP + INFILTRATION = swmm_SubcatchProperty.swmm_SUBCATCH_INFIL + RUNOFF = swmm_SubcatchProperty.swmm_SUBCATCH_RUNOFF + REPORT_FLAG = swmm_SubcatchProperty.swmm_SUBCATCH_RPTFLAG + POLLUTANT_BUILDUP = swmm_SubcatchProperty.swmm_SUBCATCH_POLLUTANT_BUILDUP # Pollutant buildup + POLLUTANT_PONDED_CONCENTRATION = swmm_SubcatchProperty.swmm_SUBCATCH_POLLUTANT_PONDED_CONCENTRATION # Pollutant ponded concentration + POLLUTANT_RUNOFF_CONCENTRATION = swmm_SubcatchProperty.swmm_SUBCATCH_POLLUTANT_TOTAL_LOAD # Pollutant total load + +class SWMMNodeProperties(Enum): + """ + Enumeration of SWMM node properties. + + :ivar TYPE: Node type + :type TYPE: int + :ivar ELEVATION: Elevation + :type ELEVATION: int + :ivar MAX_DEPTH: Maximum depth + :type MAX_DEPTH: int + :ivar DEPTH: Depth + :type DEPTH: int + :ivar HYDRAULIC_HEAD: Hydraulic head + :type HYDRAULIC_HEAD: int + :ivar VOLUME: Volume + :type VOLUME: int + :ivar LATERAL_INFLOW: Lateral inflow + :type LATERAL_INFLOW: int + :ivar TOTAL_INFLOW: Total inflow + :type TOTAL_INFLOW: int + :ivar FLOODING: Flooding + :type FLOODING: int + :ivar REPORT_FLAG: Report flag + :type REPORT_FLAG: int + """ + TYPE = swmm_NodeProperty.swmm_NODE_TYPE + ELEVATION = swmm_NodeProperty.swmm_NODE_ELEV + MAX_DEPTH = swmm_NodeProperty.swmm_NODE_MAXDEPTH + DEPTH = swmm_NodeProperty.swmm_NODE_DEPTH + HYDRAULIC_HEAD = swmm_NodeProperty.swmm_NODE_HEAD + VOLUME = swmm_NodeProperty.swmm_NODE_VOLUME + LATERAL_INFLOW = swmm_NodeProperty.swmm_NODE_LATFLOW + TOTAL_INFLOW = swmm_NodeProperty.swmm_NODE_INFLOW + FLOODING = swmm_NodeProperty.swmm_NODE_OVERFLOW + REPORT_FLAG = swmm_NodeProperty.swmm_NODE_RPTFLAG + POLLUTANT_CONCENTRATION = swmm_NodeProperty.swmm_NODE_POLLUTANT_CONCENTRATION # Pollutant concentration + POLLUTANT_INFLOW_CONCENTRATION = swmm_NodeProperty.swmm_NODE_POLLUTANT_INFLOW_CONCENTRATION # Pollutant inflow concentration + +class SWMMLinkProperties(Enum): + """ + Enumeration of SWMM link properties. + + :ivar TYPE: Link type + :type TYPE: int + :ivar OFFSET1: Offset 1 + :type OFFSET1: int + :ivar OFFSET2: Offset 2 + :type OFFSET2: int + :ivar DIAMETER: Diameter + :type DIAMETER: int + :ivar LENGTH: Length + :type LENGTH: int + :ivar ROUGHNESS: Roughness + :type ROUGHNESS: int + :ivar INLET_HEIGHT: Inlet height + :type INLET_HEIGHT: int + :ivar OUTLET_HEIGHT: Outlet height + :type OUTLET_HEIGHT: int + :ivar INIT_FLOW: Initial flow + :type INIT_FLOW: int + :ivar FLOW_LIMIT: Flow limit + :type FLOW_LIMIT: int + :ivar REPORT_FLAG: Report flag + :type REPORT_FLAG: int + """ + TYPE = swmm_LinkProperty.swmm_LINK_TYPE + START_NODE = swmm_LinkProperty.swmm_LINK_NODE1 + END_NODE = swmm_LinkProperty.swmm_LINK_NODE2 + LENGTH = swmm_LinkProperty.swmm_LINK_LENGTH + SLOPE = swmm_LinkProperty.swmm_LINK_SLOPE + FULL_DEPTH = swmm_LinkProperty.swmm_LINK_FULLDEPTH + FULL_FLOW = swmm_LinkProperty.swmm_LINK_FULLFLOW + SETTING = swmm_LinkProperty.swmm_LINK_SETTING + TIME_OPEN = swmm_LinkProperty.swmm_LINK_TIMEOPEN + TIME_CLOSED = swmm_LinkProperty.swmm_LINK_TIMECLOSED + FLOW = swmm_LinkProperty.swmm_LINK_FLOW + DEPTH = swmm_LinkProperty.swmm_LINK_DEPTH + VELOCITY = swmm_LinkProperty.swmm_LINK_VELOCITY + TOP_WIDTH = swmm_LinkProperty.swmm_LINK_TOPWIDTH + REPORT_FLAG = swmm_LinkProperty.swmm_LINK_RPTFLAG + POLLUTANT_CONCENTRATION = swmm_LinkProperty.swmm_LINK_POLLUTANT_CONCENTRATION # Pollutant concentration + POLLUTANT_LOAD = swmm_LinkProperty.swmm_LINK_POLLUTANT_LOAD # Pollutant load + +class SWMMSystemProperties(Enum): + """ + Enumeration of SWMM system properties. + """ + START_DATE = swmm_SystemProperty.swmm_STARTDATE + CURRENT_DATE = swmm_SystemProperty.swmm_CURRENTDATE + ELAPSED_TIME = swmm_SystemProperty.swmm_ELAPSEDTIME + ROUTING_STEP = swmm_SystemProperty.swmm_ROUTESTEP + MAX_ROUTING_STEP = swmm_SystemProperty.swmm_MAXROUTESTEP + REPORT_STEP = swmm_SystemProperty.swmm_REPORTSTEP + TOTAL_STEPS = swmm_SystemProperty.swmm_TOTALSTEPS + NO_REPORT_FLAG = swmm_SystemProperty.swmm_NOREPORT + FLOW_UNITS = swmm_SystemProperty.swmm_FLOWUNITS + END_DATE = swmm_SystemProperty.swmm_ENDDATE + REPORT_START_DATE = swmm_SystemProperty.swmm_REPORTSTART + UNIT_SYSTEM = swmm_SystemProperty.swmm_UNITSYSTEM + SURCHARGE_METHOD = swmm_SystemProperty.swmm_SURCHARGEMETHOD + ALLOW_PONDING = swmm_SystemProperty.swmm_ALLOWPONDING + INTERTIAL_DAMPING = swmm_SystemProperty.swmm_INERTIADAMPING + NORMAL_FLOW_LIMITED = swmm_SystemProperty.swmm_NORMALFLOWLTD + SKIP_STEADY_STATE = swmm_SystemProperty.swmm_SKIPSTEADYSTATE + IGNORE_RAINFALL = swmm_SystemProperty.swmm_IGNORERAINFALL + IGNORE_RDII = swmm_SystemProperty.swmm_IGNORERDII + IGNORE_SNOWMELT = swmm_SystemProperty.swmm_IGNORESNOWMELT + IGNORE_GROUNDWATER = swmm_SystemProperty.swmm_IGNOREGWATER + IGNORE_ROUTING = swmm_SystemProperty.swmm_IGNOREROUTING + IGNORE_QUALITY = swmm_SystemProperty.swmm_IGNOREQUALITY + RULE_STEP = swmm_SystemProperty.swmm_RULESTEP + SWEEP_START = swmm_SystemProperty.swmm_SWEEPSTART + SWEEP_END = swmm_SystemProperty.swmm_SWEEPEND + MAX_TRIALS = swmm_SystemProperty.swmm_MAXTRIALS + NUM_THREADS = swmm_SystemProperty.swmm_NUMTHREADS + MIN_ROUTE_STEP = swmm_SystemProperty.swmm_MINROUTESTEP + LENGTHENING_STEP = swmm_SystemProperty.swmm_LENGTHENINGSTEP + START_DRY_DAYS = swmm_SystemProperty.swmm_STARTDRYDAYS + COURANT_FACTOR = swmm_SystemProperty.swmm_COURANTFACTOR + MIN_SURF_AREA = swmm_SystemProperty.swmm_MINSURFAREA + MIN_SLOPE = swmm_SystemProperty.swmm_MINSLOPE + RUNOFF_ERROR = swmm_SystemProperty.swmm_RUNOFFERROR + FLOW_ERROR = swmm_SystemProperty.swmm_FLOWERROR + QUAL_ERROR = swmm_SystemProperty.swmm_QUALERROR + HEAD_TOL = swmm_SystemProperty.swmm_HEADTOL + SYS_FLOW_TOL = swmm_SystemProperty.swmm_SYSFLOWTOL + LAT_FLOW_TOL = swmm_SystemProperty.swmm_LATFLOWTOL + +class SWMMFlowUnits(Enum): + """ + Enumeration of SWMM flow units. + + :ivar CFS: Cubic feet per second + :type CFS: int + :ivar GPM: Gallons per minute + :type GPM: int + :ivar MGD: Million gallons per day + :type MGD: int + :ivar CMS: Cubic meters per second + :type CMS: int + :ivar LPS: Liters per second + :type LPS: int + :ivar MLD: Million liters per day + :type MLD: int + """ + CFS = swmm_FlowUnitsProperty.swmm_CFS + GPM = swmm_FlowUnitsProperty.swmm_GPM + MGD = swmm_FlowUnitsProperty.swmm_MGD + CMS = swmm_FlowUnitsProperty.swmm_CMS + LPS = swmm_FlowUnitsProperty.swmm_LPS + MLD = swmm_FlowUnitsProperty.swmm_MLD + +class SWMMAPIErrors(Enum): + """ + Enumeration of SWMM API errors. + + :ivar PROJECT_NOT_OPENED: Project not opened + :type PROJECT_NOT_OPENED: int + :ivar SIMULATION_NOT_STARTED: Simulation not started + :type SIMULATION_NOT_STARTED: int + :ivar SIMULATION_NOT_ENDED: Simulation not ended + :type SIMULATION_NOT_ENDED: int + """ + PROJECT_NOT_OPENED = swmm_API_Errors.ERR_API_NOT_OPEN # API not open + SIMULATION_NOT_STARTED = swmm_API_Errors.ERR_API_NOT_STARTED # API not started + SIMULATION_NOT_ENDED = swmm_API_Errors.ERR_API_NOT_ENDED # API not ended + OBJECT_TYPE = swmm_API_Errors.ERR_API_OBJECT_TYPE # Invalid object type + OBJECT_INDEX = swmm_API_Errors.ERR_API_OBJECT_INDEX # Invalid object index + OBJECT_NAME = swmm_API_Errors.ERR_API_OBJECT_NAME # Invalid object name + PROPERTY_TYPE = swmm_API_Errors.ERR_API_PROPERTY_TYPE # Invalid property type + PROPERTY_VALUE = swmm_API_Errors.ERR_API_PROPERTY_VALUE # Invalid property value + TIME_PERIOD = swmm_API_Errors.ERR_API_TIME_PERIOD # Invalid time period + HOTSTART_FILE_OPEN = swmm_API_Errors.ERR_API_HOTSTART_FILE_OPEN # Error opening hotstart file + HOTSTART_FILE_FORMAT = swmm_API_Errors.ERR_API_HOTSTART_FILE_FORMAT # Invalid hotstart file format + +cpdef int run_solver(str inp_file, str rpt_file, str out_file): + """ + Run a SWMM simulation. + + :param inp_file: Input file name + :param rpt_file: Report file name + :param out_file: Output file name + :return: Error code (0 if successful) + """ + cdef int error_code = 0 + cdef bytes c_inp_file_bytes = inp_file.encode('utf-8') + + if rpt_file is not None: + rpt_file = inp_file.replace('.inp', '.rpt') + + if out_file is not None: + out_file = inp_file.replace('.inp', '.out') + + cdef bytes c_rpt_file_bytes = rpt_file.encode('utf-8') + cdef bytes c_out_file_bytes = out_file.encode('utf-8') + + cdef const char* c_inp_file = c_inp_file_bytes + cdef const char* c_rpt_file = c_rpt_file_bytes + cdef const char* c_out_file = c_out_file_bytes + + error_code = swmm_open(c_inp_file, c_rpt_file, c_out_file) + + if error_code != 0: + raise Exception(f'Run failed with message: {get_error_message(error_code)}') + + return error_code + +cpdef datetime decode_swmm_datetime(double swmm_datetime): + """ + Decode a SWMM datetime into a datetime object. + + :param swmm_datetime: SWMM datetime float value + :type swmm_datetime: float + :return: datetime object + :rtype: datetime + """ + cdef int year, month, day, hour, minute, second, day_of_week + swmm_decodeDate(swmm_datetime, &year, &month, &day, &hour, &minute, &second, &day_of_week) + + return datetime(year, month, day, hour, minute, second) + +cpdef int version(): + """ + Get the SWMM version. + + :return: SWMM version + :rtype: str + """ + cdef int swmm_version = swmm_getVersion() + + return swmm_version + +cpdef str get_error_message(int error_code): + """ + Get the error message for a SWMM error code. + + :param error_code: Error code + :type error_code: int + :return: Error message + :rtype: str + """ + cdef char* c_error_message = malloc(1024*sizeof(char)) + + swmm_getErrorFromCode(error_code, &c_error_message) + + error_message = c_error_message.decode('utf-8') + + free(c_error_message) + + return error_message + +class SolverState(Enum): + """ + An enumeration to represent the state of the solver. + """ + CREATED = 0 + OPEN = 1 + STARTED = 2 + FINISHED = 3 + ENDED = 4 + REPORTED = 5 + CLOSED = 6 + +class CallbackType(Enum): + """ + An enumeration to represent the type of callback. + """ + BEFORE_INITIALIZE = 0 + BEFORE_OPEN = 1 + AFTER_OPEN = 2 + BEFORE_START = 3 + AFTER_START = 4 + BEFORE_STEP = 5 + AFTER_STEP = 6 + BEFORE_END = 7 + AFTER_END = 8 + BEFORE_REPORT = 9 + AFTER_REPORT = 10 + BEFORE_CLOSE = 11 + AFTER_CLOSE = 12 + +cdef class Solver: + """ + A class to represent a SWMM solver. + """ + cdef str _inp_file + cdef str _rpt_file + cdef str _out_file + cdef bint _save_results + cdef int _stride_step + cdef list _before_initialize_callbacks + cdef list _before_open_callbacks + cdef list _after_open_callbacks + cdef list _before_start_callbacks + cdef list _after_start_callbacks + cdef list _before_step_callbacks + cdef list _before_end_callbacks + cdef list _after_end_callbacks + cdef list _before_report_callbacks + cdef list _after_report_callbacks + cdef list _before_close_callbacks + cdef list _after_close_callbacks + + def __cinit__(self, str inp_file, str rpt_file, str out_file, bint save_results=True): + """ + Constructor to create a new SWMM solver. + + :param inp_file: Input file name + :param rpt_file: Report file name + :param out_file: Output file name + """ + self._save_results = save_results + self._inp_file = inp_file + + if rpt_file is not None: + self._rpt_file = rpt_file + else: + self._rpt_file = inp_file.replace('.inp', '.rpt') + + if out_file is not None: + self._out_file = out_file + else: + self._out_file = inp_file.replace('.inp', '.out') + + self._solver_state = SolverState.CREATED + + def __enter__(self): + """ + Enter method for context manager. + """ + if ( + (self._solver_state != SolverState.CREATED) or + (self._solver_state != SolverState.CLOSED) + ): + raise Exception('') + else: + self.initialize() + + return self + + def __exit__(self, exc_type, exc_value, traceback): + """ + Exit method for context manager. + """ + self.__close() + + def __close(self): + """ + Close the solver. + """ + pass + + def __dealloc__(self): + """ + Destructor to free the solver. + """ + pass + + def __iter__(self): + """ + Iterator method for the solver. + """ + return self + + def __next__(self): + """ + Next method for the solver. + """ + pass + + @property + def start_date(self) -> datetime: + """ + Get the start date of the simulation. + + :return: Start date + :rtype: datetime + """ + pass + + @property + def end_date(self) -> datetime: + """ + Get the end date of the simulation. + + :return: End date + :rtype: datetime + """ + pass + + @property + def current_date(self) -> datetime: + """ + Get the current date of the simulation. + + :return: Current date + :rtype: datetime + """ + pass + + @property + def stride_step(self) -> int: + """ + Get the stride step of the simulation. + + :return: Stride step + :rtype: int + """ + pass + + @stride_step.setter + def stride_step(self, value: int): + """ + Set the stride time step of the simulation. + + :param value: Stride step in seconds + :type value: int + """ + pass + + def __validate_error(self, error_code: int) -> None: + """ + Validate the error code and raise an exception if it is not 0. + + :param error_code: Error code to validate + :type error_code: int + """ + if error_code != 0: + raise Exception(f'Run failed with message: {self.__get_error()}') + + def status(self) -> SolverState: + """ + Get the status of the solver. + + :return: Solver state + :rtype: SolverState + """ + pass + + cpdef str __get_error(self): + """ + Get the error code from the solver. + + :return: Error code + :rtype: int + """ + cdef char* c_error_message = malloc(1024*sizeof(char)) + swmm_getError(c_error_message, 1024) + + error_message = c_error_message.decode('utf-8') + + free(c_error_message) + + return error_message + + def __initialize(self) -> int: + """ + Open a SWMM input file. + + :param inp_file: Input file name + :param rpt_file: Report file name + :param out_file: Output file name + :return: Error code (0 if successful) + """ + cdef error_code = 0 + + cdef bytes c_inp_file_bytes = self._inp_file.encode('utf-8') + cdef bytes c_rpt_file_bytes = self._rpt_file.encode('utf-8') + cdef bytes c_out_file_bytes = self._out_file.encode('utf-8') + + cdef const char* c_inp_file = c_inp_file_bytes + cdef const char* c_rpt_file = c_rpt_file_bytes + cdef const char* c_out_file = c_out_file_bytes + + error_code = swmm_open(c_inp_file, c_rpt_file, c_out_file) + self.__validate_error(error_code) + + def __finalize(self) -> int: + """ + Close a SWMM input file. + + :return: Error code (0 if successful) + """ + cdef error_code = 0 + + error_code = swmm_close() + self.__validate_error(error_code) + + def __start(self, save_hotstart: bool) -> int: + """ + Start a SWMM simulation. + + :param save_hotstart: Flag to save hotstart file + :return: Error code (0 if successful) + """ + pass + + def step(self) -> datetime: + """ + Step a SWMM simulation. + + :return: Error code (0 if successful) + """ + pass + + def step_stride(self, stride: int) -> int: + """ + Stride a SWMM simulation. + + :param stride: Number of steps to stride + :return: Error code (0 if successful) + """ + pass + + def use_hotstart(self, hotstart_file: str) -> int: + """ + Use a hotstart file. + + :param hotstart_file: Hotstart file name + :return: Error code (0 if successful) + """ + pass + + def save_hotstart(self, hotstart_file: str) -> int: + """ + Save a hotstart file. + + :param hotstart_file: Hotstart file name + :return: Error code (0 if successful) + """ + pass \ No newline at end of file