processforge 0.2.14__tar.gz → 0.2.15__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.
- {processforge-0.2.14/src/processforge.egg-info → processforge-0.2.15}/PKG-INFO +53 -4
- {processforge-0.2.14 → processforge-0.2.15}/README.md +52 -3
- {processforge-0.2.14 → processforge-0.2.15}/flowsheets/closed-loop-chain.json +0 -4
- {processforge-0.2.14 → processforge-0.2.15}/flowsheets/hydraulic-chain.json +39 -13
- {processforge-0.2.14 → processforge-0.2.15}/pyproject.toml +1 -1
- {processforge-0.2.14 → processforge-0.2.15}/src/processforge/utils/validate_flowsheet.py +53 -0
- {processforge-0.2.14 → processforge-0.2.15/src/processforge.egg-info}/PKG-INFO +53 -4
- {processforge-0.2.14 → processforge-0.2.15}/LICENSE +0 -0
- {processforge-0.2.14 → processforge-0.2.15}/MANIFEST.in +0 -0
- {processforge-0.2.14 → processforge-0.2.15}/flowsheets/archive/example_dynamic_hybrid.json +0 -0
- {processforge-0.2.14 → processforge-0.2.15}/flowsheets/archive/example_dynamic_tank.json +0 -0
- {processforge-0.2.14 → processforge-0.2.15}/flowsheets/archive/example_flash.json +0 -0
- {processforge-0.2.14 → processforge-0.2.15}/flowsheets/archive/hydraulic_chain.json +0 -0
- {processforge-0.2.14 → processforge-0.2.15}/setup.cfg +0 -0
- {processforge-0.2.14 → processforge-0.2.15}/src/processforge/__init__.py +0 -0
- {processforge-0.2.14 → processforge-0.2.15}/src/processforge/_schema.py +0 -0
- {processforge-0.2.14 → processforge-0.2.15}/src/processforge/eo/__init__.py +0 -0
- {processforge-0.2.14 → processforge-0.2.15}/src/processforge/eo/backends/__init__.py +0 -0
- {processforge-0.2.14 → processforge-0.2.15}/src/processforge/eo/backends/base.py +0 -0
- {processforge-0.2.14 → processforge-0.2.15}/src/processforge/eo/backends/casadi_backend.py +0 -0
- {processforge-0.2.14 → processforge-0.2.15}/src/processforge/eo/backends/pyomo_backend.py +0 -0
- {processforge-0.2.14 → processforge-0.2.15}/src/processforge/eo/backends/scipy_backend.py +0 -0
- {processforge-0.2.14 → processforge-0.2.15}/src/processforge/eo/flowsheet.py +0 -0
- {processforge-0.2.14 → processforge-0.2.15}/src/processforge/eo/jacobian.py +0 -0
- {processforge-0.2.14 → processforge-0.2.15}/src/processforge/eo/mixin.py +0 -0
- {processforge-0.2.14 → processforge-0.2.15}/src/processforge/eo/solver.py +0 -0
- {processforge-0.2.14 → processforge-0.2.15}/src/processforge/eo/stream_var.py +0 -0
- {processforge-0.2.14 → processforge-0.2.15}/src/processforge/eo/units/__init__.py +0 -0
- {processforge-0.2.14 → processforge-0.2.15}/src/processforge/eo/units/flash_eo.py +0 -0
- {processforge-0.2.14 → processforge-0.2.15}/src/processforge/eo/units/heater_eo.py +0 -0
- {processforge-0.2.14 → processforge-0.2.15}/src/processforge/eo/units/pipes_eo.py +0 -0
- {processforge-0.2.14 → processforge-0.2.15}/src/processforge/eo/units/pump_eo.py +0 -0
- {processforge-0.2.14 → processforge-0.2.15}/src/processforge/eo/units/strainer_eo.py +0 -0
- {processforge-0.2.14 → processforge-0.2.15}/src/processforge/eo/units/valve_eo.py +0 -0
- {processforge-0.2.14 → processforge-0.2.15}/src/processforge/flowsheet.py +0 -0
- {processforge-0.2.14 → processforge-0.2.15}/src/processforge/fmu/__init__.py +0 -0
- {processforge-0.2.14 → processforge-0.2.15}/src/processforge/fmu/_fmi_vars.py +0 -0
- {processforge-0.2.14 → processforge-0.2.15}/src/processforge/fmu/builder.py +0 -0
- {processforge-0.2.14 → processforge-0.2.15}/src/processforge/fmu/slave_template.py +0 -0
- {processforge-0.2.14 → processforge-0.2.15}/src/processforge/modelica/__init__.py +0 -0
- {processforge-0.2.14 → processforge-0.2.15}/src/processforge/modelica/mo_writer.py +0 -0
- {processforge-0.2.14 → processforge-0.2.15}/src/processforge/modelica/omc_runner.py +0 -0
- {processforge-0.2.14 → processforge-0.2.15}/src/processforge/modelica/transpiler.py +0 -0
- {processforge-0.2.14 → processforge-0.2.15}/src/processforge/modelica/unit_equations.py +0 -0
- {processforge-0.2.14 → processforge-0.2.15}/src/processforge/provenance.py +0 -0
- {processforge-0.2.14 → processforge-0.2.15}/src/processforge/result.py +0 -0
- {processforge-0.2.14 → processforge-0.2.15}/src/processforge/schemas/__init__.py +0 -0
- {processforge-0.2.14 → processforge-0.2.15}/src/processforge/schemas/flowsheet_schema.json +0 -0
- {processforge-0.2.14 → processforge-0.2.15}/src/processforge/simulate.py +0 -0
- {processforge-0.2.14 → processforge-0.2.15}/src/processforge/solver.py +0 -0
- {processforge-0.2.14 → processforge-0.2.15}/src/processforge/thermo.py +0 -0
- {processforge-0.2.14 → processforge-0.2.15}/src/processforge/units/__init__.py +0 -0
- {processforge-0.2.14 → processforge-0.2.15}/src/processforge/units/flash.py +0 -0
- {processforge-0.2.14 → processforge-0.2.15}/src/processforge/units/heater.py +0 -0
- {processforge-0.2.14 → processforge-0.2.15}/src/processforge/units/pipes.py +0 -0
- {processforge-0.2.14 → processforge-0.2.15}/src/processforge/units/pump.py +0 -0
- {processforge-0.2.14 → processforge-0.2.15}/src/processforge/units/solver.py +0 -0
- {processforge-0.2.14 → processforge-0.2.15}/src/processforge/units/strainer.py +0 -0
- {processforge-0.2.14 → processforge-0.2.15}/src/processforge/units/tank.py +0 -0
- {processforge-0.2.14 → processforge-0.2.15}/src/processforge/units/valve.py +0 -0
- {processforge-0.2.14 → processforge-0.2.15}/src/processforge/utils/__init__.py +0 -0
- {processforge-0.2.14 → processforge-0.2.15}/src/processforge/utils/flowsheet_diagram.py +0 -0
- {processforge-0.2.14 → processforge-0.2.15}/src/processforge/utils/validation.py +0 -0
- {processforge-0.2.14 → processforge-0.2.15}/src/processforge/validate.py +0 -0
- {processforge-0.2.14 → processforge-0.2.15}/src/processforge.egg-info/SOURCES.txt +0 -0
- {processforge-0.2.14 → processforge-0.2.15}/src/processforge.egg-info/dependency_links.txt +0 -0
- {processforge-0.2.14 → processforge-0.2.15}/src/processforge.egg-info/entry_points.txt +0 -0
- {processforge-0.2.14 → processforge-0.2.15}/src/processforge.egg-info/requires.txt +0 -0
- {processforge-0.2.14 → processforge-0.2.15}/src/processforge.egg-info/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: processforge
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.15
|
|
4
4
|
Summary: A Python-based process simulation framework for chemical engineering applications.
|
|
5
5
|
Author-email: Process Forge Team <team@processforge.dev>
|
|
6
6
|
License-Expression: BSD-3-Clause
|
|
@@ -223,12 +223,27 @@ Flowsheets are defined as JSON files. The `simulation.mode` field controls which
|
|
|
223
223
|
```json
|
|
224
224
|
{
|
|
225
225
|
"metadata": { "name": "My Flowsheet", "version": "2.0" },
|
|
226
|
+
"materials": {
|
|
227
|
+
"Water": { "friendly_material_id": 1 },
|
|
228
|
+
"Toluene": { "friendly_material_id": 2 },
|
|
229
|
+
"Steel": { "friendly_material_id": 3 }
|
|
230
|
+
},
|
|
231
|
+
"material_mixes": {
|
|
232
|
+
"Water_Toluene_Mix": {
|
|
233
|
+
"friendly_material_mix_id": 1,
|
|
234
|
+
"percent_type": "ao",
|
|
235
|
+
"components": [
|
|
236
|
+
{ "name": "Water", "fraction": 0.8 },
|
|
237
|
+
{ "name": "Toluene", "fraction": 0.2 }
|
|
238
|
+
]
|
|
239
|
+
}
|
|
240
|
+
},
|
|
226
241
|
"streams": {
|
|
227
|
-
"feed": { "T": 298.15, "P": 101325, "flowrate": 1.0, "
|
|
242
|
+
"feed": { "T": 298.15, "P": 101325, "flowrate": 1.0, "material_mix": 1 }
|
|
228
243
|
},
|
|
229
244
|
"units": {
|
|
230
|
-
"pump_1":
|
|
231
|
-
"valve_1": { "type": "Valve", "in": "after_pump",
|
|
245
|
+
"pump_1": { "type": "Pump", "in": "feed", "out": "after_pump", "deltaP": 200000, "efficiency": 0.75, "material": 3 },
|
|
246
|
+
"valve_1": { "type": "Valve", "in": "after_pump", "out": "product", "pressure_ratio": 0.5, "material": 3 }
|
|
232
247
|
},
|
|
233
248
|
"simulation": {
|
|
234
249
|
"mode": "steady",
|
|
@@ -239,6 +254,40 @@ Flowsheets are defined as JSON files. The `simulation.mode` field controls which
|
|
|
239
254
|
|
|
240
255
|
The optional `backend` key selects the EO solver backend (`"scipy"`, `"pyomo"`, or `"casadi"`). Defaults to `"scipy"`.
|
|
241
256
|
|
|
257
|
+
### Materials and composition
|
|
258
|
+
|
|
259
|
+
Stream composition can be defined in two ways:
|
|
260
|
+
|
|
261
|
+
**Explicit `z` dict** — list component mole fractions directly on the stream:
|
|
262
|
+
```json
|
|
263
|
+
"feed": { "T": 298.15, "P": 101325, "flowrate": 1.0, "z": { "Water": 0.8, "Toluene": 0.2 } }
|
|
264
|
+
```
|
|
265
|
+
|
|
266
|
+
**`material_mix` reference** — define a reusable mix in the top-level `material_mixes` section and reference it by `friendly_material_mix_id`. The validator automatically expands the reference into a `z` dict before simulation:
|
|
267
|
+
```json
|
|
268
|
+
"material_mixes": {
|
|
269
|
+
"Water_Toluene_Mix": {
|
|
270
|
+
"friendly_material_mix_id": 1,
|
|
271
|
+
"percent_type": "ao",
|
|
272
|
+
"components": [
|
|
273
|
+
{ "name": "Water", "fraction": 0.8 },
|
|
274
|
+
{ "name": "Toluene", "fraction": 0.2 }
|
|
275
|
+
]
|
|
276
|
+
}
|
|
277
|
+
},
|
|
278
|
+
"streams": {
|
|
279
|
+
"feed": { "T": 298.15, "P": 101325, "flowrate": 1.0, "material_mix": 1 }
|
|
280
|
+
}
|
|
281
|
+
```
|
|
282
|
+
|
|
283
|
+
Rules:
|
|
284
|
+
- `z` and `material_mix` are mutually exclusive on a single stream.
|
|
285
|
+
- `friendly_material_mix_id` values must be unique across all mixes.
|
|
286
|
+
- Each component `name` in a mix must match a key in the top-level `materials` section.
|
|
287
|
+
- When all component fractions are provided they must sum to 1.0.
|
|
288
|
+
|
|
289
|
+
Every unit also requires a `material` integer field pointing to a `friendly_material_id` in the `materials` section (this identifies the structural material the unit is made of, separate from the fluid composition).
|
|
290
|
+
|
|
242
291
|
### Recycle streams
|
|
243
292
|
|
|
244
293
|
Recycle streams require no special configuration. Any stream produced as the `out` of one unit can be used as the `in` of any other unit — including upstream units. The EO solver resolves the full coupled system simultaneously.
|
|
@@ -172,12 +172,27 @@ Flowsheets are defined as JSON files. The `simulation.mode` field controls which
|
|
|
172
172
|
```json
|
|
173
173
|
{
|
|
174
174
|
"metadata": { "name": "My Flowsheet", "version": "2.0" },
|
|
175
|
+
"materials": {
|
|
176
|
+
"Water": { "friendly_material_id": 1 },
|
|
177
|
+
"Toluene": { "friendly_material_id": 2 },
|
|
178
|
+
"Steel": { "friendly_material_id": 3 }
|
|
179
|
+
},
|
|
180
|
+
"material_mixes": {
|
|
181
|
+
"Water_Toluene_Mix": {
|
|
182
|
+
"friendly_material_mix_id": 1,
|
|
183
|
+
"percent_type": "ao",
|
|
184
|
+
"components": [
|
|
185
|
+
{ "name": "Water", "fraction": 0.8 },
|
|
186
|
+
{ "name": "Toluene", "fraction": 0.2 }
|
|
187
|
+
]
|
|
188
|
+
}
|
|
189
|
+
},
|
|
175
190
|
"streams": {
|
|
176
|
-
"feed": { "T": 298.15, "P": 101325, "flowrate": 1.0, "
|
|
191
|
+
"feed": { "T": 298.15, "P": 101325, "flowrate": 1.0, "material_mix": 1 }
|
|
177
192
|
},
|
|
178
193
|
"units": {
|
|
179
|
-
"pump_1":
|
|
180
|
-
"valve_1": { "type": "Valve", "in": "after_pump",
|
|
194
|
+
"pump_1": { "type": "Pump", "in": "feed", "out": "after_pump", "deltaP": 200000, "efficiency": 0.75, "material": 3 },
|
|
195
|
+
"valve_1": { "type": "Valve", "in": "after_pump", "out": "product", "pressure_ratio": 0.5, "material": 3 }
|
|
181
196
|
},
|
|
182
197
|
"simulation": {
|
|
183
198
|
"mode": "steady",
|
|
@@ -188,6 +203,40 @@ Flowsheets are defined as JSON files. The `simulation.mode` field controls which
|
|
|
188
203
|
|
|
189
204
|
The optional `backend` key selects the EO solver backend (`"scipy"`, `"pyomo"`, or `"casadi"`). Defaults to `"scipy"`.
|
|
190
205
|
|
|
206
|
+
### Materials and composition
|
|
207
|
+
|
|
208
|
+
Stream composition can be defined in two ways:
|
|
209
|
+
|
|
210
|
+
**Explicit `z` dict** — list component mole fractions directly on the stream:
|
|
211
|
+
```json
|
|
212
|
+
"feed": { "T": 298.15, "P": 101325, "flowrate": 1.0, "z": { "Water": 0.8, "Toluene": 0.2 } }
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
**`material_mix` reference** — define a reusable mix in the top-level `material_mixes` section and reference it by `friendly_material_mix_id`. The validator automatically expands the reference into a `z` dict before simulation:
|
|
216
|
+
```json
|
|
217
|
+
"material_mixes": {
|
|
218
|
+
"Water_Toluene_Mix": {
|
|
219
|
+
"friendly_material_mix_id": 1,
|
|
220
|
+
"percent_type": "ao",
|
|
221
|
+
"components": [
|
|
222
|
+
{ "name": "Water", "fraction": 0.8 },
|
|
223
|
+
{ "name": "Toluene", "fraction": 0.2 }
|
|
224
|
+
]
|
|
225
|
+
}
|
|
226
|
+
},
|
|
227
|
+
"streams": {
|
|
228
|
+
"feed": { "T": 298.15, "P": 101325, "flowrate": 1.0, "material_mix": 1 }
|
|
229
|
+
}
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
Rules:
|
|
233
|
+
- `z` and `material_mix` are mutually exclusive on a single stream.
|
|
234
|
+
- `friendly_material_mix_id` values must be unique across all mixes.
|
|
235
|
+
- Each component `name` in a mix must match a key in the top-level `materials` section.
|
|
236
|
+
- When all component fractions are provided they must sum to 1.0.
|
|
237
|
+
|
|
238
|
+
Every unit also requires a `material` integer field pointing to a `friendly_material_id` in the `materials` section (this identifies the structural material the unit is made of, separate from the fluid composition).
|
|
239
|
+
|
|
191
240
|
### Recycle streams
|
|
192
241
|
|
|
193
242
|
Recycle streams require no special configuration. Any stream produced as the `out` of one unit can be used as the `in` of any other unit — including upstream units. The EO solver resolves the full coupled system simultaneously.
|
|
@@ -4,15 +4,33 @@
|
|
|
4
4
|
"description": "A two-pump, one-valve, one-strainer steady-state hydraulic system.",
|
|
5
5
|
"version": "2.0"
|
|
6
6
|
},
|
|
7
|
+
"materials": {
|
|
8
|
+
"Water": {
|
|
9
|
+
"friendly_material_id": 1
|
|
10
|
+
},
|
|
11
|
+
"Toluene": {
|
|
12
|
+
"friendly_material_id": 2
|
|
13
|
+
},
|
|
14
|
+
"Steel": {
|
|
15
|
+
"friendly_material_id": 3
|
|
16
|
+
}
|
|
17
|
+
},
|
|
18
|
+
"material_mixes": {
|
|
19
|
+
"Water_Toluene_Mix": {
|
|
20
|
+
"friendly_material_mix_id": 1,
|
|
21
|
+
"percent_type": "ao",
|
|
22
|
+
"components": [
|
|
23
|
+
{"name": "Water", "fraction": 0.8},
|
|
24
|
+
{"name": "Toluene", "fraction": 0.2}
|
|
25
|
+
]
|
|
26
|
+
}
|
|
27
|
+
},
|
|
7
28
|
"streams": {
|
|
8
29
|
"feed": {
|
|
9
30
|
"T": 298.15,
|
|
10
31
|
"P": 101325,
|
|
11
|
-
"
|
|
12
|
-
|
|
13
|
-
"Toluene": 0.2
|
|
14
|
-
},
|
|
15
|
-
"flowrate": 1.0
|
|
32
|
+
"flowrate": 1.0,
|
|
33
|
+
"material_mix": 1
|
|
16
34
|
}
|
|
17
35
|
},
|
|
18
36
|
"units": {
|
|
@@ -21,54 +39,62 @@
|
|
|
21
39
|
"in": "feed",
|
|
22
40
|
"out": "after_pump_1",
|
|
23
41
|
"deltaP": 200000.0,
|
|
24
|
-
"efficiency": 0.75
|
|
42
|
+
"efficiency": 0.75,
|
|
43
|
+
"material": 3
|
|
25
44
|
},
|
|
26
45
|
"pipe_1": {
|
|
27
46
|
"type": "Pipes",
|
|
28
47
|
"in": "after_pump_1",
|
|
29
48
|
"out": "after_pipe_1",
|
|
30
49
|
"delta_p": 1000.0,
|
|
31
|
-
"diameter": 0.05
|
|
50
|
+
"diameter": 0.05,
|
|
51
|
+
"material": 3
|
|
32
52
|
},
|
|
33
53
|
"valve_1": {
|
|
34
54
|
"type": "Valve",
|
|
35
55
|
"in": "after_pipe_1",
|
|
36
56
|
"out": "after_valve_1",
|
|
37
|
-
"pressure_ratio": 0.5
|
|
57
|
+
"pressure_ratio": 0.5,
|
|
58
|
+
"material": 3
|
|
38
59
|
},
|
|
39
60
|
"pipe_2": {
|
|
40
61
|
"type": "Pipes",
|
|
41
62
|
"in": "after_valve_1",
|
|
42
63
|
"out": "after_pipe_2",
|
|
43
64
|
"delta_p": 1000.0,
|
|
44
|
-
"diameter": 0.08
|
|
65
|
+
"diameter": 0.08,
|
|
66
|
+
"material": 3
|
|
45
67
|
},
|
|
46
68
|
"strainer_1": {
|
|
47
69
|
"type": "Strainer",
|
|
48
70
|
"in": "after_pipe_2",
|
|
49
71
|
"out": "after_strainer_1",
|
|
50
|
-
"deltaP": 5000.0
|
|
72
|
+
"deltaP": 5000.0,
|
|
73
|
+
"material": 3
|
|
51
74
|
},
|
|
52
75
|
"pipe_3": {
|
|
53
76
|
"type": "Pipes",
|
|
54
77
|
"in": "after_strainer_1",
|
|
55
78
|
"out": "after_pipe_3",
|
|
56
79
|
"delta_p": 1000.0,
|
|
57
|
-
"diameter": 0.1
|
|
80
|
+
"diameter": 0.1,
|
|
81
|
+
"material": 3
|
|
58
82
|
},
|
|
59
83
|
"pump_2": {
|
|
60
84
|
"type": "Pump",
|
|
61
85
|
"in": "after_pipe_3",
|
|
62
86
|
"out": "after_pump_2",
|
|
63
87
|
"deltaP": 300000.0,
|
|
64
|
-
"efficiency": 0.8
|
|
88
|
+
"efficiency": 0.8,
|
|
89
|
+
"material": 3
|
|
65
90
|
},
|
|
66
91
|
"pipe_4": {
|
|
67
92
|
"type": "Pipes",
|
|
68
93
|
"in": "after_pump_2",
|
|
69
94
|
"out": "product",
|
|
70
95
|
"delta_p": 1000.0,
|
|
71
|
-
"diameter": 0.12
|
|
96
|
+
"diameter": 0.12,
|
|
97
|
+
"material": 3
|
|
72
98
|
}
|
|
73
99
|
},
|
|
74
100
|
"simulation": {
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "processforge"
|
|
7
|
-
version = "0.2.
|
|
7
|
+
version = "0.2.15"
|
|
8
8
|
description = "A Python-based process simulation framework for chemical engineering applications."
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
license = "BSD-3-Clause"
|
|
@@ -33,6 +33,7 @@ def validate_flowsheet(config_path):
|
|
|
33
33
|
logger.info(f"✅ Flowsheet '{config_path}' is valid.")
|
|
34
34
|
check_stream_connectivity(config)
|
|
35
35
|
_check_material_semantics(config)
|
|
36
|
+
_resolve_material_mix_streams(config)
|
|
36
37
|
return config
|
|
37
38
|
except ValidationError as e:
|
|
38
39
|
logger.error(f"❌ Validation failed for '{config_path}':")
|
|
@@ -237,6 +238,15 @@ def _check_material_semantics(config):
|
|
|
237
238
|
f"❌ Stream '{stream_name}' z-key '{comp_name}' is not defined in materials."
|
|
238
239
|
)
|
|
239
240
|
|
|
241
|
+
# 5b. z and material_mix are mutually exclusive on a stream
|
|
242
|
+
for stream_name, stream_def in config.get("streams", {}).items():
|
|
243
|
+
if "z" in stream_def and "material_mix" in stream_def:
|
|
244
|
+
raise ValueError(
|
|
245
|
+
f"❌ Stream '{stream_name}' defines both 'z' and 'material_mix'. "
|
|
246
|
+
f"Use 'material_mix' to reference a predefined mix, or 'z' for an "
|
|
247
|
+
f"explicit composition — not both."
|
|
248
|
+
)
|
|
249
|
+
|
|
240
250
|
# 6. Stream material_mix references must resolve to a valid friendly_material_mix_id
|
|
241
251
|
valid_mix_ids = {
|
|
242
252
|
mix_def["friendly_material_mix_id"]
|
|
@@ -261,3 +271,46 @@ def _check_material_semantics(config):
|
|
|
261
271
|
)
|
|
262
272
|
|
|
263
273
|
return True
|
|
274
|
+
|
|
275
|
+
|
|
276
|
+
def _resolve_material_mix_streams(config: dict) -> dict:
|
|
277
|
+
"""Expand material_mix references on streams into z composition dicts.
|
|
278
|
+
|
|
279
|
+
For every stream that carries a ``material_mix`` integer
|
|
280
|
+
(a ``friendly_material_mix_id``), this function looks up the corresponding
|
|
281
|
+
mix definition and writes its component fractions into ``stream["z"]``.
|
|
282
|
+
This expansion happens after validation so that the solver receives a fully
|
|
283
|
+
populated ``z`` dict regardless of whether the author wrote ``z`` explicitly
|
|
284
|
+
or used a ``material_mix`` reference.
|
|
285
|
+
|
|
286
|
+
The mutual-exclusivity rule (``z`` and ``material_mix`` cannot coexist) is
|
|
287
|
+
enforced earlier in ``_check_material_semantics``, so by the time this
|
|
288
|
+
function runs every stream has at most one of the two.
|
|
289
|
+
|
|
290
|
+
Args:
|
|
291
|
+
config: Validated flowsheet configuration dict (mutated in place).
|
|
292
|
+
|
|
293
|
+
Returns:
|
|
294
|
+
The same config dict with ``z`` populated on any stream that had
|
|
295
|
+
``material_mix``.
|
|
296
|
+
"""
|
|
297
|
+
material_mixes = config.get("material_mixes", {})
|
|
298
|
+
if not material_mixes:
|
|
299
|
+
return config
|
|
300
|
+
|
|
301
|
+
id_to_mix = {
|
|
302
|
+
mix_def["friendly_material_mix_id"]: mix_def
|
|
303
|
+
for mix_def in material_mixes.values()
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
for stream_def in config.get("streams", {}).values():
|
|
307
|
+
mix_ref = stream_def.get("material_mix")
|
|
308
|
+
if mix_ref is not None:
|
|
309
|
+
mix = id_to_mix[mix_ref] # existence already validated
|
|
310
|
+
stream_def["z"] = {
|
|
311
|
+
comp["name"]: comp["fraction"]
|
|
312
|
+
for comp in mix["components"]
|
|
313
|
+
if "fraction" in comp
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
return config
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: processforge
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.15
|
|
4
4
|
Summary: A Python-based process simulation framework for chemical engineering applications.
|
|
5
5
|
Author-email: Process Forge Team <team@processforge.dev>
|
|
6
6
|
License-Expression: BSD-3-Clause
|
|
@@ -223,12 +223,27 @@ Flowsheets are defined as JSON files. The `simulation.mode` field controls which
|
|
|
223
223
|
```json
|
|
224
224
|
{
|
|
225
225
|
"metadata": { "name": "My Flowsheet", "version": "2.0" },
|
|
226
|
+
"materials": {
|
|
227
|
+
"Water": { "friendly_material_id": 1 },
|
|
228
|
+
"Toluene": { "friendly_material_id": 2 },
|
|
229
|
+
"Steel": { "friendly_material_id": 3 }
|
|
230
|
+
},
|
|
231
|
+
"material_mixes": {
|
|
232
|
+
"Water_Toluene_Mix": {
|
|
233
|
+
"friendly_material_mix_id": 1,
|
|
234
|
+
"percent_type": "ao",
|
|
235
|
+
"components": [
|
|
236
|
+
{ "name": "Water", "fraction": 0.8 },
|
|
237
|
+
{ "name": "Toluene", "fraction": 0.2 }
|
|
238
|
+
]
|
|
239
|
+
}
|
|
240
|
+
},
|
|
226
241
|
"streams": {
|
|
227
|
-
"feed": { "T": 298.15, "P": 101325, "flowrate": 1.0, "
|
|
242
|
+
"feed": { "T": 298.15, "P": 101325, "flowrate": 1.0, "material_mix": 1 }
|
|
228
243
|
},
|
|
229
244
|
"units": {
|
|
230
|
-
"pump_1":
|
|
231
|
-
"valve_1": { "type": "Valve", "in": "after_pump",
|
|
245
|
+
"pump_1": { "type": "Pump", "in": "feed", "out": "after_pump", "deltaP": 200000, "efficiency": 0.75, "material": 3 },
|
|
246
|
+
"valve_1": { "type": "Valve", "in": "after_pump", "out": "product", "pressure_ratio": 0.5, "material": 3 }
|
|
232
247
|
},
|
|
233
248
|
"simulation": {
|
|
234
249
|
"mode": "steady",
|
|
@@ -239,6 +254,40 @@ Flowsheets are defined as JSON files. The `simulation.mode` field controls which
|
|
|
239
254
|
|
|
240
255
|
The optional `backend` key selects the EO solver backend (`"scipy"`, `"pyomo"`, or `"casadi"`). Defaults to `"scipy"`.
|
|
241
256
|
|
|
257
|
+
### Materials and composition
|
|
258
|
+
|
|
259
|
+
Stream composition can be defined in two ways:
|
|
260
|
+
|
|
261
|
+
**Explicit `z` dict** — list component mole fractions directly on the stream:
|
|
262
|
+
```json
|
|
263
|
+
"feed": { "T": 298.15, "P": 101325, "flowrate": 1.0, "z": { "Water": 0.8, "Toluene": 0.2 } }
|
|
264
|
+
```
|
|
265
|
+
|
|
266
|
+
**`material_mix` reference** — define a reusable mix in the top-level `material_mixes` section and reference it by `friendly_material_mix_id`. The validator automatically expands the reference into a `z` dict before simulation:
|
|
267
|
+
```json
|
|
268
|
+
"material_mixes": {
|
|
269
|
+
"Water_Toluene_Mix": {
|
|
270
|
+
"friendly_material_mix_id": 1,
|
|
271
|
+
"percent_type": "ao",
|
|
272
|
+
"components": [
|
|
273
|
+
{ "name": "Water", "fraction": 0.8 },
|
|
274
|
+
{ "name": "Toluene", "fraction": 0.2 }
|
|
275
|
+
]
|
|
276
|
+
}
|
|
277
|
+
},
|
|
278
|
+
"streams": {
|
|
279
|
+
"feed": { "T": 298.15, "P": 101325, "flowrate": 1.0, "material_mix": 1 }
|
|
280
|
+
}
|
|
281
|
+
```
|
|
282
|
+
|
|
283
|
+
Rules:
|
|
284
|
+
- `z` and `material_mix` are mutually exclusive on a single stream.
|
|
285
|
+
- `friendly_material_mix_id` values must be unique across all mixes.
|
|
286
|
+
- Each component `name` in a mix must match a key in the top-level `materials` section.
|
|
287
|
+
- When all component fractions are provided they must sum to 1.0.
|
|
288
|
+
|
|
289
|
+
Every unit also requires a `material` integer field pointing to a `friendly_material_id` in the `materials` section (this identifies the structural material the unit is made of, separate from the fluid composition).
|
|
290
|
+
|
|
242
291
|
### Recycle streams
|
|
243
292
|
|
|
244
293
|
Recycle streams require no special configuration. Any stream produced as the `out` of one unit can be used as the `in` of any other unit — including upstream units. The EO solver resolves the full coupled system simultaneously.
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|