watertrain 1.0.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.
- watertrain-1.0.0/PKG-INFO +136 -0
- watertrain-1.0.0/README.md +121 -0
- watertrain-1.0.0/pyproject.toml +29 -0
- watertrain-1.0.0/setup.cfg +4 -0
- watertrain-1.0.0/src/watertrain/__init__.py +42 -0
- watertrain-1.0.0/src/watertrain/cli.py +107 -0
- watertrain-1.0.0/src/watertrain/database.py +75 -0
- watertrain-1.0.0/src/watertrain/design.py +218 -0
- watertrain-1.0.0/src/watertrain/engine.py +180 -0
- watertrain-1.0.0/src/watertrain/examples.py +51 -0
- watertrain-1.0.0/src/watertrain/excel.py +88 -0
- watertrain-1.0.0/src/watertrain/model.py +131 -0
- watertrain-1.0.0/src/watertrain/sfiles.py +15 -0
- watertrain-1.0.0/src/watertrain.egg-info/PKG-INFO +136 -0
- watertrain-1.0.0/src/watertrain.egg-info/SOURCES.txt +18 -0
- watertrain-1.0.0/src/watertrain.egg-info/dependency_links.txt +1 -0
- watertrain-1.0.0/src/watertrain.egg-info/entry_points.txt +2 -0
- watertrain-1.0.0/src/watertrain.egg-info/requires.txt +3 -0
- watertrain-1.0.0/src/watertrain.egg-info/top_level.txt +1 -0
- watertrain-1.0.0/tests/test_parity.py +69 -0
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: watertrain
|
|
3
|
+
Version: 1.0.0
|
|
4
|
+
Summary: Automatic water-treatment-plant flowsheet generator: superstructure enumeration with pruning, analytical reliability and TAC ranking, plus equipment sizing.
|
|
5
|
+
Author: Anjan Tula
|
|
6
|
+
License: MIT
|
|
7
|
+
Keywords: water treatment,flowsheet,process synthesis,superstructure,reverse osmosis,desalination
|
|
8
|
+
Classifier: Programming Language :: Python :: 3
|
|
9
|
+
Classifier: Intended Audience :: Science/Research
|
|
10
|
+
Classifier: Topic :: Scientific/Engineering :: Chemistry
|
|
11
|
+
Requires-Python: >=3.9
|
|
12
|
+
Description-Content-Type: text/markdown
|
|
13
|
+
Provides-Extra: excel
|
|
14
|
+
Requires-Dist: openpyxl>=3.1; extra == "excel"
|
|
15
|
+
|
|
16
|
+
# watertrain — Automatic WTP Flowsheet Generator
|
|
17
|
+
|
|
18
|
+
Python port of the Excel/VBA tool `WTP_FlowsheetGenerator.xlsm`. Generates
|
|
19
|
+
every feasible water-treatment train for a given raw-water quality and
|
|
20
|
+
product spec, ranks them by a total-annualised-cost (TAC) screening index,
|
|
21
|
+
and sizes the equipment of any selected train.
|
|
22
|
+
|
|
23
|
+
**Method:** superstructure depth-first enumeration with pruning rules P1–P7,
|
|
24
|
+
backward flow / forward composition balance, analytical reliability
|
|
25
|
+
(log-normal approximation driven by the leak-CV column) and a TAC index.
|
|
26
|
+
The Python engine reproduces the VBA workbook results exactly (see
|
|
27
|
+
`tests/test_parity.py`).
|
|
28
|
+
|
|
29
|
+
## Install
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
pip install . # core (no dependencies, pure Python >= 3.9)
|
|
33
|
+
pip install .[excel] # + openpyxl, to read cases/DB from the workbook
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
## Quick start (Python API)
|
|
37
|
+
|
|
38
|
+
```python
|
|
39
|
+
from watertrain import WaterCase, generate, rank, design
|
|
40
|
+
|
|
41
|
+
case = WaterCase.create(
|
|
42
|
+
feed=dict(turbidity=10, tss=15, oil_grease=1, fe=0.3, mn=0.1,
|
|
43
|
+
cod=40, toc=10, tds=5384, sio2=30, hardness=193),
|
|
44
|
+
target=dict(turbidity=1, tss=1, oil_grease=0.1, fe=0.1, mn=0.05,
|
|
45
|
+
cod=10, toc=2, tds=100, sio2=5, hardness=20),
|
|
46
|
+
product_flow=19.3, # m3/h
|
|
47
|
+
source="Treated Wastewater/Sewage",
|
|
48
|
+
max_train_len=9, max_flowsheets=200)
|
|
49
|
+
|
|
50
|
+
flowsheets = generate(case) # 1. all feasible trains
|
|
51
|
+
best = rank(flowsheets, top_x=5) # 2. cheapest first (TAC)
|
|
52
|
+
d = design(best[0], case) # 3. size the chosen train
|
|
53
|
+
print(d.report()) # equipment schedule + PFD + stream table
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
The workflow mirrors the workbook macros:
|
|
57
|
+
`GenerateFlowsheets → RankFlowsheets → DesignSelected`.
|
|
58
|
+
|
|
59
|
+
Built-in example cases (same 7 as the workbook):
|
|
60
|
+
|
|
61
|
+
```python
|
|
62
|
+
from watertrain import get_example
|
|
63
|
+
case = get_example(6) # "6. PFD-1 reject reclaim"
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
## Command line
|
|
67
|
+
|
|
68
|
+
```bash
|
|
69
|
+
watertrain --list-examples
|
|
70
|
+
watertrain --example 6 --top 5 --design 1
|
|
71
|
+
watertrain --case mycase.json --top 10
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
`mycase.json`:
|
|
75
|
+
|
|
76
|
+
```json
|
|
77
|
+
{
|
|
78
|
+
"feed": {"turbidity": 10, "tss": 15, "tds": 5384, "hardness": 193},
|
|
79
|
+
"target": {"turbidity": 1, "tss": 1, "tds": 100, "hardness": 20},
|
|
80
|
+
"product_flow": 19.3,
|
|
81
|
+
"max_train_len": 9
|
|
82
|
+
}
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
Parameters omitted from `feed` default to 0 (not present).
|
|
86
|
+
|
|
87
|
+
## Custom technology database
|
|
88
|
+
|
|
89
|
+
The default database (`watertrain.DEFAULT_TECHNOLOGIES`) is the
|
|
90
|
+
`2.ProcessGroups` sheet. Pass your own list to `generate()`:
|
|
91
|
+
|
|
92
|
+
```python
|
|
93
|
+
from watertrain import Technology, generate
|
|
94
|
+
|
|
95
|
+
my_tech = Technology.create(
|
|
96
|
+
"MyMBR", stage=2, recovery=0.95, leak_cv=0.25, capex=2.0, opex=0.9,
|
|
97
|
+
removal=dict(turbidity=0.99, tss=0.999, cod=0.6, toc=0.5),
|
|
98
|
+
inlet_max=dict(turbidity=200))
|
|
99
|
+
|
|
100
|
+
techs = list(__import__("watertrain").DEFAULT_TECHNOLOGIES) + [my_tech]
|
|
101
|
+
flowsheets = generate(case, technologies=techs)
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
Or keep editing the database in Excel and load it live
|
|
105
|
+
(requires `pip install .[excel]`):
|
|
106
|
+
|
|
107
|
+
```python
|
|
108
|
+
from watertrain.excel import load_case, load_technologies
|
|
109
|
+
case = load_case("WTP_FlowsheetGenerator.xlsm")
|
|
110
|
+
techs = load_technologies("WTP_FlowsheetGenerator.xlsm")
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
## Water-quality parameters (fixed order)
|
|
114
|
+
|
|
115
|
+
`turbidity, tss, oil_grease, fe, mn, cod, toc, tds, sio2, hardness`
|
|
116
|
+
|
|
117
|
+
## Notes
|
|
118
|
+
|
|
119
|
+
* Reliability is P(product meets spec) under performance uncertainty.
|
|
120
|
+
* TAC is a screening cost index for ranking, not a bid price.
|
|
121
|
+
* Run the parity tests with `pytest`.
|
|
122
|
+
|
|
123
|
+
## Package layout
|
|
124
|
+
|
|
125
|
+
```
|
|
126
|
+
src/watertrain/
|
|
127
|
+
model.py dataclasses: Technology, WaterCase, Flowsheet; constants
|
|
128
|
+
database.py default technology database (2.ProcessGroups)
|
|
129
|
+
examples.py the 7 example cases
|
|
130
|
+
engine.py generate() + rank(): DFS, pruning P1–P7, reliability, TAC
|
|
131
|
+
design.py design(): flow balance, equipment sizing, streams, text PFD
|
|
132
|
+
sfiles.py SFILES string helpers
|
|
133
|
+
excel.py optional openpyxl bridge to the original workbook
|
|
134
|
+
cli.py `watertrain` command
|
|
135
|
+
tests/test_parity.py reproduces the workbook numbers exactly
|
|
136
|
+
```
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
# watertrain — Automatic WTP Flowsheet Generator
|
|
2
|
+
|
|
3
|
+
Python port of the Excel/VBA tool `WTP_FlowsheetGenerator.xlsm`. Generates
|
|
4
|
+
every feasible water-treatment train for a given raw-water quality and
|
|
5
|
+
product spec, ranks them by a total-annualised-cost (TAC) screening index,
|
|
6
|
+
and sizes the equipment of any selected train.
|
|
7
|
+
|
|
8
|
+
**Method:** superstructure depth-first enumeration with pruning rules P1–P7,
|
|
9
|
+
backward flow / forward composition balance, analytical reliability
|
|
10
|
+
(log-normal approximation driven by the leak-CV column) and a TAC index.
|
|
11
|
+
The Python engine reproduces the VBA workbook results exactly (see
|
|
12
|
+
`tests/test_parity.py`).
|
|
13
|
+
|
|
14
|
+
## Install
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
pip install . # core (no dependencies, pure Python >= 3.9)
|
|
18
|
+
pip install .[excel] # + openpyxl, to read cases/DB from the workbook
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
## Quick start (Python API)
|
|
22
|
+
|
|
23
|
+
```python
|
|
24
|
+
from watertrain import WaterCase, generate, rank, design
|
|
25
|
+
|
|
26
|
+
case = WaterCase.create(
|
|
27
|
+
feed=dict(turbidity=10, tss=15, oil_grease=1, fe=0.3, mn=0.1,
|
|
28
|
+
cod=40, toc=10, tds=5384, sio2=30, hardness=193),
|
|
29
|
+
target=dict(turbidity=1, tss=1, oil_grease=0.1, fe=0.1, mn=0.05,
|
|
30
|
+
cod=10, toc=2, tds=100, sio2=5, hardness=20),
|
|
31
|
+
product_flow=19.3, # m3/h
|
|
32
|
+
source="Treated Wastewater/Sewage",
|
|
33
|
+
max_train_len=9, max_flowsheets=200)
|
|
34
|
+
|
|
35
|
+
flowsheets = generate(case) # 1. all feasible trains
|
|
36
|
+
best = rank(flowsheets, top_x=5) # 2. cheapest first (TAC)
|
|
37
|
+
d = design(best[0], case) # 3. size the chosen train
|
|
38
|
+
print(d.report()) # equipment schedule + PFD + stream table
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
The workflow mirrors the workbook macros:
|
|
42
|
+
`GenerateFlowsheets → RankFlowsheets → DesignSelected`.
|
|
43
|
+
|
|
44
|
+
Built-in example cases (same 7 as the workbook):
|
|
45
|
+
|
|
46
|
+
```python
|
|
47
|
+
from watertrain import get_example
|
|
48
|
+
case = get_example(6) # "6. PFD-1 reject reclaim"
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
## Command line
|
|
52
|
+
|
|
53
|
+
```bash
|
|
54
|
+
watertrain --list-examples
|
|
55
|
+
watertrain --example 6 --top 5 --design 1
|
|
56
|
+
watertrain --case mycase.json --top 10
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
`mycase.json`:
|
|
60
|
+
|
|
61
|
+
```json
|
|
62
|
+
{
|
|
63
|
+
"feed": {"turbidity": 10, "tss": 15, "tds": 5384, "hardness": 193},
|
|
64
|
+
"target": {"turbidity": 1, "tss": 1, "tds": 100, "hardness": 20},
|
|
65
|
+
"product_flow": 19.3,
|
|
66
|
+
"max_train_len": 9
|
|
67
|
+
}
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
Parameters omitted from `feed` default to 0 (not present).
|
|
71
|
+
|
|
72
|
+
## Custom technology database
|
|
73
|
+
|
|
74
|
+
The default database (`watertrain.DEFAULT_TECHNOLOGIES`) is the
|
|
75
|
+
`2.ProcessGroups` sheet. Pass your own list to `generate()`:
|
|
76
|
+
|
|
77
|
+
```python
|
|
78
|
+
from watertrain import Technology, generate
|
|
79
|
+
|
|
80
|
+
my_tech = Technology.create(
|
|
81
|
+
"MyMBR", stage=2, recovery=0.95, leak_cv=0.25, capex=2.0, opex=0.9,
|
|
82
|
+
removal=dict(turbidity=0.99, tss=0.999, cod=0.6, toc=0.5),
|
|
83
|
+
inlet_max=dict(turbidity=200))
|
|
84
|
+
|
|
85
|
+
techs = list(__import__("watertrain").DEFAULT_TECHNOLOGIES) + [my_tech]
|
|
86
|
+
flowsheets = generate(case, technologies=techs)
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
Or keep editing the database in Excel and load it live
|
|
90
|
+
(requires `pip install .[excel]`):
|
|
91
|
+
|
|
92
|
+
```python
|
|
93
|
+
from watertrain.excel import load_case, load_technologies
|
|
94
|
+
case = load_case("WTP_FlowsheetGenerator.xlsm")
|
|
95
|
+
techs = load_technologies("WTP_FlowsheetGenerator.xlsm")
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
## Water-quality parameters (fixed order)
|
|
99
|
+
|
|
100
|
+
`turbidity, tss, oil_grease, fe, mn, cod, toc, tds, sio2, hardness`
|
|
101
|
+
|
|
102
|
+
## Notes
|
|
103
|
+
|
|
104
|
+
* Reliability is P(product meets spec) under performance uncertainty.
|
|
105
|
+
* TAC is a screening cost index for ranking, not a bid price.
|
|
106
|
+
* Run the parity tests with `pytest`.
|
|
107
|
+
|
|
108
|
+
## Package layout
|
|
109
|
+
|
|
110
|
+
```
|
|
111
|
+
src/watertrain/
|
|
112
|
+
model.py dataclasses: Technology, WaterCase, Flowsheet; constants
|
|
113
|
+
database.py default technology database (2.ProcessGroups)
|
|
114
|
+
examples.py the 7 example cases
|
|
115
|
+
engine.py generate() + rank(): DFS, pruning P1–P7, reliability, TAC
|
|
116
|
+
design.py design(): flow balance, equipment sizing, streams, text PFD
|
|
117
|
+
sfiles.py SFILES string helpers
|
|
118
|
+
excel.py optional openpyxl bridge to the original workbook
|
|
119
|
+
cli.py `watertrain` command
|
|
120
|
+
tests/test_parity.py reproduces the workbook numbers exactly
|
|
121
|
+
```
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=68"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "watertrain"
|
|
7
|
+
version = "1.0.0"
|
|
8
|
+
description = "Automatic water-treatment-plant flowsheet generator: superstructure enumeration with pruning, analytical reliability and TAC ranking, plus equipment sizing."
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.9"
|
|
11
|
+
license = { text = "MIT" }
|
|
12
|
+
authors = [{ name = "Anjan Tula" }]
|
|
13
|
+
keywords = ["water treatment", "flowsheet", "process synthesis",
|
|
14
|
+
"superstructure", "reverse osmosis", "desalination"]
|
|
15
|
+
classifiers = [
|
|
16
|
+
"Programming Language :: Python :: 3",
|
|
17
|
+
"Intended Audience :: Science/Research",
|
|
18
|
+
"Topic :: Scientific/Engineering :: Chemistry",
|
|
19
|
+
]
|
|
20
|
+
dependencies = [] # pure standard library
|
|
21
|
+
|
|
22
|
+
[project.optional-dependencies]
|
|
23
|
+
excel = ["openpyxl>=3.1"] # read cases / tech DB from the original workbook
|
|
24
|
+
|
|
25
|
+
[project.scripts]
|
|
26
|
+
watertrain = "watertrain.cli:main"
|
|
27
|
+
|
|
28
|
+
[tool.setuptools.packages.find]
|
|
29
|
+
where = ["src"]
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
"""watertrain — Automatic Water-Treatment-Plant Flowsheet Generator.
|
|
2
|
+
|
|
3
|
+
Python port of the validated Excel/VBA tool (WTP_FlowsheetGenerator.xlsm).
|
|
4
|
+
|
|
5
|
+
Typical use::
|
|
6
|
+
|
|
7
|
+
from watertrain import WaterCase, generate, rank, design
|
|
8
|
+
|
|
9
|
+
case = WaterCase.create(
|
|
10
|
+
feed=dict(turbidity=10, tss=15, oil_grease=1, fe=0.3, mn=0.1,
|
|
11
|
+
cod=40, toc=10, tds=5384, sio2=30, hardness=193),
|
|
12
|
+
target=dict(turbidity=1, tss=1, oil_grease=0.1, fe=0.1, mn=0.05,
|
|
13
|
+
cod=10, toc=2, tds=100, sio2=5, hardness=20),
|
|
14
|
+
product_flow=19.3, source="Treated Wastewater/Sewage",
|
|
15
|
+
max_train_len=9, max_flowsheets=10)
|
|
16
|
+
|
|
17
|
+
flowsheets = generate(case) # every feasible train
|
|
18
|
+
best = rank(flowsheets, top_x=5) # cheapest first (TAC)
|
|
19
|
+
print(design(best[0], case).report())
|
|
20
|
+
|
|
21
|
+
Workflow: generate -> rank -> design (same as the workbook macros
|
|
22
|
+
GenerateFlowsheets -> RankFlowsheets -> DesignSelected).
|
|
23
|
+
"""
|
|
24
|
+
from .model import (NO_LIMIT, NP, PARAMS, Flowsheet, Technology, WaterCase)
|
|
25
|
+
from .database import DEFAULT_TECHNOLOGIES, TECH_BY_NAME
|
|
26
|
+
from .engine import generate, rank
|
|
27
|
+
from .design import Design, Equipment, Stream, design
|
|
28
|
+
from .examples import EXAMPLES, get_example
|
|
29
|
+
from .sfiles import sfiles_string, sfiles_tag
|
|
30
|
+
|
|
31
|
+
__version__ = "1.0.0"
|
|
32
|
+
|
|
33
|
+
__all__ = [
|
|
34
|
+
"PARAMS", "NP", "NO_LIMIT",
|
|
35
|
+
"Technology", "WaterCase", "Flowsheet",
|
|
36
|
+
"DEFAULT_TECHNOLOGIES", "TECH_BY_NAME",
|
|
37
|
+
"generate", "rank", "design",
|
|
38
|
+
"Design", "Equipment", "Stream",
|
|
39
|
+
"EXAMPLES", "get_example",
|
|
40
|
+
"sfiles_string", "sfiles_tag",
|
|
41
|
+
"__version__",
|
|
42
|
+
]
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
"""Command-line interface.
|
|
2
|
+
|
|
3
|
+
Examples::
|
|
4
|
+
|
|
5
|
+
watertrain --list-examples
|
|
6
|
+
watertrain --example 6 --top 5
|
|
7
|
+
watertrain --example 6 --top 5 --design 1
|
|
8
|
+
watertrain --case mycase.json --top 10 --design 2
|
|
9
|
+
|
|
10
|
+
mycase.json format::
|
|
11
|
+
|
|
12
|
+
{
|
|
13
|
+
"feed": {"turbidity": 10, "tss": 15, "tds": 5384, "...": 0},
|
|
14
|
+
"target": {"turbidity": 1, "tss": 1, "tds": 100, "...": 0},
|
|
15
|
+
"product_flow": 19.3,
|
|
16
|
+
"source": "Treated Wastewater/Sewage",
|
|
17
|
+
"max_train_len": 9,
|
|
18
|
+
"max_flowsheets": 200
|
|
19
|
+
}
|
|
20
|
+
"""
|
|
21
|
+
from __future__ import annotations
|
|
22
|
+
|
|
23
|
+
import argparse
|
|
24
|
+
import json
|
|
25
|
+
import sys
|
|
26
|
+
|
|
27
|
+
from . import __version__
|
|
28
|
+
from .design import design as design_fs
|
|
29
|
+
from .engine import generate, rank
|
|
30
|
+
from .examples import EXAMPLES, get_example
|
|
31
|
+
from .model import WaterCase
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def _load_case_json(path: str) -> WaterCase:
|
|
35
|
+
with open(path, "r", encoding="utf-8") as fh:
|
|
36
|
+
d = json.load(fh)
|
|
37
|
+
return WaterCase.create(
|
|
38
|
+
feed=d["feed"], target=d["target"],
|
|
39
|
+
product_flow=d["product_flow"], source=d.get("source", ""),
|
|
40
|
+
max_train_len=d.get("max_train_len", 7),
|
|
41
|
+
max_flowsheets=d.get("max_flowsheets", 200),
|
|
42
|
+
name=d.get("name", path))
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def main(argv=None) -> int:
|
|
46
|
+
ap = argparse.ArgumentParser(
|
|
47
|
+
prog="watertrain",
|
|
48
|
+
description="Automatic WTP flowsheet generator "
|
|
49
|
+
"(generate -> rank -> design).")
|
|
50
|
+
ap.add_argument("--version", action="version", version=__version__)
|
|
51
|
+
ap.add_argument("--list-examples", action="store_true",
|
|
52
|
+
help="list the built-in example cases and exit")
|
|
53
|
+
src = ap.add_mutually_exclusive_group()
|
|
54
|
+
src.add_argument("--example", metavar="N",
|
|
55
|
+
help="use built-in example (1-7 or full name)")
|
|
56
|
+
src.add_argument("--case", metavar="FILE.json",
|
|
57
|
+
help="load a case from a JSON file")
|
|
58
|
+
ap.add_argument("--top", type=int, default=10, metavar="X",
|
|
59
|
+
help="rank: keep the X cheapest trains (default 10)")
|
|
60
|
+
ap.add_argument("--design", type=int, metavar="RANK",
|
|
61
|
+
help="print the full design of the given rank (1 = best)")
|
|
62
|
+
ap.add_argument("--max-flowsheets", type=int, metavar="M",
|
|
63
|
+
help="override the enumeration cap")
|
|
64
|
+
args = ap.parse_args(argv)
|
|
65
|
+
|
|
66
|
+
if args.list_examples:
|
|
67
|
+
for name in EXAMPLES:
|
|
68
|
+
print(name)
|
|
69
|
+
return 0
|
|
70
|
+
|
|
71
|
+
if args.example:
|
|
72
|
+
case = get_example(args.example)
|
|
73
|
+
elif args.case:
|
|
74
|
+
case = _load_case_json(args.case)
|
|
75
|
+
else:
|
|
76
|
+
ap.error("choose --example N or --case FILE.json "
|
|
77
|
+
"(or --list-examples)")
|
|
78
|
+
|
|
79
|
+
if args.max_flowsheets:
|
|
80
|
+
case = WaterCase.create(
|
|
81
|
+
feed=case.feed_dict(), target=case.target_dict(),
|
|
82
|
+
product_flow=case.product_flow, source=case.source,
|
|
83
|
+
max_train_len=case.max_train_len,
|
|
84
|
+
max_flowsheets=args.max_flowsheets, name=case.name)
|
|
85
|
+
|
|
86
|
+
fss = generate(case)
|
|
87
|
+
print(f"Case: {case.name or '(custom)'} | "
|
|
88
|
+
f"generated {len(fss)} feasible flowsheet(s)")
|
|
89
|
+
top = rank(fss, top_x=args.top)
|
|
90
|
+
print(f"\nTop {len(top)} by TAC:")
|
|
91
|
+
print(f"{'#':>3} {'TAC':>8} {'Rec%':>6} {'Rel%':>6} {'Intake':>8} SFILES")
|
|
92
|
+
for r, fs in enumerate(top, 1):
|
|
93
|
+
print(f"{r:>3} {fs.tac:8.3f} {fs.recovery_pct:6.1f} "
|
|
94
|
+
f"{fs.reliability_pct:6.1f} {fs.intake:8.2f} {fs.sfiles}")
|
|
95
|
+
|
|
96
|
+
if args.design:
|
|
97
|
+
if not (1 <= args.design <= len(top)):
|
|
98
|
+
print(f"\n--design {args.design} is out of range (1..{len(top)})",
|
|
99
|
+
file=sys.stderr)
|
|
100
|
+
return 2
|
|
101
|
+
print("\n" + "=" * 70)
|
|
102
|
+
print(design_fs(top[args.design - 1], case).report())
|
|
103
|
+
return 0
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
if __name__ == "__main__":
|
|
107
|
+
raise SystemExit(main())
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
"""Default technology database.
|
|
2
|
+
|
|
3
|
+
Ported 1:1 from sheet '2.ProcessGroups' of WTP_FlowsheetGenerator.xlsm.
|
|
4
|
+
rem_* = removal fraction (0-1); in_* = max inlet the unit tolerates
|
|
5
|
+
(absent = no limit). Stage: 1 pre-treatment, 2 filtration, 3 RO, 4 polish.
|
|
6
|
+
|
|
7
|
+
Users can pass their own list of Technology objects to generate(); this
|
|
8
|
+
module is only the built-in default.
|
|
9
|
+
"""
|
|
10
|
+
from .model import Technology
|
|
11
|
+
|
|
12
|
+
T = Technology.create
|
|
13
|
+
|
|
14
|
+
DEFAULT_TECHNOLOGIES = [
|
|
15
|
+
T("Clarifier", 1, 0.991, 0.35, 1.6, 0.5,
|
|
16
|
+
removal=dict(turbidity=0.98, tss=0.97, oil_grease=0.9, fe=0.8, mn=0.6,
|
|
17
|
+
cod=0.4, toc=0.35, tds=0.0, sio2=0.1, hardness=0.0)),
|
|
18
|
+
T("DAF", 1, 0.99, 0.35, 1.7, 0.8,
|
|
19
|
+
removal=dict(turbidity=0.9, tss=0.92, oil_grease=0.95,
|
|
20
|
+
cod=0.35, toc=0.25)),
|
|
21
|
+
T("HRSCC", 1, 0.985, 0.35, 2.2, 1.0,
|
|
22
|
+
removal=dict(turbidity=0.985, tss=0.98, fe=0.85, mn=0.7,
|
|
23
|
+
cod=0.45, toc=0.4, sio2=0.3, hardness=0.6)),
|
|
24
|
+
T("ABF", 2, 0.992, 0.30, 0.8, 0.2,
|
|
25
|
+
removal=dict(turbidity=0.6, tss=0.7, fe=0.4, mn=0.3),
|
|
26
|
+
inlet_max=dict(turbidity=50, tss=50)),
|
|
27
|
+
T("MMF", 2, 0.97, 0.30, 1.0, 0.3,
|
|
28
|
+
removal=dict(turbidity=0.8, tss=0.85, oil_grease=0.3, fe=0.5, mn=0.4,
|
|
29
|
+
cod=0.05, toc=0.05),
|
|
30
|
+
inlet_max=dict(turbidity=50, tss=50)),
|
|
31
|
+
T("GSF", 2, 0.973, 0.30, 1.1, 0.35,
|
|
32
|
+
removal=dict(turbidity=0.9, tss=0.92, oil_grease=0.3, fe=0.85, mn=0.8,
|
|
33
|
+
cod=0.05, toc=0.05),
|
|
34
|
+
inlet_max=dict(turbidity=50, tss=50)),
|
|
35
|
+
T("ACF", 2, 0.93, 0.35, 1.2, 0.6,
|
|
36
|
+
removal=dict(turbidity=0.4, tss=0.5, oil_grease=0.85,
|
|
37
|
+
cod=0.55, toc=0.65),
|
|
38
|
+
inlet_max=dict(turbidity=10, tss=10)),
|
|
39
|
+
T("SF", 2, 0.97, 0.30, 0.9, 0.25,
|
|
40
|
+
removal=dict(turbidity=0.8, tss=0.85),
|
|
41
|
+
inlet_max=dict(turbidity=20, tss=20)),
|
|
42
|
+
T("WAC", 2, 0.975, 0.20, 1.3, 0.9,
|
|
43
|
+
removal=dict(tds=0.05, hardness=0.98),
|
|
44
|
+
inlet_max=dict(turbidity=5, tss=5)),
|
|
45
|
+
T("UF", 2, 0.90, 0.20, 1.8, 0.7,
|
|
46
|
+
removal=dict(turbidity=0.995, tss=0.999, oil_grease=0.7, fe=0.6, mn=0.3,
|
|
47
|
+
cod=0.25, toc=0.2),
|
|
48
|
+
inlet_max=dict(turbidity=100, tss=100, oil_grease=5)),
|
|
49
|
+
T("RO Pass1", 3, 0.75, 0.35, 3.0, 2.0,
|
|
50
|
+
removal=dict(turbidity=0.95, tss=0.99, oil_grease=0.9, fe=0.98, mn=0.98,
|
|
51
|
+
cod=0.9, toc=0.95, tds=0.92, sio2=0.9, hardness=0.94),
|
|
52
|
+
inlet_max=dict(turbidity=1, tss=1, oil_grease=0.1, fe=0.3, mn=0.1,
|
|
53
|
+
tds=10000)),
|
|
54
|
+
T("SWRO", 3, 0.45, 0.30, 5.0, 4.5,
|
|
55
|
+
removal=dict(turbidity=0.95, tss=0.99, oil_grease=0.9, fe=0.98, mn=0.98,
|
|
56
|
+
cod=0.9, toc=0.95, tds=0.991, sio2=0.99, hardness=0.995),
|
|
57
|
+
inlet_max=dict(turbidity=1, tss=1, oil_grease=0.1, tds=50000)),
|
|
58
|
+
T("RO Pass2", 3, 0.85, 0.30, 2.2, 1.2,
|
|
59
|
+
removal=dict(cod=0.85, toc=0.9, tds=0.95, sio2=0.95, hardness=0.97),
|
|
60
|
+
inlet_max=dict(turbidity=0.5, tss=0.5, tds=2000)),
|
|
61
|
+
T("EDI", 4, 0.90, 0.25, 2.5, 1.5,
|
|
62
|
+
removal=dict(toc=0.5, tds=0.99, sio2=0.95, hardness=0.999),
|
|
63
|
+
inlet_max=dict(turbidity=0.2, fe=0.01, mn=0.01, toc=0.5,
|
|
64
|
+
tds=50, sio2=1, hardness=1)),
|
|
65
|
+
T("NRMB", 4, 1.0, 0.25, 1.4, 2.5,
|
|
66
|
+
removal=dict(tds=0.99, sio2=0.98, hardness=0.999),
|
|
67
|
+
inlet_max=dict(turbidity=0.2, tds=20)),
|
|
68
|
+
T("GUARD", 4, 1.0, 0.05, 0.3, 0.2,
|
|
69
|
+
removal=dict(turbidity=0.95, tss=0.95, fe=0.3)),
|
|
70
|
+
T("UV", 4, 1.0, 0.40, 0.6, 0.4,
|
|
71
|
+
removal=dict(cod=0.1, toc=0.3),
|
|
72
|
+
inlet_max=dict(turbidity=5)),
|
|
73
|
+
]
|
|
74
|
+
|
|
75
|
+
TECH_BY_NAME = {t.name: t for t in DEFAULT_TECHNOLOGIES}
|