Skip to content

Model Library #509

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 3 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions MANIFEST.in
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,4 @@ include wntr/tests/networks_for_testing/*.inp
include wntr/library/msx/*.json
include wntr/library/msx/*.msx
include wntr/library/DemandPatternLibrary.json
include wntr/library/networks/*.inp
3 changes: 2 additions & 1 deletion wntr/library/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,5 @@
The wntr.library package contains classes to help define water network models
"""
from .demand_library import DemandPatternLibrary
from . import msx
from . import msx
from .model_library import ModelLibrary
127 changes: 127 additions & 0 deletions wntr/library/model_library.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
import os
import wntr
from wntr.network import WaterNetworkModel

class ModelLibrary:
"""
Model library class to manage water network models.
Parameters
----------
directory_path : str
Path to a directory containing INP files.
"""

def __init__(self, directory_path=None):
if directory_path is None:
this_file_path = os.path.dirname(os.path.abspath(__file__))
directory_path = os.path.join(this_file_path, "networks")
if not os.path.isdir(directory_path):
raise ValueError(f"Provided path '{directory_path}' is not a valid directory.")
self.directory_path = directory_path
self._build_model_paths()

def _build_model_paths(self):
# Scan directory recursively for INP files
self._model_paths = {}
for root, _, files in os.walk(self.directory_path):
for file in files:
if file.endswith('.inp'):
model_name = os.path.splitext(file)[0]
self._model_paths[model_name] = os.path.join(root, file)

@property
def model_name_list(self):
"""
Return a list of model names in the library.
Returns
-------
list of str
"""
return list(self._model_paths.keys())

def get_filepath(self, name):
"""
Get the file path of a model by its name.
Parameters
----------
name : str
Name of the model.
Returns
-------
str
File path of the model's INP file.
"""
if name not in self._model_paths:
raise KeyError(f"Model '{name}' not found in the library.")
return self._model_paths[name]

def get_model(self, name):
"""
Get a WaterNetworkModel by its name.
Parameters
----------
name : str
Name of the model.
Returns
-------
WaterNetworkModel
"""
if name not in self._model_paths:
raise KeyError(f"Model '{name}' not found in the library.")
return WaterNetworkModel(self._model_paths[name])

def add_model(self, name, wn_model):
"""
Add a new WaterNetworkModel to the library.
Parameters
----------
name : str
Name of the model.
wn_model : WaterNetworkModel
WaterNetworkModel object to add to the library.
"""
if name in self._model_paths:
raise KeyError(f"Model '{name}' already exists in the library.")
if not isinstance(wn_model, WaterNetworkModel):
raise TypeError("The provided model must be a WaterNetworkModel object.")

# Save the model to the directory
file_path = os.path.join(self.directory_path, f"{name}.inp")
wntr.network.io.write_inpfile(wn_model, file_path)
self._model_paths[name] = file_path
self._build_model_paths()

# def remove_model(self, name):
# """
# Remove a model from the library.
# Parameters
# ----------
# name : str
# Name of the model to remove.
# """
# if name not in self._model_paths:
# raise KeyError(f"Model '{name}' not found in the library.")
# os.remove(self._model_paths[name])
# del self._model_paths[name]

def copy_model(self, source_name, target_name):
"""
Copy an existing model to create a new model entry.
Parameters
----------
source_name : str
Name of the existing model to copy.
target_name : str
Name of the new model.
"""
if source_name not in self._model_paths:
raise KeyError(f"Source model '{source_name}' not found in the library.")
if target_name in self._model_paths:
raise KeyError(f"Target model '{target_name}' already exists in the library.")

# Load the source model
source_model = self.get_model(source_name)

# Add the copied model with the new name
self.add_model(target_name, source_model)
self._build_model_paths()
178 changes: 178 additions & 0 deletions wntr/library/networks/Net1.inp
Original file line number Diff line number Diff line change
@@ -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 Overflow
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.000 70.000
11 30.000 70.000
12 50.000 70.000
13 70.000 70.000
21 30.000 40.000
22 50.000 40.000
23 70.000 40.000
31 30.000 10.000
32 50.000 10.000
9 10.000 70.000
2 50.000 90.000

[VERTICES]
;Link X-Coord Y-Coord

[LABELS]
;X-Coord Y-Coord Label & Anchor Node
6.990 73.630 "Source"
13.480 68.130 "Pump"
43.850 91.210 "Tank"

[BACKDROP]
DIMENSIONS 7.000 6.000 73.000 94.000
UNITS None
FILE
OFFSET 0.00 0.00

[END]
Loading
Loading