processforge 0.2.3__tar.gz → 0.2.5__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.3/src/processforge.egg-info → processforge-0.2.5}/PKG-INFO +1 -1
- {processforge-0.2.3 → processforge-0.2.5}/pyproject.toml +1 -1
- {processforge-0.2.3 → processforge-0.2.5}/src/processforge/__init__.py +2 -0
- {processforge-0.2.3 → processforge-0.2.5}/src/processforge/eo/flowsheet.py +28 -0
- processforge-0.2.5/src/processforge/provenance.py +123 -0
- {processforge-0.2.3 → processforge-0.2.5}/src/processforge/result.py +60 -3
- {processforge-0.2.3 → processforge-0.2.5}/src/processforge/simulate.py +7 -1
- {processforge-0.2.3 → processforge-0.2.5}/src/processforge/units/pump.py +1 -1
- {processforge-0.2.3 → processforge-0.2.5/src/processforge.egg-info}/PKG-INFO +1 -1
- {processforge-0.2.3 → processforge-0.2.5}/src/processforge.egg-info/SOURCES.txt +1 -0
- {processforge-0.2.3 → processforge-0.2.5}/LICENSE +0 -0
- {processforge-0.2.3 → processforge-0.2.5}/MANIFEST.in +0 -0
- {processforge-0.2.3 → processforge-0.2.5}/README.md +0 -0
- {processforge-0.2.3 → processforge-0.2.5}/flowsheets/archive/example_dynamic_hybrid.json +0 -0
- {processforge-0.2.3 → processforge-0.2.5}/flowsheets/archive/example_dynamic_tank.json +0 -0
- {processforge-0.2.3 → processforge-0.2.5}/flowsheets/archive/example_flash.json +0 -0
- {processforge-0.2.3 → processforge-0.2.5}/flowsheets/archive/hydraulic_chain.json +0 -0
- {processforge-0.2.3 → processforge-0.2.5}/flowsheets/closed-loop-chain.json +0 -0
- {processforge-0.2.3 → processforge-0.2.5}/flowsheets/hydraulic-chain.json +0 -0
- {processforge-0.2.3 → processforge-0.2.5}/setup.cfg +0 -0
- {processforge-0.2.3 → processforge-0.2.5}/src/processforge/_schema.py +0 -0
- {processforge-0.2.3 → processforge-0.2.5}/src/processforge/eo/__init__.py +0 -0
- {processforge-0.2.3 → processforge-0.2.5}/src/processforge/eo/backends/__init__.py +0 -0
- {processforge-0.2.3 → processforge-0.2.5}/src/processforge/eo/backends/base.py +0 -0
- {processforge-0.2.3 → processforge-0.2.5}/src/processforge/eo/backends/casadi_backend.py +0 -0
- {processforge-0.2.3 → processforge-0.2.5}/src/processforge/eo/backends/pyomo_backend.py +0 -0
- {processforge-0.2.3 → processforge-0.2.5}/src/processforge/eo/backends/scipy_backend.py +0 -0
- {processforge-0.2.3 → processforge-0.2.5}/src/processforge/eo/jacobian.py +0 -0
- {processforge-0.2.3 → processforge-0.2.5}/src/processforge/eo/mixin.py +0 -0
- {processforge-0.2.3 → processforge-0.2.5}/src/processforge/eo/solver.py +0 -0
- {processforge-0.2.3 → processforge-0.2.5}/src/processforge/eo/stream_var.py +0 -0
- {processforge-0.2.3 → processforge-0.2.5}/src/processforge/eo/units/__init__.py +0 -0
- {processforge-0.2.3 → processforge-0.2.5}/src/processforge/eo/units/flash_eo.py +0 -0
- {processforge-0.2.3 → processforge-0.2.5}/src/processforge/eo/units/heater_eo.py +0 -0
- {processforge-0.2.3 → processforge-0.2.5}/src/processforge/eo/units/pipes_eo.py +0 -0
- {processforge-0.2.3 → processforge-0.2.5}/src/processforge/eo/units/pump_eo.py +0 -0
- {processforge-0.2.3 → processforge-0.2.5}/src/processforge/eo/units/strainer_eo.py +0 -0
- {processforge-0.2.3 → processforge-0.2.5}/src/processforge/eo/units/valve_eo.py +0 -0
- {processforge-0.2.3 → processforge-0.2.5}/src/processforge/flowsheet.py +0 -0
- {processforge-0.2.3 → processforge-0.2.5}/src/processforge/schemas/__init__.py +0 -0
- {processforge-0.2.3 → processforge-0.2.5}/src/processforge/schemas/flowsheet_schema.json +0 -0
- {processforge-0.2.3 → processforge-0.2.5}/src/processforge/solver.py +0 -0
- {processforge-0.2.3 → processforge-0.2.5}/src/processforge/thermo.py +0 -0
- {processforge-0.2.3 → processforge-0.2.5}/src/processforge/units/__init__.py +0 -0
- {processforge-0.2.3 → processforge-0.2.5}/src/processforge/units/flash.py +0 -0
- {processforge-0.2.3 → processforge-0.2.5}/src/processforge/units/heater.py +0 -0
- {processforge-0.2.3 → processforge-0.2.5}/src/processforge/units/pipes.py +0 -0
- {processforge-0.2.3 → processforge-0.2.5}/src/processforge/units/solver.py +0 -0
- {processforge-0.2.3 → processforge-0.2.5}/src/processforge/units/strainer.py +0 -0
- {processforge-0.2.3 → processforge-0.2.5}/src/processforge/units/tank.py +0 -0
- {processforge-0.2.3 → processforge-0.2.5}/src/processforge/units/valve.py +0 -0
- {processforge-0.2.3 → processforge-0.2.5}/src/processforge/utils/__init__.py +0 -0
- {processforge-0.2.3 → processforge-0.2.5}/src/processforge/utils/flowsheet_diagram.py +0 -0
- {processforge-0.2.3 → processforge-0.2.5}/src/processforge/utils/validate_flowsheet.py +0 -0
- {processforge-0.2.3 → processforge-0.2.5}/src/processforge/utils/validation.py +0 -0
- {processforge-0.2.3 → processforge-0.2.5}/src/processforge/validate.py +0 -0
- {processforge-0.2.3 → processforge-0.2.5}/src/processforge.egg-info/dependency_links.txt +0 -0
- {processforge-0.2.3 → processforge-0.2.5}/src/processforge.egg-info/entry_points.txt +0 -0
- {processforge-0.2.3 → processforge-0.2.5}/src/processforge.egg-info/requires.txt +0 -0
- {processforge-0.2.3 → processforge-0.2.5}/src/processforge.egg-info/top_level.txt +0 -0
|
@@ -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.5"
|
|
8
8
|
description = "A Python-based process simulation framework for chemical engineering applications."
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
license = "BSD-3-Clause"
|
|
@@ -11,6 +11,7 @@ Provides steady-state and dynamic process simulation capabilities including:
|
|
|
11
11
|
"""
|
|
12
12
|
|
|
13
13
|
from .flowsheet import Flowsheet
|
|
14
|
+
from .provenance import build_run_info
|
|
14
15
|
from .solver import Solver
|
|
15
16
|
from .thermo import get_enthalpy_molar, get_Cp_molar, get_K_values, rachford_rice
|
|
16
17
|
from .validate import validate_flowsheet
|
|
@@ -45,6 +46,7 @@ __all__ = [
|
|
|
45
46
|
"plot_results",
|
|
46
47
|
"plot_timeseries",
|
|
47
48
|
"save_results_zarr",
|
|
49
|
+
"build_run_info",
|
|
48
50
|
"Pump",
|
|
49
51
|
"Valve",
|
|
50
52
|
"Strainer",
|
|
@@ -3,6 +3,7 @@ from __future__ import annotations
|
|
|
3
3
|
|
|
4
4
|
from copy import deepcopy
|
|
5
5
|
|
|
6
|
+
import numpy as np
|
|
6
7
|
from loguru import logger
|
|
7
8
|
|
|
8
9
|
from .jacobian import GlobalJacobianManager
|
|
@@ -64,12 +65,19 @@ class EOFlowsheet:
|
|
|
64
65
|
"""
|
|
65
66
|
Build and solve the EO system.
|
|
66
67
|
|
|
68
|
+
After this method returns, ``self.x0`` holds the initial-guess vector
|
|
69
|
+
and ``self.var_names`` holds a human-readable label for each element —
|
|
70
|
+
both can be passed directly to
|
|
71
|
+
:func:`processforge.provenance.build_run_info` for reproducibility.
|
|
72
|
+
|
|
67
73
|
Returns:
|
|
68
74
|
Stream result dict in the same format as ``Flowsheet.run()``:
|
|
69
75
|
``{stream_name: {"T": ..., "P": ..., "flowrate": ..., "z": {...}}}``
|
|
70
76
|
"""
|
|
71
77
|
manager = self._build()
|
|
72
78
|
x0 = self._warm_start(manager)
|
|
79
|
+
self.x0: "np.ndarray" = x0.copy()
|
|
80
|
+
self.var_names: list[str] = self._build_var_names(manager)
|
|
73
81
|
solver = EOSolver(backend=self.backend)
|
|
74
82
|
x_sol, converged, stats = solver.solve(manager, x0)
|
|
75
83
|
if not converged:
|
|
@@ -172,6 +180,26 @@ class EOFlowsheet:
|
|
|
172
180
|
comps.update(stream.get("z", {}).keys())
|
|
173
181
|
return sorted(comps)
|
|
174
182
|
|
|
183
|
+
# ------------------------------------------------------------------
|
|
184
|
+
# Provenance helpers
|
|
185
|
+
# ------------------------------------------------------------------
|
|
186
|
+
|
|
187
|
+
def _build_var_names(self, manager: "GlobalJacobianManager") -> list[str]:
|
|
188
|
+
"""Return a human-readable label for every scalar in the x vector.
|
|
189
|
+
|
|
190
|
+
Variable order mirrors ``GlobalJacobianManager`` layout:
|
|
191
|
+
``T``, ``P``, ``flowrate``, then one entry per component ``z_<comp>``
|
|
192
|
+
for each registered stream in registration order.
|
|
193
|
+
"""
|
|
194
|
+
suffixes = ["T", "P", "flowrate"] + [
|
|
195
|
+
f"z_{c}" for c in manager.components
|
|
196
|
+
]
|
|
197
|
+
names: list[str] = []
|
|
198
|
+
for stream_name in manager._streams:
|
|
199
|
+
for suffix in suffixes:
|
|
200
|
+
names.append(f"{stream_name}/{suffix}")
|
|
201
|
+
return names
|
|
202
|
+
|
|
175
203
|
# ------------------------------------------------------------------
|
|
176
204
|
# Warm-start
|
|
177
205
|
# ------------------------------------------------------------------
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
"""Provenance and metadata utilities for ProcessForge runs."""
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
|
|
4
|
+
import platform
|
|
5
|
+
import subprocess
|
|
6
|
+
import sys
|
|
7
|
+
from datetime import datetime, timezone
|
|
8
|
+
|
|
9
|
+
import numpy as np
|
|
10
|
+
|
|
11
|
+
_KEY_PACKAGES = [
|
|
12
|
+
"numpy",
|
|
13
|
+
"scipy",
|
|
14
|
+
"zarr",
|
|
15
|
+
"coolprop",
|
|
16
|
+
"loguru",
|
|
17
|
+
"pandas",
|
|
18
|
+
"openpyxl",
|
|
19
|
+
"matplotlib",
|
|
20
|
+
"jsonschema",
|
|
21
|
+
"graphviz",
|
|
22
|
+
]
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def _git_hash() -> str:
|
|
26
|
+
"""Return the current git commit hash, or 'unknown' if not in a git repo."""
|
|
27
|
+
try:
|
|
28
|
+
result = subprocess.run(
|
|
29
|
+
["git", "rev-parse", "HEAD"],
|
|
30
|
+
capture_output=True,
|
|
31
|
+
text=True,
|
|
32
|
+
timeout=5,
|
|
33
|
+
)
|
|
34
|
+
if result.returncode == 0:
|
|
35
|
+
return result.stdout.strip()
|
|
36
|
+
except Exception:
|
|
37
|
+
pass
|
|
38
|
+
return "unknown"
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def _package_versions(packages: list[str]) -> dict[str, str]:
|
|
42
|
+
"""Return installed versions for each package (or 'unknown')."""
|
|
43
|
+
from importlib.metadata import PackageNotFoundError, version
|
|
44
|
+
|
|
45
|
+
versions: dict[str, str] = {}
|
|
46
|
+
for pkg in packages:
|
|
47
|
+
try:
|
|
48
|
+
versions[pkg] = version(pkg)
|
|
49
|
+
except PackageNotFoundError:
|
|
50
|
+
versions[pkg] = "unknown"
|
|
51
|
+
return versions
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def build_run_info(
|
|
55
|
+
config: dict,
|
|
56
|
+
x0: np.ndarray | None = None,
|
|
57
|
+
var_names: list[str] | None = None,
|
|
58
|
+
) -> dict:
|
|
59
|
+
"""Build a run_info metadata dict for provenance tracking.
|
|
60
|
+
|
|
61
|
+
Args:
|
|
62
|
+
config: The validated flowsheet configuration dict.
|
|
63
|
+
x0: Initial guess vector (EO mode) or flattened feed-stream
|
|
64
|
+
initial conditions (dynamic mode).
|
|
65
|
+
var_names: Human-readable label for each element of x0 (e.g.
|
|
66
|
+
``"feed/T"``, ``"feed/P"``, ``"product/z_H2O"``).
|
|
67
|
+
|
|
68
|
+
Returns:
|
|
69
|
+
A dict suitable for passing to ``save_results_zarr`` as *run_info*.
|
|
70
|
+
Keys:
|
|
71
|
+
- ``git_hash`` – current HEAD commit SHA
|
|
72
|
+
- ``timestamp`` – ISO-8601 UTC timestamp
|
|
73
|
+
- ``python_version`` – full ``sys.version`` string
|
|
74
|
+
- ``platform`` – ``platform.platform()`` string
|
|
75
|
+
- ``processforge_version`` – installed processforge version
|
|
76
|
+
- ``mode`` – ``"steady"`` or ``"dynamic"``
|
|
77
|
+
- ``backend`` – EO backend name (steady-state only)
|
|
78
|
+
- ``pkg_versions`` – dict of {package: version}
|
|
79
|
+
- ``x0`` – numpy float64 array or ``None``
|
|
80
|
+
- ``var_names`` – list of str labels or ``None``
|
|
81
|
+
"""
|
|
82
|
+
from . import __version__ as _pf_version
|
|
83
|
+
|
|
84
|
+
sim_cfg = config.get("simulation", {})
|
|
85
|
+
return {
|
|
86
|
+
"git_hash": _git_hash(),
|
|
87
|
+
"timestamp": datetime.now(timezone.utc).isoformat(),
|
|
88
|
+
"python_version": sys.version,
|
|
89
|
+
"platform": platform.platform(),
|
|
90
|
+
"processforge_version": _pf_version,
|
|
91
|
+
"mode": sim_cfg.get("mode", "steady"),
|
|
92
|
+
"backend": sim_cfg.get("backend", "scipy"),
|
|
93
|
+
"pkg_versions": _package_versions(_KEY_PACKAGES),
|
|
94
|
+
"x0": np.asarray(x0, dtype=float) if x0 is not None else None,
|
|
95
|
+
"var_names": list(var_names) if var_names is not None else None,
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
def build_dynamic_x0(config: dict) -> tuple[np.ndarray, list[str]]:
|
|
100
|
+
"""Flatten feed-stream initial conditions into a reproducibility vector.
|
|
101
|
+
|
|
102
|
+
For dynamic simulations there is no single ``x0`` passed to a global
|
|
103
|
+
solver; instead the starting state is fully determined by the feed streams
|
|
104
|
+
in the config. This helper packs those values into a flat numpy array so
|
|
105
|
+
the same schema used for EO runs can be applied here.
|
|
106
|
+
|
|
107
|
+
Returns:
|
|
108
|
+
(x0_array, var_names) where ``x0_array[i]`` corresponds to
|
|
109
|
+
``var_names[i]`` (e.g. ``"feed/T"``, ``"feed/z_H2O"``).
|
|
110
|
+
"""
|
|
111
|
+
values: list[float] = []
|
|
112
|
+
names: list[str] = []
|
|
113
|
+
|
|
114
|
+
for stream_name, stream in config.get("streams", {}).items():
|
|
115
|
+
for prop in ("T", "P", "flowrate"):
|
|
116
|
+
val = stream.get(prop, 0.0)
|
|
117
|
+
values.append(float(val))
|
|
118
|
+
names.append(f"{stream_name}/{prop}")
|
|
119
|
+
for comp, frac in sorted(stream.get("z", {}).items()):
|
|
120
|
+
values.append(float(frac))
|
|
121
|
+
names.append(f"{stream_name}/z_{comp}")
|
|
122
|
+
|
|
123
|
+
return np.asarray(values, dtype=float), names
|
|
@@ -39,8 +39,63 @@ def _store_stream(stream_group, stream_data):
|
|
|
39
39
|
stream_group.create_dataset(key, data=arr, shape=arr.shape)
|
|
40
40
|
|
|
41
41
|
|
|
42
|
-
def
|
|
43
|
-
"""Persist
|
|
42
|
+
def _store_run_info(root, run_info: dict) -> None:
|
|
43
|
+
"""Persist provenance metadata in a ``run_info`` Zarr sub-group.
|
|
44
|
+
|
|
45
|
+
Layout::
|
|
46
|
+
|
|
47
|
+
run_info/
|
|
48
|
+
.attrs → git_hash, timestamp, python_version, platform,
|
|
49
|
+
processforge_version, mode, backend
|
|
50
|
+
pkg_versions/
|
|
51
|
+
.attrs → {package: version, ...}
|
|
52
|
+
initial_guess/
|
|
53
|
+
x0 – float64 array (length = n_vars)
|
|
54
|
+
.attrs → var_names (JSON list of string labels)
|
|
55
|
+
"""
|
|
56
|
+
ri_group = root.create_group("run_info")
|
|
57
|
+
|
|
58
|
+
scalar_keys = [
|
|
59
|
+
"git_hash",
|
|
60
|
+
"timestamp",
|
|
61
|
+
"python_version",
|
|
62
|
+
"platform",
|
|
63
|
+
"processforge_version",
|
|
64
|
+
"mode",
|
|
65
|
+
"backend",
|
|
66
|
+
]
|
|
67
|
+
ri_group.attrs.update(
|
|
68
|
+
{k: str(run_info[k]) for k in scalar_keys if k in run_info}
|
|
69
|
+
)
|
|
70
|
+
|
|
71
|
+
pkg_versions = run_info.get("pkg_versions") or {}
|
|
72
|
+
if pkg_versions:
|
|
73
|
+
pkg_group = ri_group.create_group("pkg_versions")
|
|
74
|
+
pkg_group.attrs.update({k: str(v) for k, v in pkg_versions.items()})
|
|
75
|
+
|
|
76
|
+
x0 = run_info.get("x0")
|
|
77
|
+
if x0 is not None:
|
|
78
|
+
x0_arr = np.asarray(x0, dtype=float)
|
|
79
|
+
ig_group = ri_group.create_group("initial_guess")
|
|
80
|
+
ig_group.create_dataset("x0", data=x0_arr, shape=x0_arr.shape)
|
|
81
|
+
var_names = run_info.get("var_names")
|
|
82
|
+
if var_names:
|
|
83
|
+
ig_group.attrs["var_names"] = list(var_names)
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
def save_results_zarr(results, fname="results.zarr", run_info=None):
|
|
87
|
+
"""Persist simulation results in a Zarr directory.
|
|
88
|
+
|
|
89
|
+
Args:
|
|
90
|
+
results: Stream result dict as returned by ``Flowsheet.run()``
|
|
91
|
+
or ``EOFlowsheet.run()``.
|
|
92
|
+
fname: Output path for the Zarr store directory.
|
|
93
|
+
run_info: Optional provenance dict from
|
|
94
|
+
:func:`processforge.provenance.build_run_info`. When
|
|
95
|
+
supplied, a ``run_info`` sub-group is written inside the
|
|
96
|
+
store containing the git hash, package versions, and
|
|
97
|
+
initial-guess vector for full reproducibility.
|
|
98
|
+
"""
|
|
44
99
|
store_path = os.path.abspath(fname)
|
|
45
100
|
if os.path.exists(store_path):
|
|
46
101
|
if os.path.isdir(store_path):
|
|
@@ -55,6 +110,8 @@ def save_results_zarr(results, fname="results.zarr"):
|
|
|
55
110
|
for stream_name, stream_data in results.items():
|
|
56
111
|
stream_group = root.create_group(stream_name)
|
|
57
112
|
_store_stream(stream_group, stream_data)
|
|
113
|
+
if run_info is not None:
|
|
114
|
+
_store_run_info(root, run_info)
|
|
58
115
|
logger.info(f"Saved Zarr results to {store_path}")
|
|
59
116
|
return store_path
|
|
60
117
|
|
|
@@ -190,7 +247,7 @@ def _build_dataframe_row(group, stream_name, idx, comp_names, include_time):
|
|
|
190
247
|
def _load_dataframe_from_zarr(store_path):
|
|
191
248
|
store = zarr.storage.LocalStore(store_path)
|
|
192
249
|
root = zarr.open(store=store, mode="r")
|
|
193
|
-
streams = sorted(root.group_keys())
|
|
250
|
+
streams = sorted(k for k in root.group_keys() if k != "run_info")
|
|
194
251
|
rows = []
|
|
195
252
|
components = set()
|
|
196
253
|
mode = root.attrs.get("mode", "steady")
|
|
@@ -7,6 +7,7 @@ from loguru import logger
|
|
|
7
7
|
from .utils.validate_flowsheet import validate_flowsheet
|
|
8
8
|
from .flowsheet import Flowsheet
|
|
9
9
|
from .eo import EOFlowsheet
|
|
10
|
+
from .provenance import build_dynamic_x0, build_run_info
|
|
10
11
|
from .result import (
|
|
11
12
|
generate_validation_excel,
|
|
12
13
|
plot_results,
|
|
@@ -40,15 +41,20 @@ def _cmd_run(args):
|
|
|
40
41
|
if is_dynamic:
|
|
41
42
|
fs = Flowsheet(config)
|
|
42
43
|
logger.info("=== Dynamic Results ===")
|
|
44
|
+
results = fs.run()
|
|
45
|
+
x0, var_names = build_dynamic_x0(config)
|
|
46
|
+
run_info = build_run_info(config, x0=x0, var_names=var_names)
|
|
43
47
|
else:
|
|
44
48
|
backend = sim_cfg.get("backend", "scipy")
|
|
45
49
|
fs = EOFlowsheet(config, backend=backend)
|
|
46
50
|
logger.info("=== Steady-State EO Results ===")
|
|
51
|
+
results = fs.run()
|
|
52
|
+
run_info = build_run_info(config, x0=fs.x0, var_names=fs.var_names)
|
|
47
53
|
|
|
48
|
-
results = fs.run()
|
|
49
54
|
zarr_path = save_results_zarr(
|
|
50
55
|
results,
|
|
51
56
|
os.path.join("outputs", f"{base_name}_results.zarr"),
|
|
57
|
+
run_info=run_info,
|
|
52
58
|
)
|
|
53
59
|
validation_path = os.path.join("outputs", f"{base_name}_validation.xlsx")
|
|
54
60
|
generate_validation_excel(
|
|
@@ -49,7 +49,7 @@ class Pump(PumpEOMixin):
|
|
|
49
49
|
|
|
50
50
|
# Approximate temperature rise (adiabatic inefficiency)
|
|
51
51
|
Cp = 4180.0 # J/kg-K for water
|
|
52
|
-
dT = (power * (1 - self.efficiency)) / (mass_flow * Cp)
|
|
52
|
+
dT = (power * (1 - self.efficiency)) / (mass_flow * Cp) if mass_flow > 0 else 0.0
|
|
53
53
|
outlet["T"] = inlet["T"] + dT
|
|
54
54
|
|
|
55
55
|
outlet["power"] = power
|
|
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
|