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.
Files changed (69) hide show
  1. {processforge-0.2.14/src/processforge.egg-info → processforge-0.2.15}/PKG-INFO +53 -4
  2. {processforge-0.2.14 → processforge-0.2.15}/README.md +52 -3
  3. {processforge-0.2.14 → processforge-0.2.15}/flowsheets/closed-loop-chain.json +0 -4
  4. {processforge-0.2.14 → processforge-0.2.15}/flowsheets/hydraulic-chain.json +39 -13
  5. {processforge-0.2.14 → processforge-0.2.15}/pyproject.toml +1 -1
  6. {processforge-0.2.14 → processforge-0.2.15}/src/processforge/utils/validate_flowsheet.py +53 -0
  7. {processforge-0.2.14 → processforge-0.2.15/src/processforge.egg-info}/PKG-INFO +53 -4
  8. {processforge-0.2.14 → processforge-0.2.15}/LICENSE +0 -0
  9. {processforge-0.2.14 → processforge-0.2.15}/MANIFEST.in +0 -0
  10. {processforge-0.2.14 → processforge-0.2.15}/flowsheets/archive/example_dynamic_hybrid.json +0 -0
  11. {processforge-0.2.14 → processforge-0.2.15}/flowsheets/archive/example_dynamic_tank.json +0 -0
  12. {processforge-0.2.14 → processforge-0.2.15}/flowsheets/archive/example_flash.json +0 -0
  13. {processforge-0.2.14 → processforge-0.2.15}/flowsheets/archive/hydraulic_chain.json +0 -0
  14. {processforge-0.2.14 → processforge-0.2.15}/setup.cfg +0 -0
  15. {processforge-0.2.14 → processforge-0.2.15}/src/processforge/__init__.py +0 -0
  16. {processforge-0.2.14 → processforge-0.2.15}/src/processforge/_schema.py +0 -0
  17. {processforge-0.2.14 → processforge-0.2.15}/src/processforge/eo/__init__.py +0 -0
  18. {processforge-0.2.14 → processforge-0.2.15}/src/processforge/eo/backends/__init__.py +0 -0
  19. {processforge-0.2.14 → processforge-0.2.15}/src/processforge/eo/backends/base.py +0 -0
  20. {processforge-0.2.14 → processforge-0.2.15}/src/processforge/eo/backends/casadi_backend.py +0 -0
  21. {processforge-0.2.14 → processforge-0.2.15}/src/processforge/eo/backends/pyomo_backend.py +0 -0
  22. {processforge-0.2.14 → processforge-0.2.15}/src/processforge/eo/backends/scipy_backend.py +0 -0
  23. {processforge-0.2.14 → processforge-0.2.15}/src/processforge/eo/flowsheet.py +0 -0
  24. {processforge-0.2.14 → processforge-0.2.15}/src/processforge/eo/jacobian.py +0 -0
  25. {processforge-0.2.14 → processforge-0.2.15}/src/processforge/eo/mixin.py +0 -0
  26. {processforge-0.2.14 → processforge-0.2.15}/src/processforge/eo/solver.py +0 -0
  27. {processforge-0.2.14 → processforge-0.2.15}/src/processforge/eo/stream_var.py +0 -0
  28. {processforge-0.2.14 → processforge-0.2.15}/src/processforge/eo/units/__init__.py +0 -0
  29. {processforge-0.2.14 → processforge-0.2.15}/src/processforge/eo/units/flash_eo.py +0 -0
  30. {processforge-0.2.14 → processforge-0.2.15}/src/processforge/eo/units/heater_eo.py +0 -0
  31. {processforge-0.2.14 → processforge-0.2.15}/src/processforge/eo/units/pipes_eo.py +0 -0
  32. {processforge-0.2.14 → processforge-0.2.15}/src/processforge/eo/units/pump_eo.py +0 -0
  33. {processforge-0.2.14 → processforge-0.2.15}/src/processforge/eo/units/strainer_eo.py +0 -0
  34. {processforge-0.2.14 → processforge-0.2.15}/src/processforge/eo/units/valve_eo.py +0 -0
  35. {processforge-0.2.14 → processforge-0.2.15}/src/processforge/flowsheet.py +0 -0
  36. {processforge-0.2.14 → processforge-0.2.15}/src/processforge/fmu/__init__.py +0 -0
  37. {processforge-0.2.14 → processforge-0.2.15}/src/processforge/fmu/_fmi_vars.py +0 -0
  38. {processforge-0.2.14 → processforge-0.2.15}/src/processforge/fmu/builder.py +0 -0
  39. {processforge-0.2.14 → processforge-0.2.15}/src/processforge/fmu/slave_template.py +0 -0
  40. {processforge-0.2.14 → processforge-0.2.15}/src/processforge/modelica/__init__.py +0 -0
  41. {processforge-0.2.14 → processforge-0.2.15}/src/processforge/modelica/mo_writer.py +0 -0
  42. {processforge-0.2.14 → processforge-0.2.15}/src/processforge/modelica/omc_runner.py +0 -0
  43. {processforge-0.2.14 → processforge-0.2.15}/src/processforge/modelica/transpiler.py +0 -0
  44. {processforge-0.2.14 → processforge-0.2.15}/src/processforge/modelica/unit_equations.py +0 -0
  45. {processforge-0.2.14 → processforge-0.2.15}/src/processforge/provenance.py +0 -0
  46. {processforge-0.2.14 → processforge-0.2.15}/src/processforge/result.py +0 -0
  47. {processforge-0.2.14 → processforge-0.2.15}/src/processforge/schemas/__init__.py +0 -0
  48. {processforge-0.2.14 → processforge-0.2.15}/src/processforge/schemas/flowsheet_schema.json +0 -0
  49. {processforge-0.2.14 → processforge-0.2.15}/src/processforge/simulate.py +0 -0
  50. {processforge-0.2.14 → processforge-0.2.15}/src/processforge/solver.py +0 -0
  51. {processforge-0.2.14 → processforge-0.2.15}/src/processforge/thermo.py +0 -0
  52. {processforge-0.2.14 → processforge-0.2.15}/src/processforge/units/__init__.py +0 -0
  53. {processforge-0.2.14 → processforge-0.2.15}/src/processforge/units/flash.py +0 -0
  54. {processforge-0.2.14 → processforge-0.2.15}/src/processforge/units/heater.py +0 -0
  55. {processforge-0.2.14 → processforge-0.2.15}/src/processforge/units/pipes.py +0 -0
  56. {processforge-0.2.14 → processforge-0.2.15}/src/processforge/units/pump.py +0 -0
  57. {processforge-0.2.14 → processforge-0.2.15}/src/processforge/units/solver.py +0 -0
  58. {processforge-0.2.14 → processforge-0.2.15}/src/processforge/units/strainer.py +0 -0
  59. {processforge-0.2.14 → processforge-0.2.15}/src/processforge/units/tank.py +0 -0
  60. {processforge-0.2.14 → processforge-0.2.15}/src/processforge/units/valve.py +0 -0
  61. {processforge-0.2.14 → processforge-0.2.15}/src/processforge/utils/__init__.py +0 -0
  62. {processforge-0.2.14 → processforge-0.2.15}/src/processforge/utils/flowsheet_diagram.py +0 -0
  63. {processforge-0.2.14 → processforge-0.2.15}/src/processforge/utils/validation.py +0 -0
  64. {processforge-0.2.14 → processforge-0.2.15}/src/processforge/validate.py +0 -0
  65. {processforge-0.2.14 → processforge-0.2.15}/src/processforge.egg-info/SOURCES.txt +0 -0
  66. {processforge-0.2.14 → processforge-0.2.15}/src/processforge.egg-info/dependency_links.txt +0 -0
  67. {processforge-0.2.14 → processforge-0.2.15}/src/processforge.egg-info/entry_points.txt +0 -0
  68. {processforge-0.2.14 → processforge-0.2.15}/src/processforge.egg-info/requires.txt +0 -0
  69. {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.14
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, "z": { "Water": 0.8, "Toluene": 0.2 } }
242
+ "feed": { "T": 298.15, "P": 101325, "flowrate": 1.0, "material_mix": 1 }
228
243
  },
229
244
  "units": {
230
- "pump_1": { "type": "Pump", "in": "feed", "out": "after_pump", "deltaP": 200000, "efficiency": 0.75 },
231
- "valve_1": { "type": "Valve", "in": "after_pump", "out": "product", "pressure_ratio": 0.5 }
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, "z": { "Water": 0.8, "Toluene": 0.2 } }
191
+ "feed": { "T": 298.15, "P": 101325, "flowrate": 1.0, "material_mix": 1 }
177
192
  },
178
193
  "units": {
179
- "pump_1": { "type": "Pump", "in": "feed", "out": "after_pump", "deltaP": 200000, "efficiency": 0.75 },
180
- "valve_1": { "type": "Valve", "in": "after_pump", "out": "product", "pressure_ratio": 0.5 }
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.
@@ -52,10 +52,6 @@
52
52
  "feed": {
53
53
  "T": 298.15,
54
54
  "P": 101325,
55
- "z": {
56
- "Benzene": 0.5,
57
- "Toluene": 0.5
58
- },
59
55
  "flowrate": 0.1,
60
56
  "material_mix": 1
61
57
  }
@@ -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
- "z": {
12
- "Water": 0.8,
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.14"
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.14
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, "z": { "Water": 0.8, "Toluene": 0.2 } }
242
+ "feed": { "T": 298.15, "P": 101325, "flowrate": 1.0, "material_mix": 1 }
228
243
  },
229
244
  "units": {
230
- "pump_1": { "type": "Pump", "in": "feed", "out": "after_pump", "deltaP": 200000, "efficiency": 0.75 },
231
- "valve_1": { "type": "Valve", "in": "after_pump", "out": "product", "pressure_ratio": 0.5 }
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