processforge 0.3.11__tar.gz → 0.3.12__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.3.11/src/processforge.egg-info → processforge-0.3.12}/PKG-INFO +2 -3
- {processforge-0.3.11 → processforge-0.3.12}/flowsheets/festim/hydrogen_transport_1d.json +24 -25
- processforge-0.3.12/flowsheets/festim/tds_tungsten.json +136 -0
- {processforge-0.3.11 → processforge-0.3.12}/pyproject.toml +2 -4
- {processforge-0.3.11 → processforge-0.3.12}/src/processforge/api/serve.py +11 -22
- {processforge-0.3.11 → processforge-0.3.12}/src/processforge/cli/apply.py +24 -10
- {processforge-0.3.11 → processforge-0.3.12}/src/processforge/cli/common.py +9 -9
- {processforge-0.3.11 → processforge-0.3.12}/src/processforge/cli/run.py +64 -22
- {processforge-0.3.11 → processforge-0.3.12}/src/processforge/eo/flowsheet.py +36 -1
- {processforge-0.3.11 → processforge-0.3.12}/src/processforge/eo/jacobian.py +9 -5
- {processforge-0.3.11 → processforge-0.3.12}/src/processforge/flowsheet.py +50 -1
- processforge-0.3.12/src/processforge/output_collector.py +97 -0
- processforge-0.3.12/src/processforge/persistence/__init__.py +7 -0
- processforge-0.3.12/src/processforge/persistence/archive.py +166 -0
- processforge-0.3.12/src/processforge/persistence/artifact_store.py +132 -0
- {processforge-0.3.11 → processforge-0.3.12}/src/processforge/providers/base.py +5 -4
- {processforge-0.3.11 → processforge-0.3.12}/src/processforge/providers/container_client.py +34 -16
- processforge-0.3.12/src/processforge/providers/festim_provider.py +891 -0
- {processforge-0.3.11 → processforge-0.3.12}/src/processforge/providers/openmc_provider.py +85 -41
- processforge-0.3.12/src/processforge/quantity.py +104 -0
- processforge-0.3.12/src/processforge/schemas/festim/festim_model.py +717 -0
- {processforge-0.3.11 → processforge-0.3.12}/src/processforge/types.py +154 -23
- {processforge-0.3.11 → processforge-0.3.12}/src/processforge/units/__init__.py +3 -0
- {processforge-0.3.11 → processforge-0.3.12}/src/processforge/units/solver_unit.py +9 -4
- {processforge-0.3.11 → processforge-0.3.12}/src/processforge/utils/validate_flowsheet.py +31 -89
- {processforge-0.3.11 → processforge-0.3.12/src/processforge.egg-info}/PKG-INFO +2 -3
- {processforge-0.3.11 → processforge-0.3.12}/src/processforge.egg-info/SOURCES.txt +8 -0
- {processforge-0.3.11 → processforge-0.3.12}/src/processforge.egg-info/requires.txt +1 -3
- {processforge-0.3.11 → processforge-0.3.12}/tests/test_cli.py +5 -5
- processforge-0.3.12/tests/test_festim_provider.py +807 -0
- processforge-0.3.12/tests/test_festim_schema.py +341 -0
- {processforge-0.3.11 → processforge-0.3.12}/tests/test_types.py +40 -21
- processforge-0.3.11/src/processforge/providers/festim_provider.py +0 -411
- processforge-0.3.11/tests/test_festim_provider.py +0 -596
- {processforge-0.3.11 → processforge-0.3.12}/LICENSE +0 -0
- {processforge-0.3.11 → processforge-0.3.12}/MANIFEST.in +0 -0
- {processforge-0.3.11 → processforge-0.3.12}/README.md +0 -0
- {processforge-0.3.11 → processforge-0.3.12}/flowsheets/closed-loop-chain.json +0 -0
- {processforge-0.3.11 → processforge-0.3.12}/flowsheets/hydraulic-chain.json +0 -0
- {processforge-0.3.11 → processforge-0.3.12}/flowsheets/openmc/msre_eigenvalue.json +0 -0
- {processforge-0.3.11 → processforge-0.3.12}/setup.cfg +0 -0
- {processforge-0.3.11 → processforge-0.3.12}/src/processforge/__init__.py +0 -0
- {processforge-0.3.11 → processforge-0.3.12}/src/processforge/__main__.py +0 -0
- {processforge-0.3.11 → processforge-0.3.12}/src/processforge/_schema.py +0 -0
- {processforge-0.3.11 → processforge-0.3.12}/src/processforge/analysis/__init__.py +0 -0
- {processforge-0.3.11 → processforge-0.3.12}/src/processforge/analysis/dof.py +0 -0
- {processforge-0.3.11 → processforge-0.3.12}/src/processforge/api/__init__.py +0 -0
- {processforge-0.3.11 → processforge-0.3.12}/src/processforge/cli/__init__.py +0 -0
- {processforge-0.3.11 → processforge-0.3.12}/src/processforge/cli/diagram.py +0 -0
- {processforge-0.3.11 → processforge-0.3.12}/src/processforge/cli/display.py +0 -0
- {processforge-0.3.11 → processforge-0.3.12}/src/processforge/cli/export_fmu.py +0 -0
- {processforge-0.3.11 → processforge-0.3.12}/src/processforge/cli/export_modelica.py +0 -0
- {processforge-0.3.11 → processforge-0.3.12}/src/processforge/cli/init.py +0 -0
- {processforge-0.3.11 → processforge-0.3.12}/src/processforge/cli/plan.py +0 -0
- {processforge-0.3.11 → processforge-0.3.12}/src/processforge/cli/validate.py +0 -0
- {processforge-0.3.11 → processforge-0.3.12}/src/processforge/compose.py +0 -0
- {processforge-0.3.11 → processforge-0.3.12}/src/processforge/eo/__init__.py +0 -0
- {processforge-0.3.11 → processforge-0.3.12}/src/processforge/eo/backends/__init__.py +0 -0
- {processforge-0.3.11 → processforge-0.3.12}/src/processforge/eo/backends/base.py +0 -0
- {processforge-0.3.11 → processforge-0.3.12}/src/processforge/eo/backends/casadi_backend.py +0 -0
- {processforge-0.3.11 → processforge-0.3.12}/src/processforge/eo/backends/pyomo_backend.py +0 -0
- {processforge-0.3.11 → processforge-0.3.12}/src/processforge/eo/backends/scipy_backend.py +0 -0
- {processforge-0.3.11 → processforge-0.3.12}/src/processforge/eo/mixin.py +0 -0
- {processforge-0.3.11 → processforge-0.3.12}/src/processforge/eo/solver.py +0 -0
- {processforge-0.3.11 → processforge-0.3.12}/src/processforge/eo/stream_var.py +0 -0
- {processforge-0.3.11 → processforge-0.3.12}/src/processforge/eo/units/__init__.py +0 -0
- {processforge-0.3.11 → processforge-0.3.12}/src/processforge/eo/units/flash_eo.py +0 -0
- {processforge-0.3.11 → processforge-0.3.12}/src/processforge/eo/units/heater_eo.py +0 -0
- {processforge-0.3.11 → processforge-0.3.12}/src/processforge/eo/units/pipes_eo.py +0 -0
- {processforge-0.3.11 → processforge-0.3.12}/src/processforge/eo/units/pump_eo.py +0 -0
- {processforge-0.3.11 → processforge-0.3.12}/src/processforge/eo/units/strainer_eo.py +0 -0
- {processforge-0.3.11 → processforge-0.3.12}/src/processforge/eo/units/valve_eo.py +0 -0
- {processforge-0.3.11 → processforge-0.3.12}/src/processforge/fmu/__init__.py +0 -0
- {processforge-0.3.11 → processforge-0.3.12}/src/processforge/fmu/_fmi_vars.py +0 -0
- {processforge-0.3.11 → processforge-0.3.12}/src/processforge/fmu/builder.py +0 -0
- {processforge-0.3.11 → processforge-0.3.12}/src/processforge/fmu/slave_template.py +0 -0
- {processforge-0.3.11 → processforge-0.3.12}/src/processforge/lock.py +0 -0
- {processforge-0.3.11 → processforge-0.3.12}/src/processforge/modelica/__init__.py +0 -0
- {processforge-0.3.11 → processforge-0.3.12}/src/processforge/modelica/mo_writer.py +0 -0
- {processforge-0.3.11 → processforge-0.3.12}/src/processforge/modelica/omc_runner.py +0 -0
- {processforge-0.3.11 → processforge-0.3.12}/src/processforge/modelica/transpiler.py +0 -0
- {processforge-0.3.11 → processforge-0.3.12}/src/processforge/modelica/unit_equations.py +0 -0
- {processforge-0.3.11 → processforge-0.3.12}/src/processforge/pcl/__init__.py +0 -0
- {processforge-0.3.11 → processforge-0.3.12}/src/processforge/pcl/compiler.py +0 -0
- {processforge-0.3.11 → processforge-0.3.12}/src/processforge/pcl/namespace.py +0 -0
- {processforge-0.3.11 → processforge-0.3.12}/src/processforge/provenance.py +0 -0
- {processforge-0.3.11 → processforge-0.3.12}/src/processforge/providers/__init__.py +0 -0
- {processforge-0.3.11 → processforge-0.3.12}/src/processforge/providers/base_jacobian_mixin.py +0 -0
- {processforge-0.3.11 → processforge-0.3.12}/src/processforge/providers/cantera_jacobian.py +0 -0
- {processforge-0.3.11 → processforge-0.3.12}/src/processforge/providers/cantera_provider.py +0 -0
- {processforge-0.3.11 → processforge-0.3.12}/src/processforge/providers/coolprop_provider.py +0 -0
- {processforge-0.3.11 → processforge-0.3.12}/src/processforge/providers/jacobian_contributor.py +0 -0
- {processforge-0.3.11 → processforge-0.3.12}/src/processforge/providers/manager.py +0 -0
- {processforge-0.3.11 → processforge-0.3.12}/src/processforge/providers/modelica_jacobian.py +0 -0
- {processforge-0.3.11 → processforge-0.3.12}/src/processforge/providers/modelica_provider.py +0 -0
- {processforge-0.3.11 → processforge-0.3.12}/src/processforge/providers/reference_state_registry.py +0 -0
- {processforge-0.3.11 → processforge-0.3.12}/src/processforge/providers/registry.py +0 -0
- {processforge-0.3.11 → processforge-0.3.12}/src/processforge/result.py +0 -0
- {processforge-0.3.11 → processforge-0.3.12}/src/processforge/result_schema.py +0 -0
- {processforge-0.3.11 → processforge-0.3.12}/src/processforge/schemas/__init__.py +0 -0
- {processforge-0.3.11 → processforge-0.3.12}/src/processforge/schemas/flowsheet_schema.json +0 -0
- {processforge-0.3.11 → processforge-0.3.12}/src/processforge/schemas/openmc/openmc_model.py +0 -0
- {processforge-0.3.11 → processforge-0.3.12}/src/processforge/simulate.py +0 -0
- {processforge-0.3.11 → processforge-0.3.12}/src/processforge/solver.py +0 -0
- {processforge-0.3.11 → processforge-0.3.12}/src/processforge/state.py +0 -0
- {processforge-0.3.11 → processforge-0.3.12}/src/processforge/thermo.py +0 -0
- {processforge-0.3.11 → processforge-0.3.12}/src/processforge/units/base.py +0 -0
- {processforge-0.3.11 → processforge-0.3.12}/src/processforge/units/cstr.py +0 -0
- {processforge-0.3.11 → processforge-0.3.12}/src/processforge/units/flash.py +0 -0
- {processforge-0.3.11 → processforge-0.3.12}/src/processforge/units/heater.py +0 -0
- {processforge-0.3.11 → processforge-0.3.12}/src/processforge/units/pfr.py +0 -0
- {processforge-0.3.11 → processforge-0.3.12}/src/processforge/units/pipes.py +0 -0
- {processforge-0.3.11 → processforge-0.3.12}/src/processforge/units/pump.py +0 -0
- {processforge-0.3.11 → processforge-0.3.12}/src/processforge/units/registry.py +0 -0
- {processforge-0.3.11 → processforge-0.3.12}/src/processforge/units/strainer.py +0 -0
- {processforge-0.3.11 → processforge-0.3.12}/src/processforge/units/tank.py +0 -0
- {processforge-0.3.11 → processforge-0.3.12}/src/processforge/units/valve.py +0 -0
- {processforge-0.3.11 → processforge-0.3.12}/src/processforge/utils/__init__.py +0 -0
- {processforge-0.3.11 → processforge-0.3.12}/src/processforge/utils/flowsheet_diagram.py +0 -0
- {processforge-0.3.11 → processforge-0.3.12}/src/processforge/utils/mermaid_diagram.py +0 -0
- {processforge-0.3.11 → processforge-0.3.12}/src/processforge/utils/s3_upload.py +0 -0
- {processforge-0.3.11 → processforge-0.3.12}/src/processforge/utils/topology.py +0 -0
- {processforge-0.3.11 → processforge-0.3.12}/src/processforge/utils/unit_consistency.py +0 -0
- {processforge-0.3.11 → processforge-0.3.12}/src/processforge/utils/validation.py +0 -0
- {processforge-0.3.11 → processforge-0.3.12}/src/processforge.egg-info/dependency_links.txt +0 -0
- {processforge-0.3.11 → processforge-0.3.12}/src/processforge.egg-info/entry_points.txt +0 -0
- {processforge-0.3.11 → processforge-0.3.12}/src/processforge.egg-info/top_level.txt +0 -0
- {processforge-0.3.11 → processforge-0.3.12}/tests/test_init.py +0 -0
- {processforge-0.3.11 → processforge-0.3.12}/tests/test_openmc_provider.py +0 -0
- {processforge-0.3.11 → processforge-0.3.12}/tests/test_providers.py +0 -0
- {processforge-0.3.11 → processforge-0.3.12}/tests/test_s3_upload.py +0 -0
- {processforge-0.3.11 → processforge-0.3.12}/tests/test_state.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: processforge
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.12
|
|
4
4
|
Summary: A Python-based process simulation framework for chemical engineering applications.
|
|
5
5
|
Author-email: Processforge Team <team@processforge.dev>
|
|
6
6
|
License-Expression: BSD-3-Clause
|
|
@@ -28,6 +28,7 @@ Requires-Dist: jsonschema>=4.20
|
|
|
28
28
|
Requires-Dist: graphviz>=0.20
|
|
29
29
|
Requires-Dist: pandas>=2.2
|
|
30
30
|
Requires-Dist: zarr>=3.0
|
|
31
|
+
Requires-Dist: pint<1.0,>=0.24
|
|
31
32
|
Requires-Dist: coolprop>=7.2.0
|
|
32
33
|
Requires-Dist: typer>=0.12
|
|
33
34
|
Provides-Extra: api
|
|
@@ -48,8 +49,6 @@ Provides-Extra: cantera
|
|
|
48
49
|
Requires-Dist: cantera>=3.0; extra == "cantera"
|
|
49
50
|
Provides-Extra: coolprop
|
|
50
51
|
Requires-Dist: coolprop>=6.6; extra == "coolprop"
|
|
51
|
-
Provides-Extra: pcl
|
|
52
|
-
Requires-Dist: pint>=0.24; extra == "pcl"
|
|
53
52
|
Provides-Extra: dev
|
|
54
53
|
Requires-Dist: pytest>=7.0; extra == "dev"
|
|
55
54
|
Requires-Dist: black; extra == "dev"
|
|
@@ -25,31 +25,30 @@
|
|
|
25
25
|
"festim_solver": {
|
|
26
26
|
"type": "SolverUnit",
|
|
27
27
|
"provider": "festim",
|
|
28
|
-
"sim_type": "
|
|
29
|
-
"mesh": {
|
|
30
|
-
"vertices": [0.0, 0.001, 0.002, 0.003, 0.004, 0.005, 0.006, 0.007, 0.008, 0.009, 0.01]
|
|
31
|
-
},
|
|
32
|
-
"species": [
|
|
33
|
-
{ "name": "H" }
|
|
34
|
-
],
|
|
35
|
-
"subdomains": {
|
|
36
|
-
"volume": [
|
|
37
|
-
{ "id": 1, "borders": [0.0, 0.01], "material": "default" }
|
|
38
|
-
],
|
|
39
|
-
"surface": [
|
|
40
|
-
{ "id": 2, "x": 0.0 },
|
|
41
|
-
{ "id": 3, "x": 0.01 }
|
|
42
|
-
]
|
|
43
|
-
},
|
|
44
|
-
"boundary_conditions": [
|
|
45
|
-
{ "type": "fixed_concentration", "subdomain_id": 2, "species": "H", "value": 1 },
|
|
46
|
-
{ "type": "fixed_concentration", "subdomain_id": 3, "species": "H", "value": 0 }
|
|
47
|
-
],
|
|
48
|
-
"exports": [
|
|
49
|
-
{ "type": "vtx_species", "field": "H", "filename": "hydrogen_concentration.bp", "times": "milestones" },
|
|
50
|
-
{ "type": "profile_1d", "field": "H", "volume_id": 1, "times": "milestones" }
|
|
51
|
-
],
|
|
28
|
+
"sim_type": "hydrogen_transport_tds",
|
|
52
29
|
"solver_config": {
|
|
30
|
+
"mesh": {
|
|
31
|
+
"vertices": [0.0, 0.001, 0.002, 0.003, 0.004, 0.005, 0.006, 0.007, 0.008, 0.009, 0.01]
|
|
32
|
+
},
|
|
33
|
+
"subdomains": {
|
|
34
|
+
"volume": [
|
|
35
|
+
{ "id": 1, "borders": [0.0, 0.01], "material": "default" }
|
|
36
|
+
],
|
|
37
|
+
"surface": [
|
|
38
|
+
{ "id": 2, "x": 0.0 },
|
|
39
|
+
{ "id": 3, "x": 0.01 }
|
|
40
|
+
]
|
|
41
|
+
},
|
|
42
|
+
"species": [
|
|
43
|
+
{ "name": "H", "mobile": true }
|
|
44
|
+
],
|
|
45
|
+
"boundary_conditions": [
|
|
46
|
+
{ "type": "fixed_concentration", "subdomain": 2, "species": "H", "value": 1 },
|
|
47
|
+
{ "type": "fixed_concentration", "subdomain": 3, "species": "H", "value": 0 }
|
|
48
|
+
],
|
|
49
|
+
"exports": [
|
|
50
|
+
{ "type": "profile_1d", "field": "H", "volume": 1, "times": [0.05, 0.1, 0.2, 0.5, 1] }
|
|
51
|
+
],
|
|
53
52
|
"atol": 1e-10,
|
|
54
53
|
"rtol": 1e-10,
|
|
55
54
|
"final_time": 2,
|
|
@@ -67,4 +66,4 @@
|
|
|
67
66
|
"simulation": {
|
|
68
67
|
"mode": "steady"
|
|
69
68
|
}
|
|
70
|
-
}
|
|
69
|
+
}
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
{
|
|
2
|
+
"metadata": {
|
|
3
|
+
"name": "FESTIM TDS — Tungsten Thermal Desorption",
|
|
4
|
+
"description": "Thermo-desorption spectra (TDS) for a tungsten sample. Gaussian implantation source (400 s), resting phase, then a temperature ramp from 300 K at 8 K/s to desorb trapped hydrogen. Two trapping sites modelled via implicit empty-trap species. Reproduces notebooks_task02.ipynb.py.",
|
|
5
|
+
"version": "1.0"
|
|
6
|
+
},
|
|
7
|
+
"providers": {
|
|
8
|
+
"festim": {
|
|
9
|
+
"type": "festim",
|
|
10
|
+
"url": "http://localhost:9002",
|
|
11
|
+
"output_dir": "festim"
|
|
12
|
+
}
|
|
13
|
+
},
|
|
14
|
+
"materials": {
|
|
15
|
+
"tungsten": {
|
|
16
|
+
"id": 1,
|
|
17
|
+
"extra": {
|
|
18
|
+
"D_0": 4.1e-7,
|
|
19
|
+
"E_D": 0.39
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
},
|
|
23
|
+
"streams": {},
|
|
24
|
+
"units": {
|
|
25
|
+
"tds_solver": {
|
|
26
|
+
"type": "SolverUnit",
|
|
27
|
+
"provider": "festim",
|
|
28
|
+
"sim_type": "hydrogen_transport_tds",
|
|
29
|
+
"solver_config": {
|
|
30
|
+
"mesh": {
|
|
31
|
+
"segments": [
|
|
32
|
+
{ "start": 0.0, "stop": 30e-9, "num": 200 },
|
|
33
|
+
{ "start": 30e-9, "stop": 3e-6, "num": 300 },
|
|
34
|
+
{ "start": 3e-6, "stop": 20e-6, "num": 200 }
|
|
35
|
+
]
|
|
36
|
+
},
|
|
37
|
+
"subdomains": {
|
|
38
|
+
"volume": [
|
|
39
|
+
{ "id": 1, "borders": [0.0, 20e-6], "material": "tungsten" }
|
|
40
|
+
],
|
|
41
|
+
"surface": [
|
|
42
|
+
{ "id": 1, "x": 0.0 },
|
|
43
|
+
{ "id": 2, "x": 20e-6 }
|
|
44
|
+
]
|
|
45
|
+
},
|
|
46
|
+
"species": [
|
|
47
|
+
{ "name": "H", "mobile": true },
|
|
48
|
+
{ "name": "trapped_H1", "mobile": false },
|
|
49
|
+
{ "name": "trapped_H2", "mobile": false }
|
|
50
|
+
],
|
|
51
|
+
"implicit_species": [
|
|
52
|
+
{ "name": "empty_trap1", "n": 135.99814960153805, "others": ["trapped_H1"] },
|
|
53
|
+
{ "name": "empty_trap2", "n": 41.84558449278094, "others": ["trapped_H2"] }
|
|
54
|
+
],
|
|
55
|
+
"reactions": [
|
|
56
|
+
{
|
|
57
|
+
"type": "arrhenius",
|
|
58
|
+
"reactant": ["H", "empty_trap1"],
|
|
59
|
+
"product": ["trapped_H1"],
|
|
60
|
+
"k_0": 53983071.22305305,
|
|
61
|
+
"E_k": 0.39,
|
|
62
|
+
"p_0": 1e13,
|
|
63
|
+
"E_p": 0.87,
|
|
64
|
+
"volume": 1
|
|
65
|
+
},
|
|
66
|
+
{
|
|
67
|
+
"type": "arrhenius",
|
|
68
|
+
"reactant": ["H", "empty_trap2"],
|
|
69
|
+
"product": ["trapped_H2"],
|
|
70
|
+
"k_0": 53983071.22305305,
|
|
71
|
+
"E_k": 0.39,
|
|
72
|
+
"p_0": 1e13,
|
|
73
|
+
"E_p": 1.0,
|
|
74
|
+
"volume": 1
|
|
75
|
+
}
|
|
76
|
+
],
|
|
77
|
+
"sources": [
|
|
78
|
+
{
|
|
79
|
+
"type": "particle",
|
|
80
|
+
"species": "H",
|
|
81
|
+
"volume": 1,
|
|
82
|
+
"value": {
|
|
83
|
+
"type": "gaussian",
|
|
84
|
+
"amplitude": 4.151347667934617e-05,
|
|
85
|
+
"center": 4.5e-9,
|
|
86
|
+
"width": 2.5e-9,
|
|
87
|
+
"active_until": 400
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
],
|
|
91
|
+
"boundary_conditions": [
|
|
92
|
+
{ "type": "fixed_concentration", "subdomain": 1, "species": "H", "value": 0 },
|
|
93
|
+
{ "type": "fixed_concentration", "subdomain": 2, "species": "H", "value": 0 }
|
|
94
|
+
],
|
|
95
|
+
"temperature": {
|
|
96
|
+
"type": "ramp",
|
|
97
|
+
"T0": 300,
|
|
98
|
+
"start_t": 450,
|
|
99
|
+
"ramp_rate": 8
|
|
100
|
+
},
|
|
101
|
+
"exports": [
|
|
102
|
+
{ "type": "surface_flux", "field": "H", "surface": 1, "filename": "flux_left.csv" },
|
|
103
|
+
{ "type": "surface_flux", "field": "H", "surface": 2, "filename": "flux_right.csv" },
|
|
104
|
+
{ "type": "total_volume", "field": "H", "volume": 1, "filename": "total_mobile_H.csv" },
|
|
105
|
+
{ "type": "total_volume", "field": "trapped_H1", "volume": 1, "filename": "total_trapped_H1.csv" },
|
|
106
|
+
{ "type": "total_volume", "field": "trapped_H2", "volume": 1, "filename": "total_trapped_H2.csv" },
|
|
107
|
+
{ "type": "profile_1d", "field": "H", "volume": 1, "times": [400, 450, 500] },
|
|
108
|
+
{ "type": "profile_1d", "field": "trapped_H1", "volume": 1, "times": [400, 450, 500] },
|
|
109
|
+
{ "type": "profile_1d", "field": "trapped_H2", "volume": 1, "times": [400, 450, 500] }
|
|
110
|
+
],
|
|
111
|
+
"atol": 1e-10,
|
|
112
|
+
"rtol": 1e-10,
|
|
113
|
+
"max_iterations": 30,
|
|
114
|
+
"transient": true,
|
|
115
|
+
"final_time": 500,
|
|
116
|
+
"element_degree": 1,
|
|
117
|
+
"stepsize": {
|
|
118
|
+
"initial_value": 0.5,
|
|
119
|
+
"growth_factor": 1.1,
|
|
120
|
+
"cutback_factor": 0.9,
|
|
121
|
+
"target_nb_iterations": 4,
|
|
122
|
+
"max_stepsize": {
|
|
123
|
+
"type": "after_t",
|
|
124
|
+
"after_t": 450,
|
|
125
|
+
"value": 0.5
|
|
126
|
+
},
|
|
127
|
+
"milestones": [400, 450, 500]
|
|
128
|
+
},
|
|
129
|
+
"convergence_criterion": "residual"
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
},
|
|
133
|
+
"simulation": {
|
|
134
|
+
"mode": "steady"
|
|
135
|
+
}
|
|
136
|
+
}
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "processforge"
|
|
7
|
-
version = "0.3.
|
|
7
|
+
version = "0.3.12"
|
|
8
8
|
description = "A Python-based process simulation framework for chemical engineering applications."
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
license = "BSD-3-Clause"
|
|
@@ -34,6 +34,7 @@ dependencies = [
|
|
|
34
34
|
"graphviz>=0.20",
|
|
35
35
|
"pandas>=2.2",
|
|
36
36
|
"zarr>=3.0",
|
|
37
|
+
"pint>=0.24,<1.0",
|
|
37
38
|
"coolprop>=7.2.0",
|
|
38
39
|
"typer>=0.12",
|
|
39
40
|
]
|
|
@@ -64,9 +65,6 @@ cantera = [
|
|
|
64
65
|
coolprop = [
|
|
65
66
|
"coolprop>=6.6",
|
|
66
67
|
]
|
|
67
|
-
pcl = [
|
|
68
|
-
"pint>=0.24",
|
|
69
|
-
]
|
|
70
68
|
dev = [
|
|
71
69
|
"pytest>=7.0",
|
|
72
70
|
"black",
|
|
@@ -74,11 +74,20 @@ def run(body: dict):
|
|
|
74
74
|
# Build a minimal flowsheet config holding materials.
|
|
75
75
|
flowsheet_config = type("FS", (), {"materials": materials})()
|
|
76
76
|
|
|
77
|
+
run_id = body.get("run_id", "")
|
|
78
|
+
flowsheet_hash = body.get("flowsheet_hash", "")
|
|
79
|
+
|
|
77
80
|
try:
|
|
78
81
|
provider.initialize(provider_config, flowsheet_config)
|
|
79
82
|
result = provider.run_simulation(unit_cfg, body.get("inlet", {}))
|
|
80
|
-
|
|
81
|
-
|
|
83
|
+
# Upload any declared artifacts to object storage (fills remote_uris).
|
|
84
|
+
# No-op when S3 is not configured.
|
|
85
|
+
from processforge.persistence.artifact_store import ArtifactStore
|
|
86
|
+
|
|
87
|
+
ArtifactStore().persist_outputs(
|
|
88
|
+
result, run_id=run_id, flowsheet_hash=flowsheet_hash, unit=getattr(result, "unit", "")
|
|
89
|
+
)
|
|
90
|
+
return result.model_dump()
|
|
82
91
|
except HTTPException:
|
|
83
92
|
raise
|
|
84
93
|
except Exception as exc:
|
|
@@ -90,26 +99,6 @@ def run(body: dict):
|
|
|
90
99
|
provider.teardown()
|
|
91
100
|
|
|
92
101
|
|
|
93
|
-
def _maybe_upload_run_outputs(result) -> None:
|
|
94
|
-
"""Upload the provider's run_dir to S3 when configured.
|
|
95
|
-
|
|
96
|
-
No-op unless ``S3_BUCKET`` is set in the container environment and the
|
|
97
|
-
provider recorded a ``run_dir`` in its result metadata. Upload failures are
|
|
98
|
-
logged and never fail the run.
|
|
99
|
-
"""
|
|
100
|
-
run_dir = result.metadata.get("run_dir")
|
|
101
|
-
if not run_dir:
|
|
102
|
-
return
|
|
103
|
-
try:
|
|
104
|
-
from processforge.utils.s3_upload import upload_directory_to_s3
|
|
105
|
-
|
|
106
|
-
s3_uri = upload_directory_to_s3(run_dir)
|
|
107
|
-
if s3_uri:
|
|
108
|
-
result.metadata["s3_uri"] = s3_uri
|
|
109
|
-
except Exception as exc: # noqa: BLE001
|
|
110
|
-
logger.warning(f"S3 upload of {run_dir} failed: {exc}")
|
|
111
|
-
|
|
112
|
-
|
|
113
102
|
if __name__ == "__main__":
|
|
114
103
|
import uvicorn
|
|
115
104
|
|
|
@@ -6,13 +6,32 @@ import os
|
|
|
6
6
|
import time
|
|
7
7
|
from typing import Literal
|
|
8
8
|
|
|
9
|
+
import hashlib
|
|
10
|
+
import json
|
|
11
|
+
import os
|
|
9
12
|
import typer
|
|
13
|
+
from datetime import datetime, timezone
|
|
10
14
|
from loguru import logger
|
|
11
15
|
|
|
16
|
+
|
|
17
|
+
def _persist_run(archive, fs, results, run_info, config, base_name, snapshot_id):
|
|
18
|
+
"""Build a standardized RunManifest and persist it to the archive."""
|
|
19
|
+
run_id = datetime.now(timezone.utc).strftime("%Y%m%dT%H%M%SZ") + "_" + os.urandom(3).hex()
|
|
20
|
+
flowsheet_hash = hashlib.sha256(
|
|
21
|
+
json.dumps(config, sort_keys=True, default=str).encode("utf-8")
|
|
22
|
+
).hexdigest()[:16]
|
|
23
|
+
manifest = fs.collect_outputs(
|
|
24
|
+
run_id, config.get("simulation", {}).get("mode", "steady"),
|
|
25
|
+
base_name, provenance=run_info.model_dump(),
|
|
26
|
+
)
|
|
27
|
+
manifest.snapshot_id = snapshot_id
|
|
28
|
+
stream_results = {k: v for k, v in results.items() if not hasattr(v, "fields")}
|
|
29
|
+
archive.save_run(manifest, stream_results=stream_results)
|
|
30
|
+
logger.info(f"Run saved : {run_id}")
|
|
31
|
+
return run_id
|
|
32
|
+
|
|
12
33
|
from ..eo import EOFlowsheet
|
|
13
34
|
from ..provenance import build_run_info
|
|
14
|
-
from ..result import save_results_zarr
|
|
15
|
-
from ..state import StateManager
|
|
16
35
|
from .common import (
|
|
17
36
|
build_divergence_report,
|
|
18
37
|
build_run_metadata,
|
|
@@ -127,15 +146,14 @@ def apply(
|
|
|
127
146
|
label="converged state",
|
|
128
147
|
)
|
|
129
148
|
run_info = build_run_info(config, x0=fs.x0, var_names=fs.var_names)
|
|
130
|
-
|
|
131
|
-
save_results_zarr(results, zarr_path, run_info=run_info)
|
|
149
|
+
saved_run_id = _persist_run(sm, fs, results, run_info, config, base_name, snapshot_id)
|
|
132
150
|
logger.info("=== Apply Summary ===")
|
|
133
151
|
logger.info(" Status : CONVERGED")
|
|
134
152
|
logger.info(f" Final ||F|| : {fs.solver_stats.get('final_norm', '?'):.3e}")
|
|
135
153
|
logger.info(f" Iterations : {fs.solver_stats.get('iterations', '?')}")
|
|
136
154
|
logger.info(f" Backend : {display_backend(config, fs.backend)}")
|
|
137
155
|
logger.info(f" Snapshot ID : {snapshot_id}")
|
|
138
|
-
logger.info(f"
|
|
156
|
+
logger.info(f" Run ID : {saved_run_id}")
|
|
139
157
|
logger.info(f" Elapsed (s) : {elapsed:.2f}")
|
|
140
158
|
return
|
|
141
159
|
|
|
@@ -168,11 +186,7 @@ def apply(
|
|
|
168
186
|
label="homotopy solution",
|
|
169
187
|
)
|
|
170
188
|
run_info = build_run_info(config, x0=fs.x0, var_names=fs.var_names)
|
|
171
|
-
|
|
172
|
-
results,
|
|
173
|
-
os.path.join(outputs_dir, f"{base_name}_results.zarr"),
|
|
174
|
-
run_info=run_info,
|
|
175
|
-
)
|
|
189
|
+
_persist_run(sm, fs, results, run_info, config, base_name, snapshot_id)
|
|
176
190
|
logger.info("Homotopy apply succeeded. New snapshot saved.")
|
|
177
191
|
return
|
|
178
192
|
|
|
@@ -15,7 +15,7 @@ from loguru import logger
|
|
|
15
15
|
|
|
16
16
|
from .. import __version__ as _pf_version
|
|
17
17
|
from ..utils.validate_flowsheet import validate_flowsheet
|
|
18
|
-
from ..
|
|
18
|
+
from ..persistence.archive import ProcessStateArchive
|
|
19
19
|
|
|
20
20
|
if TYPE_CHECKING:
|
|
21
21
|
from ..state import SnapshotState
|
|
@@ -133,12 +133,12 @@ def extract_providers(flowsheet_path: str) -> dict:
|
|
|
133
133
|
# State management
|
|
134
134
|
# ---------------------------------------------------------------------------
|
|
135
135
|
|
|
136
|
-
def load_state_manager(outputs_dir: str, base_name: str) -> tuple[
|
|
137
|
-
"""Create a
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
state =
|
|
141
|
-
return
|
|
136
|
+
def load_state_manager(outputs_dir: str, base_name: str) -> tuple[ProcessStateArchive, "SnapshotState | None"]:
|
|
137
|
+
"""Create a ProcessStateArchive and load the snapshot (if it exists)."""
|
|
138
|
+
archive_path = os.path.join(outputs_dir, f"{base_name}.pfarchive")
|
|
139
|
+
archive = ProcessStateArchive(archive_path)
|
|
140
|
+
state = archive.load_snapshot()
|
|
141
|
+
return archive, state
|
|
142
142
|
|
|
143
143
|
|
|
144
144
|
# ---------------------------------------------------------------------------
|
|
@@ -307,7 +307,7 @@ def write_divergence_report(outputs_dir: str, base_name: str, divergence: dict)
|
|
|
307
307
|
# ---------------------------------------------------------------------------
|
|
308
308
|
|
|
309
309
|
def save_snapshot(
|
|
310
|
-
|
|
310
|
+
archive: ProcessStateArchive,
|
|
311
311
|
config: dict,
|
|
312
312
|
x_converged: Any,
|
|
313
313
|
var_names: list[str],
|
|
@@ -318,7 +318,7 @@ def save_snapshot(
|
|
|
318
318
|
) -> str:
|
|
319
319
|
"""Save a state snapshot, logging a clear message on failure."""
|
|
320
320
|
try:
|
|
321
|
-
return
|
|
321
|
+
return archive.save_snapshot(
|
|
322
322
|
config, x_converged, var_names,
|
|
323
323
|
metadata=metadata,
|
|
324
324
|
parent_snapshot_id=parent_snapshot_id,
|
|
@@ -1,17 +1,17 @@
|
|
|
1
1
|
"""``pf run`` — run a process simulation from a flowsheet JSON file."""
|
|
2
|
-
|
|
3
2
|
from __future__ import annotations
|
|
4
3
|
|
|
4
|
+
import hashlib
|
|
5
5
|
import os
|
|
6
|
+
from datetime import datetime, timezone
|
|
6
7
|
|
|
7
8
|
import typer
|
|
8
9
|
from loguru import logger
|
|
9
10
|
|
|
10
|
-
from ..flowsheet import Flowsheet
|
|
11
11
|
from ..eo import EOFlowsheet
|
|
12
|
+
from ..flowsheet import Flowsheet
|
|
13
|
+
from ..persistence.archive import ProcessStateArchive
|
|
12
14
|
from ..provenance import build_dynamic_x0, build_run_info
|
|
13
|
-
from ..result import plot_results, plot_timeseries, save_results_zarr
|
|
14
|
-
from ..state import StateManager
|
|
15
15
|
from .common import (
|
|
16
16
|
check_providers,
|
|
17
17
|
display_backend,
|
|
@@ -22,6 +22,22 @@ from .common import (
|
|
|
22
22
|
)
|
|
23
23
|
|
|
24
24
|
|
|
25
|
+
def _flowsheet_hash(config: dict) -> str:
|
|
26
|
+
return hashlib.sha256(
|
|
27
|
+
json_dumps(config).encode("utf-8")
|
|
28
|
+
).hexdigest()[:16]
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def json_dumps(obj) -> str:
|
|
32
|
+
import json
|
|
33
|
+
|
|
34
|
+
return json.dumps(obj, sort_keys=True, default=str)
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def _run_id() -> str:
|
|
38
|
+
return datetime.now(timezone.utc).strftime("%Y%m%dT%H%M%SZ") + "_" + os.urandom(3).hex()
|
|
39
|
+
|
|
40
|
+
|
|
25
41
|
def run(
|
|
26
42
|
flowsheet: str = typer.Argument(help="Path to the flowsheet JSON file"),
|
|
27
43
|
export_images: bool = typer.Option(
|
|
@@ -44,13 +60,17 @@ def run(
|
|
|
44
60
|
mode = sim_cfg.get("mode", "steady")
|
|
45
61
|
is_dynamic = mode == "dynamic"
|
|
46
62
|
|
|
63
|
+
# Standardized run id, shared with container-side artifact uploads.
|
|
64
|
+
run_id = _run_id()
|
|
65
|
+
flowsheet_hash = _flowsheet_hash(config)
|
|
66
|
+
|
|
47
67
|
if is_dynamic:
|
|
48
|
-
# Load .
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
state =
|
|
68
|
+
# Load .pfarchive snapshot as t=0 if available.
|
|
69
|
+
archive_path = os.path.join(outputs_dir, f"{base_name}.pfarchive")
|
|
70
|
+
archive = ProcessStateArchive(archive_path)
|
|
71
|
+
state = archive.load_snapshot()
|
|
52
72
|
if state is not None:
|
|
53
|
-
stream_inits =
|
|
73
|
+
stream_inits = archive.state_to_stream_dicts(state)
|
|
54
74
|
streams_cfg = config.get("streams", {})
|
|
55
75
|
for s_name, s_vals in stream_inits.items():
|
|
56
76
|
if s_name in streams_cfg:
|
|
@@ -59,9 +79,9 @@ def run(
|
|
|
59
79
|
logger.debug(
|
|
60
80
|
f"Stream '{s_name}' from snapshot not in current flowsheet — skipped."
|
|
61
81
|
)
|
|
62
|
-
logger.info("Using .
|
|
82
|
+
logger.info("Using .pfarchive converged state as dynamic t=0.")
|
|
63
83
|
else:
|
|
64
|
-
logger.debug("No .
|
|
84
|
+
logger.debug("No .pfarchive found — starting dynamic run from flowsheet defaults.")
|
|
65
85
|
|
|
66
86
|
fs = Flowsheet(config)
|
|
67
87
|
logger.info("=== Dynamic Results ===")
|
|
@@ -94,20 +114,42 @@ def run(
|
|
|
94
114
|
|
|
95
115
|
run_info = build_run_info(config, x0=fs.x0, var_names=fs.var_names)
|
|
96
116
|
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
117
|
+
# Propagate run context to any containerized providers so their S3 uploads
|
|
118
|
+
# are keyed by the same run_id/flowsheet_hash as this archive run.
|
|
119
|
+
for provider in getattr(fs, "_provider_map", {}).values():
|
|
120
|
+
if hasattr(provider, "set_run_context"):
|
|
121
|
+
provider.set_run_context(run_id, flowsheet_hash)
|
|
122
|
+
|
|
123
|
+
# Build + persist the standardized run manifest.
|
|
124
|
+
archive_path = os.path.join(outputs_dir, f"{base_name}.pfarchive")
|
|
125
|
+
archive = ProcessStateArchive(archive_path)
|
|
126
|
+
manifest = fs.collect_outputs(run_id, mode, base_name, provenance=run_info.model_dump())
|
|
127
|
+
stream_results = {
|
|
128
|
+
k: v for k, v in results.items()
|
|
129
|
+
if not hasattr(v, "fields") # exclude EngineOutput objects
|
|
130
|
+
}
|
|
131
|
+
archive.save_run(manifest, stream_results=stream_results)
|
|
132
|
+
|
|
133
|
+
logger.info(f"Backend : {display_backend(config, getattr(fs, 'backend', 'dynamic'))}")
|
|
134
|
+
logger.info(f"Run saved : {os.path.join(archive_path, 'runs', run_id + '.json')}")
|
|
135
|
+
|
|
136
|
+
# Summarize standardized outputs.
|
|
137
|
+
for unit_name, out in manifest.units.items():
|
|
138
|
+
for f in out.fields:
|
|
139
|
+
logger.info(f" [{unit_name}] {f.name} = {f.quantity.value} {f.quantity.unit}")
|
|
140
|
+
for art in out.artifacts:
|
|
141
|
+
if art.remote_uris:
|
|
142
|
+
logger.info(f" [{unit_name}] artifact {art.name} → {art.remote_uris[0]}")
|
|
143
|
+
for stream_name, out in manifest.streams.items():
|
|
144
|
+
for f in out.fields:
|
|
145
|
+
logger.info(f" [{stream_name}] {f.name} = {f.quantity.value} {f.quantity.unit}")
|
|
106
146
|
|
|
107
147
|
if export_images:
|
|
108
148
|
try:
|
|
109
|
-
plot_results
|
|
110
|
-
|
|
149
|
+
from ..result import plot_results, plot_timeseries
|
|
150
|
+
|
|
151
|
+
plot_results(stream_results, fname=f"{base_name}_results.png")
|
|
152
|
+
plot_timeseries(stream_results, fname=f"{base_name}_timeseries.png")
|
|
111
153
|
logger.info(f"Plots saved: {base_name}_results.png, {base_name}_timeseries.png")
|
|
112
154
|
except Exception as e:
|
|
113
155
|
logger.warning(f"Failed to generate plots: {type(e).__name__}: {e}")
|
|
@@ -12,7 +12,7 @@ if TYPE_CHECKING:
|
|
|
12
12
|
|
|
13
13
|
from .jacobian import GlobalJacobianManager
|
|
14
14
|
from .solver import EOSolver
|
|
15
|
-
from ..types import SnapshotState
|
|
15
|
+
from ..types import EngineOutput, SnapshotState
|
|
16
16
|
from ..utils.topology import get_inlets, get_outlets
|
|
17
17
|
|
|
18
18
|
|
|
@@ -70,6 +70,7 @@ class EOFlowsheet:
|
|
|
70
70
|
"""
|
|
71
71
|
from processforge.providers.manager import teardown_providers
|
|
72
72
|
manager = self._build()
|
|
73
|
+
self.engine_outputs: dict = {}
|
|
73
74
|
try:
|
|
74
75
|
x0 = self._warm_start(manager)
|
|
75
76
|
self.x0: "np.ndarray" = x0.copy()
|
|
@@ -102,12 +103,46 @@ class EOFlowsheet:
|
|
|
102
103
|
logger.info(f"EOFlowsheet: running standalone SolverUnit '{unit_name}'")
|
|
103
104
|
unit_result = unit._run_impl({})
|
|
104
105
|
results[unit_name] = unit_result
|
|
106
|
+
self.engine_outputs[unit_name] = unit_result
|
|
105
107
|
|
|
106
108
|
logger.info("EOFlowsheet: simulation complete.")
|
|
109
|
+
self.results = results
|
|
107
110
|
return results
|
|
108
111
|
finally:
|
|
109
112
|
teardown_providers(self._provider_map)
|
|
110
113
|
|
|
114
|
+
# ------------------------------------------------------------------
|
|
115
|
+
# Standardized output collection
|
|
116
|
+
# ------------------------------------------------------------------
|
|
117
|
+
def _thermo_provider(self):
|
|
118
|
+
"""Return the first provider capable of stream thermo properties."""
|
|
119
|
+
provider_map = getattr(self, "_provider_map", None) or {}
|
|
120
|
+
for provider in provider_map.values():
|
|
121
|
+
name = type(provider).__name__
|
|
122
|
+
if "Container" in name:
|
|
123
|
+
continue
|
|
124
|
+
if callable(getattr(provider, "get_thermo_properties", None)):
|
|
125
|
+
return provider
|
|
126
|
+
return None
|
|
127
|
+
|
|
128
|
+
def collect_outputs(self, run_id: str, mode: str, flowsheet_name: str, provenance: dict | None = None):
|
|
129
|
+
"""Assemble a :class:`RunManifest` from this run's results."""
|
|
130
|
+
from ..output_collector import build_run_manifest
|
|
131
|
+
|
|
132
|
+
stream_results = {
|
|
133
|
+
k: v for k, v in getattr(self, "results", {}).items()
|
|
134
|
+
if not isinstance(v, EngineOutput)
|
|
135
|
+
}
|
|
136
|
+
return build_run_manifest(
|
|
137
|
+
run_id=run_id,
|
|
138
|
+
mode=mode,
|
|
139
|
+
flowsheet_name=flowsheet_name,
|
|
140
|
+
provenance=provenance or {},
|
|
141
|
+
engine_outputs=getattr(self, "engine_outputs", {}),
|
|
142
|
+
stream_results=stream_results,
|
|
143
|
+
thermo_provider=self._thermo_provider(),
|
|
144
|
+
)
|
|
145
|
+
|
|
111
146
|
# ------------------------------------------------------------------
|
|
112
147
|
# Build phase
|
|
113
148
|
# ------------------------------------------------------------------
|
|
@@ -332,12 +332,16 @@ class GlobalJacobianManager:
|
|
|
332
332
|
return {}
|
|
333
333
|
return self.scalars_from_results(self._results)
|
|
334
334
|
|
|
335
|
-
def scalars_from_results(self, results: dict
|
|
336
|
-
"""Extract
|
|
335
|
+
def scalars_from_results(self, results: dict) -> dict:
|
|
336
|
+
"""Extract solver-unit outputs from a results dict.
|
|
337
|
+
|
|
338
|
+
Solver-unit results are :class:`~processforge.types.EngineOutput`
|
|
339
|
+
instances; stream entries are plain dicts and are ignored here.
|
|
340
|
+
"""
|
|
341
|
+
from processforge.types import EngineOutput
|
|
342
|
+
|
|
337
343
|
return {
|
|
338
|
-
k: v
|
|
339
|
-
for k, v in results.items()
|
|
340
|
-
if isinstance(v, dict) and "status" in v
|
|
344
|
+
k: v for k, v in results.items() if isinstance(v, EngineOutput)
|
|
341
345
|
}
|
|
342
346
|
|
|
343
347
|
# ------------------------------------------------------------------
|