swmmEnv 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- swmmenv-0.1.0/PKG-INFO +106 -0
- swmmenv-0.1.0/README.md +70 -0
- swmmenv-0.1.0/examples/example_config.yaml +76 -0
- swmmenv-0.1.0/examples/manual_control.py +214 -0
- swmmenv-0.1.0/examples/train_mappo.py +197 -0
- swmmenv-0.1.0/pyproject.toml +51 -0
- swmmenv-0.1.0/requirements.txt +14 -0
- swmmenv-0.1.0/setup.cfg +4 -0
- swmmenv-0.1.0/setup.py +47 -0
- swmmenv-0.1.0/swmmEnv/__init__.py +20 -0
- swmmenv-0.1.0/swmmEnv/config/__init__.py +8 -0
- swmmenv-0.1.0/swmmEnv/config/default_config.yaml +77 -0
- swmmenv-0.1.0/swmmEnv/config/loader.py +204 -0
- swmmenv-0.1.0/swmmEnv/envs/__init__.py +9 -0
- swmmenv-0.1.0/swmmEnv/envs/register_env.py +247 -0
- swmmenv-0.1.0/swmmEnv/envs/swmm_env/__init__.py +9 -0
- swmmenv-0.1.0/swmmEnv/envs/swmm_env/env.py +364 -0
- swmmenv-0.1.0/swmmEnv/envs/swmm_env/pettingzoo_env.py +341 -0
- swmmenv-0.1.0/swmmEnv/reward/__init__.py +7 -0
- swmmenv-0.1.0/swmmEnv/reward/custom_reward.py +129 -0
- swmmenv-0.1.0/swmmEnv/reward/default_reward.py +197 -0
- swmmenv-0.1.0/swmmEnv/sim/__init__.py +13 -0
- swmmenv-0.1.0/swmmEnv/sim/engine.py +523 -0
- swmmenv-0.1.0/swmmEnv/sim/mapping.py +268 -0
- swmmenv-0.1.0/swmmEnv/sim/normalizer.py +264 -0
- swmmenv-0.1.0/swmmEnv/sim/time_sync.py +138 -0
- swmmenv-0.1.0/swmmEnv.egg-info/PKG-INFO +106 -0
- swmmenv-0.1.0/swmmEnv.egg-info/SOURCES.txt +37 -0
- swmmenv-0.1.0/swmmEnv.egg-info/dependency_links.txt +1 -0
- swmmenv-0.1.0/swmmEnv.egg-info/requires.txt +13 -0
- swmmenv-0.1.0/swmmEnv.egg-info/top_level.txt +1 -0
- swmmenv-0.1.0/tests/__init__.py +1 -0
- swmmenv-0.1.0/tests/test_engine.py +144 -0
- swmmenv-0.1.0/tests/test_imports.py +66 -0
- swmmenv-0.1.0/tests/test_mapping.py +195 -0
- swmmenv-0.1.0/tests/test_normalizer.py +149 -0
- swmmenv-0.1.0/tests/test_pettingzoo_env.py +268 -0
- swmmenv-0.1.0/tests/test_swmm_env.py +254 -0
- swmmenv-0.1.0/tests/test_time_sync.py +128 -0
swmmenv-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: swmmEnv
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Multi-agent reinforcement learning environment for SWMM stormwater simulation
|
|
5
|
+
Home-page: https://github.com/DujDDx/swmmEnv
|
|
6
|
+
Author: dujddx
|
|
7
|
+
Author-email: dujddx <dujddx@163.com>
|
|
8
|
+
License: MIT
|
|
9
|
+
Project-URL: Homepage, https://github.com/DujDDx/swmmEnv
|
|
10
|
+
Project-URL: Documentation, https://github.com/DujDDx/swmmEnv#readme
|
|
11
|
+
Project-URL: Repository, https://github.com/DujDDx/swmmEnv.git
|
|
12
|
+
Classifier: Development Status :: 3 - Alpha
|
|
13
|
+
Classifier: Intended Audience :: Science/Research
|
|
14
|
+
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
|
15
|
+
Classifier: Programming Language :: Python :: 3
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.8
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
20
|
+
Requires-Python: >=3.8
|
|
21
|
+
Description-Content-Type: text/markdown
|
|
22
|
+
Requires-Dist: pyswmm>=2.1.0
|
|
23
|
+
Requires-Dist: pettingzoo>=1.24.0
|
|
24
|
+
Requires-Dist: gymnasium>=0.29.0
|
|
25
|
+
Requires-Dist: numpy>=1.24.0
|
|
26
|
+
Requires-Dist: pyyaml>=6.0
|
|
27
|
+
Provides-Extra: dev
|
|
28
|
+
Requires-Dist: pytest>=7.0; extra == "dev"
|
|
29
|
+
Requires-Dist: pytest-cov>=4.0; extra == "dev"
|
|
30
|
+
Provides-Extra: marl
|
|
31
|
+
Requires-Dist: marllib; extra == "marl"
|
|
32
|
+
Requires-Dist: ray[rllib]>=2.0; extra == "marl"
|
|
33
|
+
Dynamic: author
|
|
34
|
+
Dynamic: home-page
|
|
35
|
+
Dynamic: requires-python
|
|
36
|
+
|
|
37
|
+
# SWMMEnv
|
|
38
|
+
|
|
39
|
+
Multi-agent reinforcement learning environment for SWMM (Storm Water Management Model) simulation.
|
|
40
|
+
|
|
41
|
+
## Overview
|
|
42
|
+
|
|
43
|
+
SWMMEnv provides a PettingZoo-compatible interface for training multi-agent reinforcement learning (MARL) algorithms on stormwater management simulations. It integrates:
|
|
44
|
+
|
|
45
|
+
- **PySWMM**: SWMM simulation engine
|
|
46
|
+
- **PettingZoo**: Multi-agent RL environment interface
|
|
47
|
+
- **MARLlib**: Training framework (MAPPO, QMIX, etc.)
|
|
48
|
+
|
|
49
|
+
## Features
|
|
50
|
+
|
|
51
|
+
- Read standard SWMM `.inp` files and rainfall `.dat` files
|
|
52
|
+
- Control pump stations, gates, and weirs
|
|
53
|
+
- Global reward for coupled stormwater systems
|
|
54
|
+
- Config-driven design for different SWMM models
|
|
55
|
+
- Time synchronization between RL steps and SWMM simulation steps
|
|
56
|
+
- State normalization for stable training
|
|
57
|
+
|
|
58
|
+
## Installation
|
|
59
|
+
|
|
60
|
+
```bash
|
|
61
|
+
pip install -e .
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
## Quick Start
|
|
65
|
+
|
|
66
|
+
```python
|
|
67
|
+
from swmmEnv import SWMMParallelEnv, load_config
|
|
68
|
+
|
|
69
|
+
# Load configuration
|
|
70
|
+
config = load_config("config/example.yaml")
|
|
71
|
+
|
|
72
|
+
# Create environment
|
|
73
|
+
env = SWMMParallelEnv(config)
|
|
74
|
+
|
|
75
|
+
# Reset and get initial observations
|
|
76
|
+
observations, info = env.reset()
|
|
77
|
+
|
|
78
|
+
# Take a step
|
|
79
|
+
actions = {"pump_1": 0.8, "gate_1": 0.5}
|
|
80
|
+
observations, rewards, terminations, truncations, infos = env.step(actions)
|
|
81
|
+
|
|
82
|
+
# Close environment
|
|
83
|
+
env.close()
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
## Configuration
|
|
87
|
+
|
|
88
|
+
See `config/default_config.yaml` for configuration structure.
|
|
89
|
+
|
|
90
|
+
## Project Structure
|
|
91
|
+
|
|
92
|
+
```
|
|
93
|
+
swmmEnv/
|
|
94
|
+
├── swmmEnv/
|
|
95
|
+
│ ├── sim/ # Simulation modules (engine, time_sync, normalizer, mapping)
|
|
96
|
+
│ ├── envs/ # RL environments (core MDP + PettingZoo wrapper)
|
|
97
|
+
│ ├── reward/ # Reward functions
|
|
98
|
+
│ └── config/ # Configuration system
|
|
99
|
+
├── tests/ # Unit tests
|
|
100
|
+
├── examples/ # Example scripts
|
|
101
|
+
└── data/ # Sample SWMM models
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
## License
|
|
105
|
+
|
|
106
|
+
MIT License
|
swmmenv-0.1.0/README.md
ADDED
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
# SWMMEnv
|
|
2
|
+
|
|
3
|
+
Multi-agent reinforcement learning environment for SWMM (Storm Water Management Model) simulation.
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
SWMMEnv provides a PettingZoo-compatible interface for training multi-agent reinforcement learning (MARL) algorithms on stormwater management simulations. It integrates:
|
|
8
|
+
|
|
9
|
+
- **PySWMM**: SWMM simulation engine
|
|
10
|
+
- **PettingZoo**: Multi-agent RL environment interface
|
|
11
|
+
- **MARLlib**: Training framework (MAPPO, QMIX, etc.)
|
|
12
|
+
|
|
13
|
+
## Features
|
|
14
|
+
|
|
15
|
+
- Read standard SWMM `.inp` files and rainfall `.dat` files
|
|
16
|
+
- Control pump stations, gates, and weirs
|
|
17
|
+
- Global reward for coupled stormwater systems
|
|
18
|
+
- Config-driven design for different SWMM models
|
|
19
|
+
- Time synchronization between RL steps and SWMM simulation steps
|
|
20
|
+
- State normalization for stable training
|
|
21
|
+
|
|
22
|
+
## Installation
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
pip install -e .
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
## Quick Start
|
|
29
|
+
|
|
30
|
+
```python
|
|
31
|
+
from swmmEnv import SWMMParallelEnv, load_config
|
|
32
|
+
|
|
33
|
+
# Load configuration
|
|
34
|
+
config = load_config("config/example.yaml")
|
|
35
|
+
|
|
36
|
+
# Create environment
|
|
37
|
+
env = SWMMParallelEnv(config)
|
|
38
|
+
|
|
39
|
+
# Reset and get initial observations
|
|
40
|
+
observations, info = env.reset()
|
|
41
|
+
|
|
42
|
+
# Take a step
|
|
43
|
+
actions = {"pump_1": 0.8, "gate_1": 0.5}
|
|
44
|
+
observations, rewards, terminations, truncations, infos = env.step(actions)
|
|
45
|
+
|
|
46
|
+
# Close environment
|
|
47
|
+
env.close()
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
## Configuration
|
|
51
|
+
|
|
52
|
+
See `config/default_config.yaml` for configuration structure.
|
|
53
|
+
|
|
54
|
+
## Project Structure
|
|
55
|
+
|
|
56
|
+
```
|
|
57
|
+
swmmEnv/
|
|
58
|
+
├── swmmEnv/
|
|
59
|
+
│ ├── sim/ # Simulation modules (engine, time_sync, normalizer, mapping)
|
|
60
|
+
│ ├── envs/ # RL environments (core MDP + PettingZoo wrapper)
|
|
61
|
+
│ ├── reward/ # Reward functions
|
|
62
|
+
│ └── config/ # Configuration system
|
|
63
|
+
├── tests/ # Unit tests
|
|
64
|
+
├── examples/ # Example scripts
|
|
65
|
+
└── data/ # Sample SWMM models
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
## License
|
|
69
|
+
|
|
70
|
+
MIT License
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
# SWMMEnv Example Configuration
|
|
2
|
+
|
|
3
|
+
# Input files (update paths to your actual SWMM model)
|
|
4
|
+
inp_file: "data/sample.inp"
|
|
5
|
+
rain_file: null # If null, rainfall defined in inp file
|
|
6
|
+
|
|
7
|
+
# Agents definition
|
|
8
|
+
# Each agent controls one SWMM element
|
|
9
|
+
agents:
|
|
10
|
+
pump_1:
|
|
11
|
+
type: pump
|
|
12
|
+
link_id: P1
|
|
13
|
+
upstream_node: J1
|
|
14
|
+
downstream_node: J2
|
|
15
|
+
gate_1:
|
|
16
|
+
type: weir
|
|
17
|
+
link_id: W1
|
|
18
|
+
upstream_node: J3
|
|
19
|
+
downstream_node: J4
|
|
20
|
+
|
|
21
|
+
# Nodes to observe for state information
|
|
22
|
+
obs_nodes:
|
|
23
|
+
- J1
|
|
24
|
+
- J2
|
|
25
|
+
- J3
|
|
26
|
+
- J4
|
|
27
|
+
|
|
28
|
+
# Raingage ID for rainfall observation
|
|
29
|
+
obs_raingage: RG1
|
|
30
|
+
|
|
31
|
+
# Time synchronization
|
|
32
|
+
# RL agent makes decisions every decision_interval seconds
|
|
33
|
+
# SWMM advances by swmm_step seconds per simulation step
|
|
34
|
+
time_sync:
|
|
35
|
+
decision_interval: 300 # 5 minutes
|
|
36
|
+
swmm_step: 10 # 10 seconds
|
|
37
|
+
|
|
38
|
+
# Observation/reward normalization parameters
|
|
39
|
+
normalization:
|
|
40
|
+
obs:
|
|
41
|
+
depth:
|
|
42
|
+
mean: 2.0
|
|
43
|
+
std: 1.5
|
|
44
|
+
flow:
|
|
45
|
+
mean: 0.5
|
|
46
|
+
std: 0.3
|
|
47
|
+
rainfall:
|
|
48
|
+
mean: 5.0
|
|
49
|
+
std: 10.0
|
|
50
|
+
setting:
|
|
51
|
+
mean: 0.5
|
|
52
|
+
std: 0.3
|
|
53
|
+
reward:
|
|
54
|
+
mean: 0.0
|
|
55
|
+
std: 10.0
|
|
56
|
+
|
|
57
|
+
# Reward function name (from swmmEnv.reward)
|
|
58
|
+
reward_fn: "default_reward"
|
|
59
|
+
|
|
60
|
+
# Reward weights for default_reward
|
|
61
|
+
reward_weights:
|
|
62
|
+
flooding: 1.0
|
|
63
|
+
level_deviation: 0.5
|
|
64
|
+
energy: 0.2
|
|
65
|
+
|
|
66
|
+
# Target water levels (optional, for level tracking reward)
|
|
67
|
+
target_levels:
|
|
68
|
+
J1: 1.5
|
|
69
|
+
J2: 1.0
|
|
70
|
+
|
|
71
|
+
# Episode configuration
|
|
72
|
+
max_steps: 1000
|
|
73
|
+
warmup_steps: 0 # Steps to run before episode starts
|
|
74
|
+
|
|
75
|
+
# Hotstart file for consistent resets (optional)
|
|
76
|
+
hotstart_file: null
|
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Manual control example for SWMMEnv.
|
|
3
|
+
|
|
4
|
+
This script demonstrates how to manually control the SWMM environment
|
|
5
|
+
without MARLlib, useful for testing and debugging.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import sys
|
|
9
|
+
import os
|
|
10
|
+
|
|
11
|
+
# Add parent directory to path for imports
|
|
12
|
+
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
|
13
|
+
|
|
14
|
+
from swmmEnv import SWMMParallelEnv, load_config
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def run_manual_control(config_path: str = None, num_episodes: int = 1):
|
|
18
|
+
"""
|
|
19
|
+
Run manual control demonstration.
|
|
20
|
+
|
|
21
|
+
Args:
|
|
22
|
+
config_path: Path to configuration file
|
|
23
|
+
num_episodes: Number of episodes to run
|
|
24
|
+
"""
|
|
25
|
+
# Load configuration
|
|
26
|
+
if config_path is None:
|
|
27
|
+
config = load_config(None)
|
|
28
|
+
print("Using default configuration")
|
|
29
|
+
else:
|
|
30
|
+
config = load_config(config_path)
|
|
31
|
+
print(f"Loaded configuration from: {config_path}")
|
|
32
|
+
|
|
33
|
+
# Create environment
|
|
34
|
+
env = SWMMParallelEnv(config)
|
|
35
|
+
|
|
36
|
+
print(f"\nEnvironment created:")
|
|
37
|
+
print(f" Agents: {env.possible_agents}")
|
|
38
|
+
print(f" Max steps: {config.get('max_steps', 1000)}")
|
|
39
|
+
|
|
40
|
+
for episode in range(num_episodes):
|
|
41
|
+
print(f"\n{'='*60}")
|
|
42
|
+
print(f"Episode {episode + 1}/{num_episodes}")
|
|
43
|
+
print('='*60)
|
|
44
|
+
|
|
45
|
+
# Reset environment
|
|
46
|
+
observations, info = env.reset()
|
|
47
|
+
|
|
48
|
+
print(f"\nInitial observations:")
|
|
49
|
+
for agent, obs in observations.items():
|
|
50
|
+
print(f" {agent}: {obs}")
|
|
51
|
+
|
|
52
|
+
# Run episode with random actions
|
|
53
|
+
step = 0
|
|
54
|
+
total_reward = 0.0
|
|
55
|
+
done = False
|
|
56
|
+
|
|
57
|
+
while not done:
|
|
58
|
+
# Generate random actions
|
|
59
|
+
actions = {}
|
|
60
|
+
for agent in env.agents:
|
|
61
|
+
# Random action between 0 and 1
|
|
62
|
+
import numpy as np
|
|
63
|
+
actions[agent] = np.array([np.random.random()], dtype=np.float32)
|
|
64
|
+
|
|
65
|
+
# Step environment
|
|
66
|
+
observations, rewards, terminations, truncations, infos = env.step(actions)
|
|
67
|
+
|
|
68
|
+
# Track reward (all agents get same reward)
|
|
69
|
+
step_reward = list(rewards.values())[0]
|
|
70
|
+
total_reward += step_reward
|
|
71
|
+
step += 1
|
|
72
|
+
|
|
73
|
+
# Check done
|
|
74
|
+
done = any(terminations.values())
|
|
75
|
+
|
|
76
|
+
# Print progress every 10 steps
|
|
77
|
+
if step % 10 == 0:
|
|
78
|
+
print(f" Step {step}: reward={step_reward:.3f}, "
|
|
79
|
+
f"cumulative={total_reward:.3f}")
|
|
80
|
+
|
|
81
|
+
# Render every 50 steps
|
|
82
|
+
if step % 50 == 0:
|
|
83
|
+
env.render()
|
|
84
|
+
|
|
85
|
+
print(f"\nEpisode finished after {step} steps")
|
|
86
|
+
print(f"Total reward: {total_reward:.3f}")
|
|
87
|
+
|
|
88
|
+
# Get final state
|
|
89
|
+
state = env.core_env.get_state()
|
|
90
|
+
print(f"\nFinal state:")
|
|
91
|
+
print(f" Total flooding: {state.get('rainfall', 0):.1f} mm/h")
|
|
92
|
+
print(f" Rainfall: {env.core_env.engine.get_total_flooding():.3f} m³/s")
|
|
93
|
+
|
|
94
|
+
# Close environment
|
|
95
|
+
env.close()
|
|
96
|
+
print("\nEnvironment closed.")
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
def interactive_control(config_path: str = None):
|
|
100
|
+
"""
|
|
101
|
+
Interactive control mode.
|
|
102
|
+
|
|
103
|
+
Allows user to input actions manually.
|
|
104
|
+
"""
|
|
105
|
+
config = load_config(config_path)
|
|
106
|
+
env = SWMMParallelEnv(config)
|
|
107
|
+
|
|
108
|
+
print("\n" + "="*60)
|
|
109
|
+
print("Interactive SWMM Control")
|
|
110
|
+
print("="*60)
|
|
111
|
+
print(f"Agents: {env.possible_agents}")
|
|
112
|
+
print("\nCommands:")
|
|
113
|
+
print(" Enter action value (0.0-1.0) for each agent")
|
|
114
|
+
print(" 'q' - Quit")
|
|
115
|
+
print(" 'r' - Reset episode")
|
|
116
|
+
print(" 's' - Show state")
|
|
117
|
+
print("="*60)
|
|
118
|
+
|
|
119
|
+
observations, _ = env.reset()
|
|
120
|
+
step = 0
|
|
121
|
+
total_reward = 0.0
|
|
122
|
+
|
|
123
|
+
while True:
|
|
124
|
+
print(f"\n--- Step {step} ---")
|
|
125
|
+
|
|
126
|
+
# Get actions from user
|
|
127
|
+
actions = {}
|
|
128
|
+
for agent in env.agents:
|
|
129
|
+
while True:
|
|
130
|
+
try:
|
|
131
|
+
value = input(f" {agent} [0.0-1.0]: ").strip()
|
|
132
|
+
|
|
133
|
+
if value.lower() == 'q':
|
|
134
|
+
env.close()
|
|
135
|
+
print("Goodbye!")
|
|
136
|
+
return
|
|
137
|
+
|
|
138
|
+
if value.lower() == 'r':
|
|
139
|
+
observations, _ = env.reset()
|
|
140
|
+
step = 0
|
|
141
|
+
total_reward = 0.0
|
|
142
|
+
print("Episode reset!")
|
|
143
|
+
break
|
|
144
|
+
|
|
145
|
+
if value.lower() == 's':
|
|
146
|
+
env.render()
|
|
147
|
+
continue
|
|
148
|
+
|
|
149
|
+
action = float(value)
|
|
150
|
+
action = max(0.0, min(1.0, action))
|
|
151
|
+
import numpy as np
|
|
152
|
+
actions[agent] = np.array([action], dtype=np.float32)
|
|
153
|
+
break
|
|
154
|
+
|
|
155
|
+
except ValueError:
|
|
156
|
+
print(" Invalid input. Enter a number between 0.0 and 1.0")
|
|
157
|
+
|
|
158
|
+
if not actions:
|
|
159
|
+
continue
|
|
160
|
+
|
|
161
|
+
# Step environment
|
|
162
|
+
observations, rewards, terminations, truncations, infos = env.step(actions)
|
|
163
|
+
|
|
164
|
+
step_reward = list(rewards.values())[0]
|
|
165
|
+
total_reward += step_reward
|
|
166
|
+
step += 1
|
|
167
|
+
|
|
168
|
+
print(f" Reward: {step_reward:.3f} (cumulative: {total_reward:.3f})")
|
|
169
|
+
|
|
170
|
+
# Check termination
|
|
171
|
+
if any(terminations.values()):
|
|
172
|
+
print("\nEpisode ended!")
|
|
173
|
+
print(f"Total steps: {step}")
|
|
174
|
+
print(f"Total reward: {total_reward:.3f}")
|
|
175
|
+
|
|
176
|
+
response = input("\nStart new episode? (y/n): ").strip().lower()
|
|
177
|
+
if response == 'y':
|
|
178
|
+
observations, _ = env.reset()
|
|
179
|
+
step = 0
|
|
180
|
+
total_reward = 0.0
|
|
181
|
+
else:
|
|
182
|
+
break
|
|
183
|
+
|
|
184
|
+
env.close()
|
|
185
|
+
|
|
186
|
+
|
|
187
|
+
if __name__ == "__main__":
|
|
188
|
+
import argparse
|
|
189
|
+
|
|
190
|
+
parser = argparse.ArgumentParser(description="SWMMEnv manual control")
|
|
191
|
+
parser.add_argument(
|
|
192
|
+
'--config',
|
|
193
|
+
type=str,
|
|
194
|
+
default=None,
|
|
195
|
+
help='Path to configuration file'
|
|
196
|
+
)
|
|
197
|
+
parser.add_argument(
|
|
198
|
+
'--episodes',
|
|
199
|
+
type=int,
|
|
200
|
+
default=1,
|
|
201
|
+
help='Number of episodes to run'
|
|
202
|
+
)
|
|
203
|
+
parser.add_argument(
|
|
204
|
+
'--interactive',
|
|
205
|
+
action='store_true',
|
|
206
|
+
help='Run in interactive mode'
|
|
207
|
+
)
|
|
208
|
+
|
|
209
|
+
args = parser.parse_args()
|
|
210
|
+
|
|
211
|
+
if args.interactive:
|
|
212
|
+
interactive_control(args.config)
|
|
213
|
+
else:
|
|
214
|
+
run_manual_control(args.config, args.episodes)
|
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
"""
|
|
2
|
+
MARLlib training example for SWMMEnv.
|
|
3
|
+
|
|
4
|
+
This script demonstrates how to train a multi-agent policy
|
|
5
|
+
using MARLlib with the SWMMEnv.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import sys
|
|
9
|
+
import os
|
|
10
|
+
|
|
11
|
+
# Add parent directory to path for imports
|
|
12
|
+
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def train_mappo(config_path: str = None, num_steps: int = 100000):
|
|
16
|
+
"""
|
|
17
|
+
Train MAPPO on SWMMEnv using MARLlib.
|
|
18
|
+
|
|
19
|
+
Args:
|
|
20
|
+
config_path: Path to SWMMEnv configuration file
|
|
21
|
+
num_steps: Number of training timesteps
|
|
22
|
+
"""
|
|
23
|
+
try:
|
|
24
|
+
from marllib import marl
|
|
25
|
+
import ray
|
|
26
|
+
except ImportError:
|
|
27
|
+
print("MARLlib is required for this example.")
|
|
28
|
+
print("Install with: pip install marllib ray[rllib]")
|
|
29
|
+
return
|
|
30
|
+
|
|
31
|
+
# Import environment registration
|
|
32
|
+
from swmmEnv.envs.register_env import make_env, register_with_marllib
|
|
33
|
+
|
|
34
|
+
# Register environment with MARLlib
|
|
35
|
+
register_with_marllib()
|
|
36
|
+
|
|
37
|
+
# Create environment
|
|
38
|
+
if config_path:
|
|
39
|
+
env = make_env(
|
|
40
|
+
environment_name="swmm",
|
|
41
|
+
map_name="custom",
|
|
42
|
+
config_path=config_path
|
|
43
|
+
)
|
|
44
|
+
else:
|
|
45
|
+
env = make_env(environment_name="swmm", map_name="control")
|
|
46
|
+
|
|
47
|
+
# Get environment info
|
|
48
|
+
env_info = env.get_env_info()
|
|
49
|
+
print("\nEnvironment info:")
|
|
50
|
+
print(f" Number of agents: {env_info['num_agents']}")
|
|
51
|
+
print(f" Episode limit: {env_info['episode_limit']}")
|
|
52
|
+
|
|
53
|
+
# Build model configuration
|
|
54
|
+
model = marl.build_model(
|
|
55
|
+
environment=env,
|
|
56
|
+
algorithm=marl.algos.mappo,
|
|
57
|
+
model_preference={
|
|
58
|
+
"core_arch": "mlp", # MLP for non-sequential control
|
|
59
|
+
"encode_layer": "128-128",
|
|
60
|
+
"hidden_dim": 64,
|
|
61
|
+
}
|
|
62
|
+
)
|
|
63
|
+
|
|
64
|
+
# Initialize MAPPO algorithm
|
|
65
|
+
mappo = marl.algos.mappo(
|
|
66
|
+
hyperparam_source="common"
|
|
67
|
+
)
|
|
68
|
+
|
|
69
|
+
# Training configuration
|
|
70
|
+
train_config = {
|
|
71
|
+
"lr": 0.0005,
|
|
72
|
+
"gamma": 0.99,
|
|
73
|
+
"batch_episode": 10,
|
|
74
|
+
"num_sgd_iter": 5,
|
|
75
|
+
"vf_loss_coeff": 1.0,
|
|
76
|
+
"entropy_coeff": 0.01,
|
|
77
|
+
"clip_param": 0.3,
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
print("\nStarting training...")
|
|
81
|
+
print(f" Algorithm: MAPPO")
|
|
82
|
+
print(f" Target timesteps: {num_steps}")
|
|
83
|
+
|
|
84
|
+
# Start training
|
|
85
|
+
mappo.fit(
|
|
86
|
+
env=env,
|
|
87
|
+
model=model,
|
|
88
|
+
stop={"timesteps_total": num_steps},
|
|
89
|
+
**train_config
|
|
90
|
+
)
|
|
91
|
+
|
|
92
|
+
print("\nTraining completed!")
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
def train_with_rllib(config_path: str = None, num_steps: int = 100000):
|
|
96
|
+
"""
|
|
97
|
+
Train using Ray RLlib directly (alternative to MARLlib).
|
|
98
|
+
|
|
99
|
+
This shows direct RLlib integration without MARLlib's abstraction.
|
|
100
|
+
"""
|
|
101
|
+
try:
|
|
102
|
+
from ray.rllib.algorithms.ppo import PPOConfig
|
|
103
|
+
from ray.rllib.env import ParallelPettingZooEnv
|
|
104
|
+
import ray
|
|
105
|
+
except ImportError:
|
|
106
|
+
print("Ray RLlib is required for this example.")
|
|
107
|
+
print("Install with: pip install ray[rllib]")
|
|
108
|
+
return
|
|
109
|
+
|
|
110
|
+
from swmmEnv import SWMMParallelEnv, load_config
|
|
111
|
+
|
|
112
|
+
# Load config
|
|
113
|
+
config = load_config(config_path)
|
|
114
|
+
|
|
115
|
+
# Initialize Ray
|
|
116
|
+
ray.init(ignore_reinit_error=True)
|
|
117
|
+
|
|
118
|
+
# Create environment creator
|
|
119
|
+
def env_creator(env_config):
|
|
120
|
+
env_config_inner = config.copy()
|
|
121
|
+
env_config_inner.update(env_config)
|
|
122
|
+
return SWMMParallelEnv(env_config_inner)
|
|
123
|
+
|
|
124
|
+
# Register environment with Ray
|
|
125
|
+
from ray.tune.registry import register_env
|
|
126
|
+
register_env("swmm_env", env_creator)
|
|
127
|
+
|
|
128
|
+
# Configure PPO for multi-agent
|
|
129
|
+
config = (
|
|
130
|
+
PPOConfig()
|
|
131
|
+
.environment("swmm_env")
|
|
132
|
+
.multi_agent(
|
|
133
|
+
policies={
|
|
134
|
+
"shared_policy": None, # Will be auto-generated
|
|
135
|
+
},
|
|
136
|
+
policy_mapping_fn=lambda agent_id: "shared_policy",
|
|
137
|
+
)
|
|
138
|
+
.training(
|
|
139
|
+
lr=0.0005,
|
|
140
|
+
gamma=0.99,
|
|
141
|
+
train_batch_size=4000,
|
|
142
|
+
sgd_minibatch_size=128,
|
|
143
|
+
num_sgd_iter=10,
|
|
144
|
+
)
|
|
145
|
+
.resources(
|
|
146
|
+
num_gpus=0, # Set to 1 if GPU available
|
|
147
|
+
)
|
|
148
|
+
)
|
|
149
|
+
|
|
150
|
+
# Build algorithm
|
|
151
|
+
algo = config.build()
|
|
152
|
+
|
|
153
|
+
print("\nStarting training with Ray RLlib...")
|
|
154
|
+
|
|
155
|
+
# Training loop
|
|
156
|
+
for i in range(num_steps // 4000):
|
|
157
|
+
result = algo.train()
|
|
158
|
+
print(f"Iteration {i}: "
|
|
159
|
+
f"timesteps={result['timesteps_total']}, "
|
|
160
|
+
f"episode_reward_mean={result['episode_reward_mean']:.2f}")
|
|
161
|
+
|
|
162
|
+
print("\nTraining completed!")
|
|
163
|
+
|
|
164
|
+
# Cleanup
|
|
165
|
+
ray.shutdown()
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
if __name__ == "__main__":
|
|
169
|
+
import argparse
|
|
170
|
+
|
|
171
|
+
parser = argparse.ArgumentParser(description="Train MARL on SWMMEnv")
|
|
172
|
+
parser.add_argument(
|
|
173
|
+
'--config',
|
|
174
|
+
type=str,
|
|
175
|
+
default=None,
|
|
176
|
+
help='Path to SWMMEnv configuration file'
|
|
177
|
+
)
|
|
178
|
+
parser.add_argument(
|
|
179
|
+
'--steps',
|
|
180
|
+
type=int,
|
|
181
|
+
default=100000,
|
|
182
|
+
help='Number of training timesteps'
|
|
183
|
+
)
|
|
184
|
+
parser.add_argument(
|
|
185
|
+
'--backend',
|
|
186
|
+
type=str,
|
|
187
|
+
choices=['marllib', 'rllib'],
|
|
188
|
+
default='marllib',
|
|
189
|
+
help='Training backend to use'
|
|
190
|
+
)
|
|
191
|
+
|
|
192
|
+
args = parser.parse_args()
|
|
193
|
+
|
|
194
|
+
if args.backend == 'marllib':
|
|
195
|
+
train_mappo(args.config, args.steps)
|
|
196
|
+
else:
|
|
197
|
+
train_with_rllib(args.config, args.steps)
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=45.0", "wheel", "setuptools-scm>=6.2"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "swmmEnv"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
description = "Multi-agent reinforcement learning environment for SWMM stormwater simulation"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
license = {text = "MIT"}
|
|
11
|
+
authors = [
|
|
12
|
+
{name = "dujddx", email = "dujddx@163.com"}
|
|
13
|
+
]
|
|
14
|
+
requires-python = ">=3.8"
|
|
15
|
+
classifiers = [
|
|
16
|
+
"Development Status :: 3 - Alpha",
|
|
17
|
+
"Intended Audience :: Science/Research",
|
|
18
|
+
"Topic :: Scientific/Engineering :: Artificial Intelligence",
|
|
19
|
+
"Programming Language :: Python :: 3",
|
|
20
|
+
"Programming Language :: Python :: 3.8",
|
|
21
|
+
"Programming Language :: Python :: 3.9",
|
|
22
|
+
"Programming Language :: Python :: 3.10",
|
|
23
|
+
"Programming Language :: Python :: 3.11",
|
|
24
|
+
]
|
|
25
|
+
|
|
26
|
+
dependencies = [
|
|
27
|
+
"pyswmm>=2.1.0",
|
|
28
|
+
"pettingzoo>=1.24.0",
|
|
29
|
+
"gymnasium>=0.29.0",
|
|
30
|
+
"numpy>=1.24.0",
|
|
31
|
+
"pyyaml>=6.0",
|
|
32
|
+
]
|
|
33
|
+
|
|
34
|
+
[project.optional-dependencies]
|
|
35
|
+
dev = [
|
|
36
|
+
"pytest>=7.0",
|
|
37
|
+
"pytest-cov>=4.0",
|
|
38
|
+
]
|
|
39
|
+
marl = [
|
|
40
|
+
"marllib",
|
|
41
|
+
"ray[rllib]>=2.0",
|
|
42
|
+
]
|
|
43
|
+
|
|
44
|
+
[project.urls]
|
|
45
|
+
Homepage = "https://github.com/DujDDx/swmmEnv"
|
|
46
|
+
Documentation = "https://github.com/DujDDx/swmmEnv#readme"
|
|
47
|
+
Repository = "https://github.com/DujDDx/swmmEnv.git"
|
|
48
|
+
|
|
49
|
+
[tool.setuptools.packages.find]
|
|
50
|
+
where = ["."]
|
|
51
|
+
include = ["swmmEnv*"]
|