Skip to content

Commit 11f2a60

Browse files
committed
WIP finalizing core api, unit testing, installation config; #204, #162, #163, #184, #180
1 parent 6c08f7e commit 11f2a60

15 files changed

+384
-74
lines changed

README.md

+46-9
Original file line numberDiff line numberDiff line change
@@ -46,14 +46,14 @@ using CMake and the Microsoft Visual Studio C compiler on Windows:
4646
Readme file resides (which should have 'src' as a sub-directory
4747
underneath it).
4848

49-
2. Issue the following commands:
49+
2. Issue the following command to create the directory for storing the built binaries:
5050

5151
```bash
5252
mkdir build
5353
cd build
5454
```
5555

56-
3. Then enter the following CMake commands:
56+
3. Then enter the following CMake commands to build the binaries:
5757

5858
``` bash
5959
cmake -G <compiler> .. -A <platform>
@@ -77,22 +77,59 @@ cmake --build .
7777
The resulting shared object library (libswmm5.so or libswmm5.dylib) and
7878
command line executable (runswmm) will appear in the build directory.
7979

80-
The exprimental python bindings can be built and installed locally using the following command.
80+
### Python Bindings (Experimental)
81+
82+
Experimental python bindings for the SWMM API are being developed to support regression and benchmark testing as well as other applications. _**These bindings are still under development and testing and has yet to be cleared through US EPA ORD's official quality assurance review process**_. The exprimental python bindings can be built and installed locally using the following command.
8183

8284
```bash
8385
cd python
8486
python -m pip install -r requirements.txt
8587
python -m pip install .
8688
```
8789
Users may also build python wheels for installation or distribution. Once the python bindings
88-
have been validated and cleared through EPA'S clearance process, they will be available for installation
89-
via ropsitories such as pypi.
90+
have been validated and cleared through EPA's quality assuracnce clearance process, they will be available for installation via package indexing repositories such as pypi.
91+
92+
Example usage of python bindings can be found below. More extensive documentation will be provided once cleared.
93+
94+
```python
95+
96+
from epaswmm import solver
97+
from epaswmm.solver import Solver
98+
from epaswmm.output import Output
99+
100+
with Solver(inp_file="input_file.inp") as swmm_solver:
101+
102+
# Open swmm file and initialize objects
103+
swmm_solver.initialize()
104+
105+
# Set initialization parameters e.g., time step stride, start date, end date etc.
106+
swmm_solver.time_stride = 600
107+
108+
for elapsed_time in swmm_solver:
109+
110+
# Get and set attributes per timestep
111+
print(swmm_solver.current_datetime)
112+
113+
swmm_solver.set_value(
114+
object_type=solver.SWMMObjects.RAIN_GAGE.value,
115+
property_type=solver.SWMMRainGageProperties.GAGE_RAINFALL.value,
116+
index=0,
117+
value=3.6
118+
)
119+
120+
swmm_output = Output(output_file='output_file.out')
121+
122+
# Dict[datetime, float]
123+
link_timeseries = swmm_output.get_link_timeseries(
124+
element_index=5,
125+
attribute=output.LinkAttribute.FLOW_RATE.value,
126+
)
127+
128+
```
90129

91130
## Unit and Regression Testing
92131

93-
Unit tests and regression tests have been developed for both the natively compiled SWMM computational engine and output toolkit as
94-
well as their respective python bindings. Unit tests for the natively compiled toolkits use the Boost 1.67.0 library and can be
95-
compiled by adding DBUILD_TESTS=ON flag during the cmake build phase as shown below:
132+
Unit tests and regression tests have been developed for both the natively compiled SWMM computational engine and output toolkit as well as their respective python bindings. Unit tests for the natively compiled toolkits use the Boost 1.67.0 library and can be compiled by adding DBUILD_TESTS=ON flag during the cmake build phase as shown below:
96133

97134
```bash
98135
ctest --test-dir . -DBUILD_TESTS=ON --config Debug --output-on-failure
@@ -116,7 +153,7 @@ pytest --data-dir <path-to-regression-testing-files> --atol <absolute-tolerance>
116153
The source code distributed here is identical to the code found at the official [SWMM website](https://www.epa.gov/water-research/storm-water-management-model-swmm).
117154
The SWMM website also hosts the official manuals and installation binaries for the SWMM software.
118155

119-
A live web version of the SWMM documentation of the API and user manuals can be found on the [SWMM GitHub Pages website](https://usepa.github.io/Stormwater-Management-Model). Note that this is an alpha version that is still under development and has yet to go through EPA'S official QAQC review process.
156+
A live web version of the SWMM documentation of the API and user manuals can be found on the [SWMM GitHub Pages website](https://usepa.github.io/Stormwater-Management-Model). Note that this is an experimental version that is still under development and has yet to go through EPA'S official quality assurance review process.
120157

121158
## Disclaimer
122159
The United States Environmental Protection Agency (EPA) GitHub project code is provided on an "as is" basis and the user assumes responsibility for its use. EPA has relinquished control of the information and no longer has responsibility to protect the integrity, confidentiality, or availability of the information. Any reference to specific commercial products, processes, or services by service mark, trademark, manufacturer, or otherwise, does not constitute or imply their endorsement, recommendation or favoring by EPA. The EPA seal and logo shall not be used in any manner to imply endorsement of any commercial product or activity by EPA or the United States Government.

python/MANIFEST.in

Whitespace-only changes.

python/epaswmm/CMakeLists.txt

+3
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,9 @@
22
# Created by: Caleb Buahin (EPA/ORD/CESER/WID)
33
# Created on: 2024-11-19
44

5+
# Find Python extensions
6+
find_package(PythonExtensions REQUIRED)
7+
58
# Add Cython targets for epaswmm
69
add_cython_target(epaswmm epaswmm.pyx CXX PY3)
710

python/epaswmm/__init__.py

+23
Original file line numberDiff line numberDiff line change
@@ -1 +1,24 @@
1+
"""
2+
EPASWMM Python API
3+
4+
This module provides a Python interface to the EPASWMM library.
5+
6+
"""
7+
import os
8+
import platform
9+
import sys
10+
11+
# Adds directory containing swmm libraries to path
12+
if platform.system() == "Windows":
13+
lib_dir = os.path.join(sys.prefix, 'bin')
14+
if hasattr(os, 'add_dll_directory'):
15+
conda_exists = os.path.exists(os.path.join(sys.prefix, 'conda-meta'))
16+
if conda_exists:
17+
os.environ['CONDA_DLL_SEARCH_MODIFICATION_ENABLE'] = "1"
18+
os.add_dll_directory(lib_dir)
19+
else:
20+
os.environ["PATH"] = lib_dir + ";" + os.environ["PATH"]
21+
122
from .epaswmm import *
23+
from . import solver
24+
from . import output

python/epaswmm/epaswmm.pyi

+2-7
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,10 @@
1-
# Description: Cython module for encoding and decoding SWMM datetimes
2-
# Created by: Caleb Buahin (EPA/ORD/CESER/WID)
3-
# Created on: 2024-11-19
4-
51
"""
62
This type stub file was generated by cyright.
73
"""
84

95
from typing import Any
10-
from datetime import datetime
116

12-
def encode_swmm_datetime(pdatetime: datetime) -> float:
7+
def encode_swmm_datetime(pdatetime: Any) -> float:
138
"""
149
Encode a datetime object into a SWMM datetime.
1510
@@ -25,7 +20,7 @@ def encode_swmm_datetime(pdatetime: datetime) -> float:
2520
"""
2621
...
2722

28-
def decode_swmm_datetime(swmm_datetime: float) -> datetime:
23+
def decode_swmm_datetime(swmm_datetime: float) -> Any:
2924
"""
3025
Decode a SWMM datetime into a datetime object.
3126

python/epaswmm/output/__init__.py

-2
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,4 @@
1010
SystemAttribute,
1111
SWMMOutputException,
1212
Output,
13-
decode_swmm_datetime,
14-
encode_swmm_datetime,
1513
)

python/epaswmm/output/output.pyi

+40-28
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,12 @@
1-
# Description: Cython module for epaswmm output file processing and data extraction functions for the epaswmm python package.
2-
# Created by: Caleb Buahin (EPA/ORD/CESER/WID)
3-
# Created on: 2024-11-19
41
"""
52
This type stub file was generated by cyright.
63
"""
74

8-
from enum import IntEnum
5+
from enum import Enum
96
from typing import Any, Dict, List, Optional, Tuple
10-
from datetime import datetime
7+
from cpython.datetime import datetime
118

12-
class UnitSystem(IntEnum):
9+
class UnitSystem(Enum):
1310
"""
1411
Enumeration of the unit system used in the output file.
1512
@@ -22,7 +19,7 @@ class UnitSystem(IntEnum):
2219
SI = ...
2320

2421

25-
class FlowUnits(IntEnum):
22+
class FlowUnits(Enum):
2623
"""
2724
Enumeration of the flow units used in the simulation.
2825
@@ -47,7 +44,7 @@ class FlowUnits(IntEnum):
4744
MLD = ...
4845

4946

50-
class ConcentrationUnits(IntEnum):
47+
class ConcentrationUnits(Enum):
5148
"""
5249
Enumeration of the concentration units used in the simulation.
5350
@@ -66,7 +63,7 @@ class ConcentrationUnits(IntEnum):
6663
NONE = ...
6764

6865

69-
class ElementType(IntEnum):
66+
class ElementType(Enum):
7067
"""
7168
Enumeration of the SWMM element types.
7269
@@ -88,7 +85,7 @@ class ElementType(IntEnum):
8885
POLLUTANT = ...
8986

9087

91-
class TimeAttribute(IntEnum):
88+
class TimeAttribute(Enum):
9289
"""
9390
Enumeration of the report time related attributes.
9491
@@ -101,7 +98,7 @@ class TimeAttribute(IntEnum):
10198
NUM_PERIODS = ...
10299

103100

104-
class SubcatchAttribute(IntEnum):
101+
class SubcatchAttribute(Enum):
105102
"""
106103
Enumeration of the subcatchment attributes.
107104
@@ -135,7 +132,7 @@ class SubcatchAttribute(IntEnum):
135132
POLLUTANT_CONCENTRATION = ...
136133

137134

138-
class NodeAttribute(IntEnum):
135+
class NodeAttribute(Enum):
139136
"""
140137
Enumeration of the node attributes.
141138
@@ -163,7 +160,7 @@ class NodeAttribute(IntEnum):
163160
POLLUTANT_CONCENTRATION = ...
164161

165162

166-
class LinkAttribute(IntEnum):
163+
class LinkAttribute(Enum):
167164
"""
168165
Enumeration of the link attributes.
169166
@@ -188,7 +185,7 @@ class LinkAttribute(IntEnum):
188185
POLLUTANT_CONCENTRATION = ...
189186

190187

191-
class SystemAttribute(IntEnum):
188+
class SystemAttribute(Enum):
192189
"""
193190
Enumeration of the system attributes.
194191
@@ -237,6 +234,21 @@ class SystemAttribute(IntEnum):
237234
EVAPORATION_RATE = ...
238235

239236

237+
class SWMMOutputException(Exception):
238+
"""
239+
Exception class for SWMM output file processing errors.
240+
"""
241+
def __init__(self, message: str) -> None:
242+
"""
243+
Constructor to initialize the exception message.
244+
245+
:param message: Error message.
246+
:type message: str
247+
"""
248+
...
249+
250+
251+
240252
class Output:
241253
"""
242254
Class to read and process the output file generated by the SWMM simulation.
@@ -315,7 +327,7 @@ class Output:
315327
...
316328

317329
@property
318-
def start_date(self) -> datetime:
330+
def start_date(self) -> Any:
319331
"""
320332
Method to get the start date of the simulation in the SWMM output file.
321333
@@ -356,7 +368,7 @@ class Output:
356368
"""
357369
...
358370

359-
def get_element_names(self, element_type: int) -> List[str]:
371+
def get_element_names(self, element_type: int) -> list:
360372
"""
361373
Method to get the names of all elements of a given type in the SWMM output file.
362374
@@ -367,7 +379,7 @@ class Output:
367379
"""
368380
...
369381

370-
def get_subcatchment_timeseries(self, element_index: int, attribute: int, start_date_index: int = ..., end_date_index: int = ...) -> Dict[datetime, float]:
382+
def get_subcatchment_timeseries(self, element_index: int, attribute: int, start_date_index: int = ..., end_date_index: int = ...) -> dict:
371383
"""
372384
Method to get the time series data for a subcatchment attribute in the SWMM output file.
373385
@@ -385,7 +397,7 @@ class Output:
385397
"""
386398
...
387399

388-
def get_node_timeseries(self, element_index: int, attribute: int, start_date_index: int = ..., end_date_index: int = ...) -> Dict[datetime, float]:
400+
def get_node_timeseries(self, element_index: int, attribute: int, start_date_index: int = ..., end_date_index: int = ...) -> dict:
389401
"""
390402
Method to get the time series data for a node attribute in the SWMM output file.
391403
@@ -402,7 +414,7 @@ class Output:
402414
"""
403415
...
404416

405-
def get_link_timeseries(self, element_index: int, attribute: int, start_date_index: int = ..., end_date_index: int = ...) -> Dict[datetime, float]:
417+
def get_link_timeseries(self, element_index: int, attribute: int, start_date_index: int = ..., end_date_index: int = ...) -> dict:
406418
"""
407419
Method to get the time series data for a link attribute in the SWMM output file.
408420
@@ -419,7 +431,7 @@ class Output:
419431
"""
420432
...
421433

422-
def get_system_timeseries(self, attribute: int, start_date_index: int = ..., end_date_index: int = ...) -> Dict[datetime, float]:
434+
def get_system_timeseries(self, attribute: int, start_date_index: int = ..., end_date_index: int = ...) -> dict:
423435
"""
424436
Method to get the time series data for a system attribute in the SWMM output file.
425437
@@ -434,7 +446,7 @@ class Output:
434446
"""
435447
...
436448

437-
def get_subcatchment_values_by_time_and_attribute(self, time_index: int, attribute: int) -> Dict[str, float]:
449+
def get_subcatchment_values_by_time_and_attribute(self, time_index: int, attribute: int) -> dict:
438450
"""
439451
Method to get the subcatchment values for all subcatchments for a given time index and attribute.
440452
@@ -447,7 +459,7 @@ class Output:
447459
"""
448460
...
449461

450-
def get_node_values_by_time_and_attribute(self, time_index: int, attribute: int) -> Dict[str, float]:
462+
def get_node_values_by_time_and_attribute(self, time_index: int, attribute: int) -> dict:
451463
"""
452464
Method to get the node values for all nodes for a given time index and attribute.
453465
@@ -460,7 +472,7 @@ class Output:
460472
"""
461473
...
462474

463-
def get_link_values_by_time_and_attribute(self, time_index: int, attribute: int) -> Dict[str, float]:
475+
def get_link_values_by_time_and_attribute(self, time_index: int, attribute: int) -> dict:
464476
"""
465477
Method to get the link values for all links for a given time index and attribute.
466478
@@ -473,7 +485,7 @@ class Output:
473485
"""
474486
...
475487

476-
def get_system_values_by_time_and_attribute(self, time_index: int, attribute: int) -> Dict[str, float]:
488+
def get_system_values_by_time_and_attribute(self, time_index: int, attribute: int) -> dict:
477489
"""
478490
Method to get the system values for a given time index and attribute.
479491
@@ -486,7 +498,7 @@ class Output:
486498
"""
487499
...
488500

489-
def get_subcatchment_values_by_time_and_element_index(self, time_index: int, element_index: int) -> Dict[str, float]:
501+
def get_subcatchment_values_by_time_and_element_index(self, time_index: int, element_index: int) -> dict:
490502
"""
491503
Method to get all attributes of a given subcatchment for specified time.
492504
@@ -499,7 +511,7 @@ class Output:
499511
"""
500512
...
501513

502-
def get_node_values_by_time_and_element_index(self, time_index: int, element_index: int) -> Dict[str, float]:
514+
def get_node_values_by_time_and_element_index(self, time_index: int, element_index: int) -> dict:
503515
"""
504516
Method to get all attributes of a given node for specified time.
505517
@@ -512,7 +524,7 @@ class Output:
512524
"""
513525
...
514526

515-
def get_link_values_by_time_and_element_index(self, time_index: int, element_index: int) -> Dict[str, float]:
527+
def get_link_values_by_time_and_element_index(self, time_index: int, element_index: int) -> dict:
516528
"""
517529
Method to get all attributes of a given link for specified time.
518530
@@ -526,7 +538,7 @@ class Output:
526538
"""
527539
...
528540

529-
def get_system_values_by_time(self, time_index: int) -> Dict[str, float]:
541+
def get_system_values_by_time(self, time_index: int) -> dict:
530542
"""
531543
Method to get all attributes of the system for specified time.
532544

0 commit comments

Comments
 (0)