OpenPinch 0.2.2__tar.gz → 0.2.4__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.
- {openpinch-0.2.2 → openpinch-0.2.4}/OpenPinch/__init__.py +2 -0
- {openpinch-0.2.2 → openpinch-0.2.4}/OpenPinch/classes/_problem/__init__.py +2 -0
- openpinch-0.2.4/OpenPinch/classes/_problem/_component_accessor.py +54 -0
- {openpinch-0.2.2 → openpinch-0.2.4}/OpenPinch/classes/_problem/_plot_accessor.py +20 -0
- {openpinch-0.2.2 → openpinch-0.2.4}/OpenPinch/classes/_problem/_target_accessor.py +25 -1
- {openpinch-0.2.2 → openpinch-0.2.4}/OpenPinch/classes/_problem/_target_dispatch.py +19 -16
- {openpinch-0.2.2 → openpinch-0.2.4}/OpenPinch/classes/_problem/_validation.py +62 -64
- openpinch-0.2.4/OpenPinch/classes/_stream_collection/_numeric_view.py +81 -0
- {openpinch-0.2.2 → openpinch-0.2.4}/OpenPinch/classes/pinch_problem.py +69 -1
- openpinch-0.2.4/OpenPinch/classes/process_components.py +45 -0
- {openpinch-0.2.2 → openpinch-0.2.4}/OpenPinch/classes/stream.py +117 -62
- {openpinch-0.2.2 → openpinch-0.2.4}/OpenPinch/classes/stream_collection.py +45 -1
- {openpinch-0.2.2 → openpinch-0.2.4}/OpenPinch/classes/value.py +51 -23
- {openpinch-0.2.2 → openpinch-0.2.4}/OpenPinch/classes/zone.py +12 -3
- openpinch-0.2.4/OpenPinch/data/notebooks/06_energy_transfer_analysis.ipynb +255 -0
- openpinch-0.2.4/OpenPinch/data/notebooks/07_vapour_compression_mvr_cascade_hpr.ipynb +400 -0
- openpinch-0.2.4/OpenPinch/data/notebooks/08_direct_gas_stream_mvr.ipynb +439 -0
- {openpinch-0.2.2 → openpinch-0.2.4}/OpenPinch/lib/__init__.py +4 -0
- {openpinch-0.2.2 → openpinch-0.2.4}/OpenPinch/lib/config.py +1 -1
- {openpinch-0.2.2 → openpinch-0.2.4}/OpenPinch/lib/config_metadata.py +3 -0
- openpinch-0.2.4/OpenPinch/lib/coolprop_fluids.py +63 -0
- {openpinch-0.2.2 → openpinch-0.2.4}/OpenPinch/lib/enums.py +30 -1
- {openpinch-0.2.2 → openpinch-0.2.4}/OpenPinch/lib/schemas/__init__.py +2 -0
- {openpinch-0.2.2 → openpinch-0.2.4}/OpenPinch/lib/schemas/hpr.py +5 -1
- {openpinch-0.2.2 → openpinch-0.2.4}/OpenPinch/lib/schemas/io.py +61 -3
- {openpinch-0.2.2 → openpinch-0.2.4}/OpenPinch/lib/schemas/reporting.py +2 -0
- {openpinch-0.2.2 → openpinch-0.2.4}/OpenPinch/lib/schemas/targets.py +18 -0
- {openpinch-0.2.2 → openpinch-0.2.4}/OpenPinch/lib/unit_system.py +4 -0
- {openpinch-0.2.2 → openpinch-0.2.4}/OpenPinch/services/__init__.py +6 -0
- {openpinch-0.2.2 → openpinch-0.2.4}/OpenPinch/services/common/capital_cost_and_area_targeting.py +73 -22
- {openpinch-0.2.2 → openpinch-0.2.4}/OpenPinch/services/common/gcc_manipulation.py +2 -4
- {openpinch-0.2.2 → openpinch-0.2.4}/OpenPinch/services/common/graph_data.py +35 -0
- {openpinch-0.2.2 → openpinch-0.2.4}/OpenPinch/services/common/problem_table_analysis.py +67 -49
- openpinch-0.2.4/OpenPinch/services/common/service_orchestration.py +64 -0
- {openpinch-0.2.2 → openpinch-0.2.4}/OpenPinch/services/common/utility_targeting.py +20 -12
- openpinch-0.2.4/OpenPinch/services/components/__init__.py +15 -0
- openpinch-0.2.4/OpenPinch/services/components/direct_mvr/__init__.py +21 -0
- openpinch-0.2.4/OpenPinch/services/components/direct_mvr/direct_gas_mvr.py +653 -0
- openpinch-0.2.4/OpenPinch/services/components/process_mvr.py +681 -0
- {openpinch-0.2.2 → openpinch-0.2.4}/OpenPinch/services/direct_heat_integration/direct_integration_entry.py +58 -66
- openpinch-0.2.4/OpenPinch/services/energy_transfer_analysis/__init__.py +15 -0
- openpinch-0.2.4/OpenPinch/services/energy_transfer_analysis/energy_transfer_entry.py +731 -0
- {openpinch-0.2.2 → openpinch-0.2.4}/OpenPinch/services/exergy_analysis/__init__.py +4 -0
- {openpinch-0.2.2 → openpinch-0.2.4}/OpenPinch/services/exergy_analysis/exergy_targeting_entry.py +102 -0
- {openpinch-0.2.2 → openpinch-0.2.4}/OpenPinch/services/heat_pump_integration/common/preprocessing.py +4 -0
- {openpinch-0.2.2 → openpinch-0.2.4}/OpenPinch/services/heat_pump_integration/common/shared.py +82 -13
- {openpinch-0.2.2 → openpinch-0.2.4}/OpenPinch/services/heat_pump_integration/heat_pump_and_refrigeration_entry.py +4 -0
- {openpinch-0.2.2 → openpinch-0.2.4}/OpenPinch/services/heat_pump_integration/targeting_services/cascade_vapour_compression.py +15 -8
- {openpinch-0.2.2 → openpinch-0.2.4}/OpenPinch/services/heat_pump_integration/targeting_services/multi_simple_vapour_compression.py +22 -5
- {openpinch-0.2.2 → openpinch-0.2.4}/OpenPinch/services/heat_pump_integration/targeting_services/multi_temperature_carnot.py +1 -1
- openpinch-0.2.4/OpenPinch/services/heat_pump_integration/targeting_services/vapour_compression_mvr.py +444 -0
- openpinch-0.2.4/OpenPinch/services/heat_pump_integration/unit_models/__init__.py +9 -0
- openpinch-0.2.4/OpenPinch/services/heat_pump_integration/unit_models/mechanical_vapour_recompression_cycle.py +716 -0
- {openpinch-0.2.2 → openpinch-0.2.4}/OpenPinch/services/heat_pump_integration/unit_models/vapour_compression_cycle.py +3 -42
- openpinch-0.2.4/OpenPinch/services/heat_pump_integration/unit_models/vapour_compression_mvr_cascade.py +504 -0
- {openpinch-0.2.2 → openpinch-0.2.4}/OpenPinch/services/indirect_heat_integration/indirect_integration_entry.py +2 -2
- {openpinch-0.2.2 → openpinch-0.2.4}/OpenPinch/services/input_data_processing/_utility_preparation.py +61 -45
- {openpinch-0.2.2 → openpinch-0.2.4}/OpenPinch/services/input_data_processing/data_preparation.py +33 -35
- {openpinch-0.2.2 → openpinch-0.2.4}/OpenPinch/services/power_cogeneration/__init__.py +3 -1
- openpinch-0.2.2/OpenPinch/services/power_cogeneration/power_cogeneration_analysis.py → openpinch-0.2.4/OpenPinch/services/power_cogeneration/power_cogeneration_entry.py +123 -1
- openpinch-0.2.4/OpenPinch/services/services_entry.py +233 -0
- {openpinch-0.2.2 → openpinch-0.2.4}/OpenPinch/streamlit_webviewer/web_graphing.py +2 -1
- {openpinch-0.2.2 → openpinch-0.2.4}/OpenPinch/utils/bb_optimisers/common.py +31 -13
- {openpinch-0.2.2 → openpinch-0.2.4}/OpenPinch/utils/blackbox_minimisers.py +8 -2
- {openpinch-0.2.2 → openpinch-0.2.4}/OpenPinch/utils/export.py +5 -0
- {openpinch-0.2.2 → openpinch-0.2.4}/PKG-INFO +6 -3
- {openpinch-0.2.2 → openpinch-0.2.4}/README.md +5 -2
- {openpinch-0.2.2 → openpinch-0.2.4}/pyproject.toml +2 -2
- openpinch-0.2.2/OpenPinch/services/heat_pump_integration/unit_models/__init__.py +0 -1
- openpinch-0.2.2/OpenPinch/services/power_cogeneration_analysis/power_cogeneration_analysis.py +0 -139
- openpinch-0.2.2/OpenPinch/services/services_entry.py +0 -430
- {openpinch-0.2.2 → openpinch-0.2.4}/.gitignore +0 -0
- {openpinch-0.2.2 → openpinch-0.2.4}/LICENSE +0 -0
- {openpinch-0.2.2 → openpinch-0.2.4}/OpenPinch/__main__.py +0 -0
- {openpinch-0.2.2 → openpinch-0.2.4}/OpenPinch/classes/__init__.py +0 -0
- {openpinch-0.2.2 → openpinch-0.2.4}/OpenPinch/classes/_problem/_loading.py +0 -0
- {openpinch-0.2.2 → openpinch-0.2.4}/OpenPinch/classes/_problem/_output.py +0 -0
- {openpinch-0.2.2 → openpinch-0.2.4}/OpenPinch/classes/_problem/_result_extraction.py +0 -0
- {openpinch-0.2.2 → openpinch-0.2.4}/OpenPinch/classes/_problem_table/__init__.py +0 -0
- {openpinch-0.2.2 → openpinch-0.2.4}/OpenPinch/classes/_problem_table/_problem_table_constants.py +0 -0
- {openpinch-0.2.2 → openpinch-0.2.4}/OpenPinch/classes/_stream_collection/__init__.py +0 -0
- {openpinch-0.2.2 → openpinch-0.2.4}/OpenPinch/classes/_stream_collection/_helpers.py +0 -0
- {openpinch-0.2.2 → openpinch-0.2.4}/OpenPinch/classes/_workspace/__init__.py +0 -0
- {openpinch-0.2.2 → openpinch-0.2.4}/OpenPinch/classes/_workspace/execution.py +0 -0
- {openpinch-0.2.2 → openpinch-0.2.4}/OpenPinch/classes/_workspace/payloads.py +0 -0
- {openpinch-0.2.2 → openpinch-0.2.4}/OpenPinch/classes/_workspace/views.py +0 -0
- {openpinch-0.2.2 → openpinch-0.2.4}/OpenPinch/classes/pinch_workspace.py +0 -0
- {openpinch-0.2.2 → openpinch-0.2.4}/OpenPinch/classes/problem_table.py +0 -0
- {openpinch-0.2.2 → openpinch-0.2.4}/OpenPinch/data/__init__.py +0 -0
- {openpinch-0.2.2 → openpinch-0.2.4}/OpenPinch/data/notebooks/01_basic_pinch_and_dtcont_sensitivity.ipynb +0 -0
- {openpinch-0.2.2 → openpinch-0.2.4}/OpenPinch/data/notebooks/02_total_site_targets_and_sugcc.ipynb +0 -0
- {openpinch-0.2.2 → openpinch-0.2.4}/OpenPinch/data/notebooks/03_carnot_hpr_comparison.ipynb +0 -0
- {openpinch-0.2.2 → openpinch-0.2.4}/OpenPinch/data/notebooks/04_multistate_targeting_and_state_comparison.ipynb +0 -0
- {openpinch-0.2.2 → openpinch-0.2.4}/OpenPinch/data/notebooks/05_schema_service_and_output_workflows.ipynb +0 -0
- {openpinch-0.2.2 → openpinch-0.2.4}/OpenPinch/data/notebooks/__init__.py +0 -0
- {openpinch-0.2.2 → openpinch-0.2.4}/OpenPinch/data/sample_cases/__init__.py +0 -0
- {openpinch-0.2.2 → openpinch-0.2.4}/OpenPinch/data/sample_cases/basic_pinch.json +0 -0
- {openpinch-0.2.2 → openpinch-0.2.4}/OpenPinch/data/sample_cases/chocolate_factory.json +0 -0
- {openpinch-0.2.2 → openpinch-0.2.4}/OpenPinch/data/sample_cases/crude_preheat_train.json +0 -0
- {openpinch-0.2.2 → openpinch-0.2.4}/OpenPinch/data/sample_cases/crude_preheat_train_multistate.json +0 -0
- {openpinch-0.2.2 → openpinch-0.2.4}/OpenPinch/data/sample_cases/heat_pump_targeting.json +0 -0
- {openpinch-0.2.2 → openpinch-0.2.4}/OpenPinch/data/sample_cases/pulp_mill.json +0 -0
- {openpinch-0.2.2 → openpinch-0.2.4}/OpenPinch/data/sample_cases/zonal_site.json +0 -0
- {openpinch-0.2.2 → openpinch-0.2.4}/OpenPinch/data/sample_cases/zonal_site_multistate.json +0 -0
- {openpinch-0.2.2 → openpinch-0.2.4}/OpenPinch/lib/problem_table_types.py +0 -0
- {openpinch-0.2.2 → openpinch-0.2.4}/OpenPinch/lib/schemas/common.py +0 -0
- {openpinch-0.2.2 → openpinch-0.2.4}/OpenPinch/lib/schemas/graphs.py +0 -0
- {openpinch-0.2.2 → openpinch-0.2.4}/OpenPinch/lib/schemas/report_units.py +0 -0
- {openpinch-0.2.2 → openpinch-0.2.4}/OpenPinch/lib/schemas/turbine.py +0 -0
- {openpinch-0.2.2 → openpinch-0.2.4}/OpenPinch/lib/schemas/workspace.py +0 -0
- {openpinch-0.2.2 → openpinch-0.2.4}/OpenPinch/main.py +0 -0
- {openpinch-0.2.2 → openpinch-0.2.4}/OpenPinch/resources.py +0 -0
- {openpinch-0.2.2 → openpinch-0.2.4}/OpenPinch/services/common/__init__.py +0 -0
- {openpinch-0.2.2 → openpinch-0.2.4}/OpenPinch/services/common/graph_series_meta.py +0 -0
- {openpinch-0.2.2 → openpinch-0.2.4}/OpenPinch/services/common/miscellaneous.py +0 -0
- {openpinch-0.2.2 → openpinch-0.2.4}/OpenPinch/services/common/temperature_driving_force.py +0 -0
- {openpinch-0.2.2 → openpinch-0.2.4}/OpenPinch/services/direct_heat_integration/__init__.py +0 -0
- {openpinch-0.2.2 → openpinch-0.2.4}/OpenPinch/services/heat_pump_integration/__init__.py +0 -0
- {openpinch-0.2.2 → openpinch-0.2.4}/OpenPinch/services/heat_pump_integration/common/__init__.py +0 -0
- {openpinch-0.2.2 → openpinch-0.2.4}/OpenPinch/services/heat_pump_integration/common/encoding.py +0 -0
- {openpinch-0.2.2 → openpinch-0.2.4}/OpenPinch/services/heat_pump_integration/common/layout.py +0 -0
- {openpinch-0.2.2 → openpinch-0.2.4}/OpenPinch/services/heat_pump_integration/common/postprocessing.py +0 -0
- {openpinch-0.2.2 → openpinch-0.2.4}/OpenPinch/services/heat_pump_integration/targeting_services/__init__.py +0 -0
- {openpinch-0.2.2 → openpinch-0.2.4}/OpenPinch/services/heat_pump_integration/targeting_services/brayton.py +0 -0
- {openpinch-0.2.2 → openpinch-0.2.4}/OpenPinch/services/heat_pump_integration/targeting_services/multi_simple_carnot.py +0 -0
- {openpinch-0.2.2 → openpinch-0.2.4}/OpenPinch/services/heat_pump_integration/unit_models/brayton_heat_pump.py +0 -0
- {openpinch-0.2.2 → openpinch-0.2.4}/OpenPinch/services/heat_pump_integration/unit_models/cascade_vapour_compression_cycle.py +0 -0
- {openpinch-0.2.2 → openpinch-0.2.4}/OpenPinch/services/heat_pump_integration/unit_models/parallel_vapour_compression_cycles.py +0 -0
- {openpinch-0.2.2 → openpinch-0.2.4}/OpenPinch/services/indirect_heat_integration/__init__.py +0 -0
- {openpinch-0.2.2 → openpinch-0.2.4}/OpenPinch/services/input_data_processing/__init__.py +0 -0
- {openpinch-0.2.2 → openpinch-0.2.4}/OpenPinch/services/input_data_processing/_canonicalization.py +0 -0
- {openpinch-0.2.2 → openpinch-0.2.4}/OpenPinch/services/power_cogeneration/unit_models/multi_stage_steam_turbine.py +0 -0
- {openpinch-0.2.2 → openpinch-0.2.4}/OpenPinch/utils/__init__.py +0 -0
- {openpinch-0.2.2 → openpinch-0.2.4}/OpenPinch/utils/_tabular_input.py +0 -0
- {openpinch-0.2.2 → openpinch-0.2.4}/OpenPinch/utils/bb_optimisers/__init__.py +0 -0
- {openpinch-0.2.2 → openpinch-0.2.4}/OpenPinch/utils/bb_optimisers/bayesian_optimisation.py +0 -0
- {openpinch-0.2.2 → openpinch-0.2.4}/OpenPinch/utils/bb_optimisers/cmaes.py +0 -0
- {openpinch-0.2.2 → openpinch-0.2.4}/OpenPinch/utils/bb_optimisers/dual_annealing.py +0 -0
- {openpinch-0.2.2 → openpinch-0.2.4}/OpenPinch/utils/bb_optimisers/rbf_surrogate.py +0 -0
- {openpinch-0.2.2 → openpinch-0.2.4}/OpenPinch/utils/costing.py +0 -0
- {openpinch-0.2.2 → openpinch-0.2.4}/OpenPinch/utils/csv_to_json.py +0 -0
- {openpinch-0.2.2 → openpinch-0.2.4}/OpenPinch/utils/decorators.py +0 -0
- {openpinch-0.2.2 → openpinch-0.2.4}/OpenPinch/utils/heat_exchanger.py +0 -0
- {openpinch-0.2.2 → openpinch-0.2.4}/OpenPinch/utils/input_validation.py +0 -0
- {openpinch-0.2.2 → openpinch-0.2.4}/OpenPinch/utils/plots.py +0 -0
- {openpinch-0.2.2 → openpinch-0.2.4}/OpenPinch/utils/stream_linearisation.py +0 -0
- {openpinch-0.2.2 → openpinch-0.2.4}/OpenPinch/utils/value_resolution.py +0 -0
- {openpinch-0.2.2 → openpinch-0.2.4}/OpenPinch/utils/water_properties.py +0 -0
- {openpinch-0.2.2 → openpinch-0.2.4}/OpenPinch/utils/wkbook_to_json.py +0 -0
|
@@ -29,6 +29,7 @@ from .lib import (
|
|
|
29
29
|
DirectHeatPumpTarget,
|
|
30
30
|
DirectIntegrationTarget,
|
|
31
31
|
DirectRefrigerationTarget,
|
|
32
|
+
EnergyTransferTarget,
|
|
32
33
|
GetInputOutputData,
|
|
33
34
|
Graph,
|
|
34
35
|
GraphCatalogEntry,
|
|
@@ -186,6 +187,7 @@ __all__ = [
|
|
|
186
187
|
"DirectHeatPumpTarget",
|
|
187
188
|
"DirectIntegrationTarget",
|
|
188
189
|
"DirectRefrigerationTarget",
|
|
190
|
+
"EnergyTransferTarget",
|
|
189
191
|
"HeatPumpTargetBase",
|
|
190
192
|
"IndirectHeatPumpTarget",
|
|
191
193
|
"IndirectRefrigerationTarget",
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
"""Private helpers for the :mod:`OpenPinch.classes.pinch_problem` surface."""
|
|
2
2
|
|
|
3
|
+
from ._component_accessor import _ComponentAccessorDescriptor
|
|
3
4
|
from ._loading import (
|
|
4
5
|
JsonDict,
|
|
5
6
|
PathLike,
|
|
@@ -27,6 +28,7 @@ __all__ = [
|
|
|
27
28
|
"_LoadedProblemSource",
|
|
28
29
|
"PathLike",
|
|
29
30
|
"_ProblemSourceAdapters",
|
|
31
|
+
"_ComponentAccessorDescriptor",
|
|
30
32
|
"_PlotAccessorDescriptor",
|
|
31
33
|
"_TargetAccessorDescriptor",
|
|
32
34
|
"_validate_problem_semantics",
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from typing import TYPE_CHECKING, Any, Optional
|
|
4
|
+
|
|
5
|
+
if TYPE_CHECKING:
|
|
6
|
+
from ...services.components.process_mvr import ProcessMVRComponent
|
|
7
|
+
from ..pinch_problem import PinchProblem
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class _ComponentAccessor:
|
|
11
|
+
"""Helper namespace for mutating prepared process models with components."""
|
|
12
|
+
|
|
13
|
+
def __init__(self, problem: "PinchProblem") -> None:
|
|
14
|
+
self._problem = problem
|
|
15
|
+
|
|
16
|
+
def process_mvr(
|
|
17
|
+
self,
|
|
18
|
+
source_streams,
|
|
19
|
+
*,
|
|
20
|
+
mvr_id: Optional[str] = None,
|
|
21
|
+
n_stages: int = 1,
|
|
22
|
+
liquid_injection: bool = True,
|
|
23
|
+
mvr_stage_t_lift: Optional[float] = None,
|
|
24
|
+
mvr_stage_pressure_ratio: Optional[float] = None,
|
|
25
|
+
max_stage_t_sat_lift: Optional[float] = None,
|
|
26
|
+
options: Optional[dict[str, Any]] = None,
|
|
27
|
+
state_id: Optional[str] = None,
|
|
28
|
+
) -> "ProcessMVRComponent":
|
|
29
|
+
"""Add a direct process MVR component to selected hot gas stream(s)."""
|
|
30
|
+
from ...services.components.process_mvr import (
|
|
31
|
+
create_process_mvr_component,
|
|
32
|
+
)
|
|
33
|
+
|
|
34
|
+
return create_process_mvr_component(
|
|
35
|
+
self._problem,
|
|
36
|
+
source_streams=source_streams,
|
|
37
|
+
mvr_id=mvr_id,
|
|
38
|
+
n_stages=n_stages,
|
|
39
|
+
liquid_injection=liquid_injection,
|
|
40
|
+
mvr_stage_t_lift=mvr_stage_t_lift,
|
|
41
|
+
mvr_stage_pressure_ratio=mvr_stage_pressure_ratio,
|
|
42
|
+
max_stage_t_sat_lift=max_stage_t_sat_lift,
|
|
43
|
+
options=options,
|
|
44
|
+
state_id=state_id,
|
|
45
|
+
)
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
class _ComponentAccessorDescriptor:
|
|
49
|
+
"""Descriptor returning a problem-bound component accessor."""
|
|
50
|
+
|
|
51
|
+
def __get__(self, obj: "PinchProblem" | None, owner=None):
|
|
52
|
+
if obj is None:
|
|
53
|
+
return self
|
|
54
|
+
return _ComponentAccessor(obj)
|
|
@@ -19,6 +19,9 @@ GraphRecord = Dict[str, Any]
|
|
|
19
19
|
GraphPayload = Dict[str, GraphRecord]
|
|
20
20
|
|
|
21
21
|
_GRAPH_TYPE_ALIASES = {
|
|
22
|
+
"etd": GT.ETD.value,
|
|
23
|
+
"energy transfer": GT.ETD.value,
|
|
24
|
+
"energy transfer diagram": GT.ETD.value,
|
|
22
25
|
"cc": GT.CC.value,
|
|
23
26
|
"composite": GT.CC.value,
|
|
24
27
|
"composite curve": GT.CC.value,
|
|
@@ -410,6 +413,23 @@ class _PlotAccessor:
|
|
|
410
413
|
return_graph_data=return_graph_data,
|
|
411
414
|
)
|
|
412
415
|
|
|
416
|
+
def energy_transfer_diagram(
|
|
417
|
+
self,
|
|
418
|
+
*,
|
|
419
|
+
zone_name: Optional[str] = None,
|
|
420
|
+
index: float = 0,
|
|
421
|
+
show: bool = False,
|
|
422
|
+
return_graph_data: bool = False,
|
|
423
|
+
):
|
|
424
|
+
"""Return the first matching energy-transfer diagram payload or figure."""
|
|
425
|
+
return self._plot_graph(
|
|
426
|
+
zone_name=zone_name,
|
|
427
|
+
graph_type=GT.ETD.value,
|
|
428
|
+
index=index,
|
|
429
|
+
show=show,
|
|
430
|
+
return_graph_data=return_graph_data,
|
|
431
|
+
)
|
|
432
|
+
|
|
413
433
|
|
|
414
434
|
class _PlotAccessorDescriptor:
|
|
415
435
|
"""Non-data descriptor exposing a callable plot accessor on instances."""
|
|
@@ -7,6 +7,7 @@ from ...services import (
|
|
|
7
7
|
direct_heat_integration_service,
|
|
8
8
|
direct_heat_pump_service,
|
|
9
9
|
direct_refrigeration_service,
|
|
10
|
+
energy_transfer_analysis_service,
|
|
10
11
|
exergy_targeting_service,
|
|
11
12
|
indirect_heat_integration_service,
|
|
12
13
|
indirect_heat_pump_service,
|
|
@@ -25,7 +26,10 @@ class _TargetAccessor:
|
|
|
25
26
|
self._problem = problem
|
|
26
27
|
|
|
27
28
|
def __call__(
|
|
28
|
-
self,
|
|
29
|
+
self,
|
|
30
|
+
*,
|
|
31
|
+
options: Optional[dict[str, Any]] = None,
|
|
32
|
+
state_id: Optional[str] = None,
|
|
29
33
|
):
|
|
30
34
|
runtime_options = dict(options or {})
|
|
31
35
|
if state_id is not None:
|
|
@@ -219,6 +223,26 @@ class _TargetAccessor:
|
|
|
219
223
|
service_func=exergy_targeting_service,
|
|
220
224
|
)
|
|
221
225
|
|
|
226
|
+
def energy_transfer(
|
|
227
|
+
self,
|
|
228
|
+
*,
|
|
229
|
+
zone_name: Optional[str] = None,
|
|
230
|
+
options: Optional[dict[str, Any]] = None,
|
|
231
|
+
include_subzones: bool = False,
|
|
232
|
+
state_id: Optional[str] = None,
|
|
233
|
+
) -> BaseTargetModel:
|
|
234
|
+
"""Create energy-transfer diagram and heat-surplus/deficit outputs."""
|
|
235
|
+
runtime_options = dict(options or {})
|
|
236
|
+
if state_id is not None:
|
|
237
|
+
runtime_options["state_id"] = state_id
|
|
238
|
+
return self._problem._execute_targeting(
|
|
239
|
+
target_id=TT.ET.value,
|
|
240
|
+
application_zone=zone_name,
|
|
241
|
+
options=runtime_options,
|
|
242
|
+
include_subzones=include_subzones,
|
|
243
|
+
direct_service_func=energy_transfer_analysis_service,
|
|
244
|
+
)
|
|
245
|
+
|
|
222
246
|
|
|
223
247
|
class _TargetAccessorDescriptor:
|
|
224
248
|
"""Non-data descriptor exposing a callable target accessor on instances."""
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
5
|
import inspect
|
|
6
|
+
from functools import lru_cache
|
|
6
7
|
from typing import Callable
|
|
7
8
|
|
|
8
9
|
from ...lib.enums import ZT
|
|
@@ -31,21 +32,26 @@ def run_targeting_for_zone_and_subzones(
|
|
|
31
32
|
|
|
32
33
|
def _invoke_service(service_func: Callable, zone: Zone, args: dict | None = None):
|
|
33
34
|
"""Call a targeting callback with backward-compatible argument handling."""
|
|
34
|
-
if not
|
|
35
|
+
if not callable(service_func):
|
|
35
36
|
return None
|
|
36
37
|
|
|
37
|
-
if args is None:
|
|
38
|
+
if args is None or not _service_accepts_args(service_func):
|
|
38
39
|
return service_func(zone)
|
|
39
40
|
|
|
41
|
+
return service_func(zone, args)
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
@lru_cache(maxsize=64)
|
|
45
|
+
def _service_accepts_args(service_func: Callable) -> bool:
|
|
46
|
+
"""Return whether a service accepts a second positional ``args`` parameter."""
|
|
40
47
|
try:
|
|
41
48
|
signature = inspect.signature(service_func)
|
|
42
49
|
except TypeError, ValueError:
|
|
43
|
-
return
|
|
50
|
+
return True
|
|
44
51
|
|
|
45
52
|
params = list(signature.parameters.values())
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
)
|
|
53
|
+
if any(param.kind == inspect.Parameter.VAR_POSITIONAL for param in params):
|
|
54
|
+
return True
|
|
49
55
|
positional_params = [
|
|
50
56
|
param
|
|
51
57
|
for param in params
|
|
@@ -55,10 +61,7 @@ def _invoke_service(service_func: Callable, zone: Zone, args: dict | None = None
|
|
|
55
61
|
inspect.Parameter.POSITIONAL_OR_KEYWORD,
|
|
56
62
|
)
|
|
57
63
|
]
|
|
58
|
-
|
|
59
|
-
if accepts_varargs or len(positional_params) >= 2:
|
|
60
|
-
return service_func(zone, args)
|
|
61
|
-
return service_func(zone)
|
|
64
|
+
return len(positional_params) >= 2
|
|
62
65
|
|
|
63
66
|
|
|
64
67
|
def _invoke_target_handler(
|
|
@@ -95,7 +98,7 @@ def _get_unit_operation_targets(
|
|
|
95
98
|
for subzone in zone.subzones.values():
|
|
96
99
|
if subzone.type == ZT.O.value:
|
|
97
100
|
if subzone.config.DO_DIRECT_OPERATION_TARGETING:
|
|
98
|
-
if
|
|
101
|
+
if callable(direct_service_func):
|
|
99
102
|
_invoke_service(direct_service_func, subzone, args)
|
|
100
103
|
else:
|
|
101
104
|
raise ValueError(
|
|
@@ -103,7 +106,7 @@ def _get_unit_operation_targets(
|
|
|
103
106
|
"contain other operation zones."
|
|
104
107
|
)
|
|
105
108
|
|
|
106
|
-
if
|
|
109
|
+
if callable(direct_service_func):
|
|
107
110
|
_invoke_service(direct_service_func, zone, args)
|
|
108
111
|
|
|
109
112
|
return zone
|
|
@@ -142,10 +145,10 @@ def _get_process_targets(
|
|
|
142
145
|
)
|
|
143
146
|
|
|
144
147
|
if zone.config.DO_INDIRECT_PROCESS_TARGETING:
|
|
145
|
-
if
|
|
148
|
+
if callable(indirect_service_func):
|
|
146
149
|
_invoke_service(indirect_service_func, zone, args)
|
|
147
150
|
|
|
148
|
-
if
|
|
151
|
+
if callable(direct_service_func):
|
|
149
152
|
_invoke_service(direct_service_func, zone, args)
|
|
150
153
|
|
|
151
154
|
return zone
|
|
@@ -159,7 +162,7 @@ def _get_site_targets(
|
|
|
159
162
|
):
|
|
160
163
|
"""Run site targeting over the full nested zone hierarchy."""
|
|
161
164
|
|
|
162
|
-
if
|
|
165
|
+
if callable(direct_service_func):
|
|
163
166
|
_invoke_service(direct_service_func, zone, args)
|
|
164
167
|
|
|
165
168
|
if len(zone.subzones) > 0:
|
|
@@ -194,7 +197,7 @@ def _get_site_targets(
|
|
|
194
197
|
"site, process, and operation zones."
|
|
195
198
|
)
|
|
196
199
|
|
|
197
|
-
if
|
|
200
|
+
if callable(indirect_service_func):
|
|
198
201
|
_invoke_service(indirect_service_func, zone, args)
|
|
199
202
|
|
|
200
203
|
return zone
|
|
@@ -17,6 +17,48 @@ from ..value import Value
|
|
|
17
17
|
|
|
18
18
|
ValidationContext = dict[str, list[dict[str, Any]]]
|
|
19
19
|
_TEMPERATURE_EQUAL_TOL = 1e-12
|
|
20
|
+
_STREAM_VALUE_FIELDS = (
|
|
21
|
+
"t_supply",
|
|
22
|
+
"t_target",
|
|
23
|
+
"p_supply",
|
|
24
|
+
"p_target",
|
|
25
|
+
"h_supply",
|
|
26
|
+
"h_target",
|
|
27
|
+
"heat_flow",
|
|
28
|
+
"dt_cont",
|
|
29
|
+
"htc",
|
|
30
|
+
)
|
|
31
|
+
_STREAM_OPTIONAL_VALUE_FIELDS = (
|
|
32
|
+
"p_supply",
|
|
33
|
+
"p_target",
|
|
34
|
+
"h_supply",
|
|
35
|
+
"h_target",
|
|
36
|
+
"dt_cont",
|
|
37
|
+
"htc",
|
|
38
|
+
)
|
|
39
|
+
_UTILITY_VALUE_FIELDS = (
|
|
40
|
+
"t_supply",
|
|
41
|
+
"t_target",
|
|
42
|
+
"p_supply",
|
|
43
|
+
"p_target",
|
|
44
|
+
"h_supply",
|
|
45
|
+
"h_target",
|
|
46
|
+
"heat_flow",
|
|
47
|
+
"dt_cont",
|
|
48
|
+
"htc",
|
|
49
|
+
"price",
|
|
50
|
+
)
|
|
51
|
+
_UTILITY_OPTIONAL_VALUE_FIELDS = (
|
|
52
|
+
"t_target",
|
|
53
|
+
"p_supply",
|
|
54
|
+
"p_target",
|
|
55
|
+
"h_supply",
|
|
56
|
+
"h_target",
|
|
57
|
+
"heat_flow",
|
|
58
|
+
"dt_cont",
|
|
59
|
+
"htc",
|
|
60
|
+
"price",
|
|
61
|
+
)
|
|
20
62
|
|
|
21
63
|
|
|
22
64
|
def validate_problem_inputs(
|
|
@@ -136,8 +178,8 @@ def semantic_issues(
|
|
|
136
178
|
section="streams",
|
|
137
179
|
record_index=index,
|
|
138
180
|
record_label=label,
|
|
139
|
-
field_names=
|
|
140
|
-
optional_field_names=
|
|
181
|
+
field_names=_STREAM_VALUE_FIELDS,
|
|
182
|
+
optional_field_names=_STREAM_OPTIONAL_VALUE_FIELDS,
|
|
141
183
|
config=input_unit_config,
|
|
142
184
|
)
|
|
143
185
|
issues.extend(stream_value_issues)
|
|
@@ -157,21 +199,8 @@ def semantic_issues(
|
|
|
157
199
|
section="utilities",
|
|
158
200
|
record_index=index,
|
|
159
201
|
record_label=label,
|
|
160
|
-
field_names=
|
|
161
|
-
|
|
162
|
-
"t_target",
|
|
163
|
-
"heat_flow",
|
|
164
|
-
"dt_cont",
|
|
165
|
-
"htc",
|
|
166
|
-
"price",
|
|
167
|
-
),
|
|
168
|
-
optional_field_names=(
|
|
169
|
-
"t_target",
|
|
170
|
-
"heat_flow",
|
|
171
|
-
"dt_cont",
|
|
172
|
-
"htc",
|
|
173
|
-
"price",
|
|
174
|
-
),
|
|
202
|
+
field_names=_UTILITY_VALUE_FIELDS,
|
|
203
|
+
optional_field_names=_UTILITY_OPTIONAL_VALUE_FIELDS,
|
|
175
204
|
config=input_unit_config,
|
|
176
205
|
)
|
|
177
206
|
issues.extend(utility_value_issues)
|
|
@@ -435,51 +464,16 @@ def _validate_stream_record_states(
|
|
|
435
464
|
record_label: Optional[str],
|
|
436
465
|
) -> list[ValidationIssue]:
|
|
437
466
|
issues: list[ValidationIssue] = []
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
issues.extend(
|
|
448
|
-
_validate_value_finiteness(
|
|
449
|
-
values.get("t_target"),
|
|
450
|
-
section=section,
|
|
451
|
-
record_index=record_index,
|
|
452
|
-
record_label=record_label,
|
|
453
|
-
field_name="t_target",
|
|
454
|
-
)
|
|
455
|
-
)
|
|
456
|
-
issues.extend(
|
|
457
|
-
_validate_value_finiteness(
|
|
458
|
-
values.get("heat_flow"),
|
|
459
|
-
section=section,
|
|
460
|
-
record_index=record_index,
|
|
461
|
-
record_label=record_label,
|
|
462
|
-
field_name="heat_flow",
|
|
463
|
-
)
|
|
464
|
-
)
|
|
465
|
-
issues.extend(
|
|
466
|
-
_validate_value_finiteness(
|
|
467
|
-
values.get("dt_cont"),
|
|
468
|
-
section=section,
|
|
469
|
-
record_index=record_index,
|
|
470
|
-
record_label=record_label,
|
|
471
|
-
field_name="dt_cont",
|
|
472
|
-
)
|
|
473
|
-
)
|
|
474
|
-
issues.extend(
|
|
475
|
-
_validate_value_finiteness(
|
|
476
|
-
values.get("htc"),
|
|
477
|
-
section=section,
|
|
478
|
-
record_index=record_index,
|
|
479
|
-
record_label=record_label,
|
|
480
|
-
field_name="htc",
|
|
467
|
+
for field_name in _STREAM_VALUE_FIELDS:
|
|
468
|
+
issues.extend(
|
|
469
|
+
_validate_value_finiteness(
|
|
470
|
+
values.get(field_name),
|
|
471
|
+
section=section,
|
|
472
|
+
record_index=record_index,
|
|
473
|
+
record_label=record_label,
|
|
474
|
+
field_name=field_name,
|
|
475
|
+
)
|
|
481
476
|
)
|
|
482
|
-
)
|
|
483
477
|
issues.extend(
|
|
484
478
|
_validate_non_negative_states(
|
|
485
479
|
values.get("heat_flow"),
|
|
@@ -522,7 +516,7 @@ def _validate_stream_record_states(
|
|
|
522
516
|
classifications: dict[str, list[str]] = {
|
|
523
517
|
ST.Hot.value: [],
|
|
524
518
|
ST.Cold.value: [],
|
|
525
|
-
ST.
|
|
519
|
+
ST.Neutral.value: [],
|
|
526
520
|
}
|
|
527
521
|
|
|
528
522
|
num_states = 1
|
|
@@ -558,6 +552,10 @@ def _validate_stream_record_states(
|
|
|
558
552
|
),
|
|
559
553
|
)
|
|
560
554
|
)
|
|
555
|
+
elif t_supply_value > t_target_value:
|
|
556
|
+
classifications[ST.Hot.value].append(str(idx))
|
|
557
|
+
elif t_supply_value < t_target_value:
|
|
558
|
+
classifications[ST.Cold.value].append(str(idx))
|
|
561
559
|
|
|
562
560
|
active_classes = {
|
|
563
561
|
stream_type: state_group
|
|
@@ -577,7 +575,7 @@ def _validate_stream_record_states(
|
|
|
577
575
|
"Stream states must classify consistently. "
|
|
578
576
|
f"Hot={classifications[ST.Hot.value]}, "
|
|
579
577
|
f"Cold={classifications[ST.Cold.value]}, "
|
|
580
|
-
f"Neutral={classifications[ST.
|
|
578
|
+
f"Neutral={classifications[ST.Neutral.value]}."
|
|
581
579
|
),
|
|
582
580
|
)
|
|
583
581
|
)
|
|
@@ -593,7 +591,7 @@ def _validate_utility_record_states(
|
|
|
593
591
|
record_label: Optional[str],
|
|
594
592
|
) -> list[ValidationIssue]:
|
|
595
593
|
issues: list[ValidationIssue] = []
|
|
596
|
-
for field_name in
|
|
594
|
+
for field_name in _UTILITY_VALUE_FIELDS:
|
|
597
595
|
issues.extend(
|
|
598
596
|
_validate_value_finiteness(
|
|
599
597
|
values.get(field_name),
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
"""Numeric snapshot helpers for :class:`StreamCollection` internals."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from dataclasses import dataclass
|
|
6
|
+
|
|
7
|
+
import numpy as np
|
|
8
|
+
|
|
9
|
+
from ...lib.enums import ST
|
|
10
|
+
from ..value import Value
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
@dataclass(frozen=True)
|
|
14
|
+
class StreamCollectionNumericView:
|
|
15
|
+
"""Dense numeric snapshot of stream properties for internal kernels."""
|
|
16
|
+
|
|
17
|
+
t_min: np.ndarray
|
|
18
|
+
t_max: np.ndarray
|
|
19
|
+
t_min_star: np.ndarray
|
|
20
|
+
t_max_star: np.ndarray
|
|
21
|
+
cp: np.ndarray
|
|
22
|
+
rcp: np.ndarray
|
|
23
|
+
heat_flow: np.ndarray
|
|
24
|
+
dt_cont: np.ndarray
|
|
25
|
+
is_hot: np.ndarray
|
|
26
|
+
is_cold: np.ndarray
|
|
27
|
+
active: np.ndarray
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def build_numeric_view(streams: list[object], idx: int | None = None):
|
|
31
|
+
"""Build a dense numeric view from stream objects."""
|
|
32
|
+
n_streams = len(streams)
|
|
33
|
+
t_min = np.empty(n_streams, dtype=float)
|
|
34
|
+
t_max = np.empty(n_streams, dtype=float)
|
|
35
|
+
t_min_star = np.empty(n_streams, dtype=float)
|
|
36
|
+
t_max_star = np.empty(n_streams, dtype=float)
|
|
37
|
+
cp = np.empty(n_streams, dtype=float)
|
|
38
|
+
rcp = np.empty(n_streams, dtype=float)
|
|
39
|
+
heat_flow = np.empty(n_streams, dtype=float)
|
|
40
|
+
dt_cont = np.empty(n_streams, dtype=float)
|
|
41
|
+
is_hot = np.empty(n_streams, dtype=bool)
|
|
42
|
+
is_cold = np.empty(n_streams, dtype=bool)
|
|
43
|
+
active = np.empty(n_streams, dtype=bool)
|
|
44
|
+
|
|
45
|
+
for stream_idx, stream in enumerate(streams):
|
|
46
|
+
t_min[stream_idx] = value_at_idx(stream._t_min, idx)
|
|
47
|
+
t_max[stream_idx] = value_at_idx(stream._t_max, idx)
|
|
48
|
+
t_min_star[stream_idx] = value_at_idx(stream._t_min_star, idx)
|
|
49
|
+
t_max_star[stream_idx] = value_at_idx(stream._t_max_star, idx)
|
|
50
|
+
cp[stream_idx] = value_at_idx(stream._cp, idx)
|
|
51
|
+
rcp[stream_idx] = value_at_idx(stream._rcp_prod, idx)
|
|
52
|
+
heat_flow[stream_idx] = value_at_idx(stream._heat_flow, idx)
|
|
53
|
+
dt_cont[stream_idx] = value_at_idx(stream._dt_cont, idx)
|
|
54
|
+
is_hot[stream_idx] = stream.type == ST.Hot.value
|
|
55
|
+
is_cold[stream_idx] = stream.type == ST.Cold.value
|
|
56
|
+
active[stream_idx] = bool(stream.active)
|
|
57
|
+
|
|
58
|
+
return StreamCollectionNumericView(
|
|
59
|
+
t_min=t_min,
|
|
60
|
+
t_max=t_max,
|
|
61
|
+
t_min_star=t_min_star,
|
|
62
|
+
t_max_star=t_max_star,
|
|
63
|
+
cp=cp,
|
|
64
|
+
rcp=rcp,
|
|
65
|
+
heat_flow=heat_flow,
|
|
66
|
+
dt_cont=dt_cont,
|
|
67
|
+
is_hot=is_hot,
|
|
68
|
+
is_cold=is_cold,
|
|
69
|
+
active=active,
|
|
70
|
+
)
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
def value_at_idx(value: Value | None, idx: int | None = None) -> float:
|
|
74
|
+
"""Return one canonical numeric magnitude from a scalar or stateful value."""
|
|
75
|
+
if value is None:
|
|
76
|
+
return np.nan
|
|
77
|
+
magnitudes = value._quantity.magnitude
|
|
78
|
+
if magnitudes.size == 1:
|
|
79
|
+
return float(magnitudes[0])
|
|
80
|
+
state_idx = 0 if idx is None else int(idx)
|
|
81
|
+
return float(magnitudes[state_idx])
|
|
@@ -30,6 +30,7 @@ from ..utils.wkbook_to_json import get_problem_from_excel
|
|
|
30
30
|
from ._problem import (
|
|
31
31
|
JsonDict,
|
|
32
32
|
PathLike,
|
|
33
|
+
_ComponentAccessorDescriptor,
|
|
33
34
|
_LoadedProblemSource,
|
|
34
35
|
_PlotAccessorDescriptor,
|
|
35
36
|
_ProblemSourceAdapters,
|
|
@@ -65,8 +66,10 @@ class PinchProblem:
|
|
|
65
66
|
_results: Optional[TargetOutput]
|
|
66
67
|
_validated_data: Optional[TargetInput]
|
|
67
68
|
_master_zone: Optional["Zone"]
|
|
69
|
+
_process_components: dict[str, Any]
|
|
68
70
|
_input_source_kind: str
|
|
69
71
|
_validation_context: Optional[dict[str, list[dict[str, Any]]]]
|
|
72
|
+
add_component = _ComponentAccessorDescriptor()
|
|
70
73
|
plot = _PlotAccessorDescriptor()
|
|
71
74
|
target = _TargetAccessorDescriptor()
|
|
72
75
|
|
|
@@ -86,6 +89,7 @@ class PinchProblem:
|
|
|
86
89
|
self._results = None
|
|
87
90
|
self._validated_data = None
|
|
88
91
|
self._master_zone = None
|
|
92
|
+
self._process_components = {}
|
|
89
93
|
self.results_dir = None
|
|
90
94
|
|
|
91
95
|
if source is not None:
|
|
@@ -132,6 +136,7 @@ class PinchProblem:
|
|
|
132
136
|
indirect_service_func=indirect_service_func,
|
|
133
137
|
args=runtime_options,
|
|
134
138
|
)
|
|
139
|
+
self._attach_process_component_work_targets(zone, runtime_options)
|
|
135
140
|
self._results = TargetOutput.model_validate(extract_results(zone, state_id=sid))
|
|
136
141
|
return self._results
|
|
137
142
|
|
|
@@ -167,6 +172,10 @@ class PinchProblem:
|
|
|
167
172
|
direct_service_func(zone, runtime_options)
|
|
168
173
|
if indirect_service_func is not None:
|
|
169
174
|
indirect_service_func(zone, runtime_options)
|
|
175
|
+
self._attach_process_component_work_targets(
|
|
176
|
+
execution_master_zone,
|
|
177
|
+
runtime_options,
|
|
178
|
+
)
|
|
170
179
|
self._results = TargetOutput.model_validate(
|
|
171
180
|
extract_results(execution_master_zone, state_id=sid)
|
|
172
181
|
)
|
|
@@ -207,6 +216,10 @@ class PinchProblem:
|
|
|
207
216
|
else:
|
|
208
217
|
if service_func is not None:
|
|
209
218
|
service_func(zone, runtime_options)
|
|
219
|
+
self._attach_process_component_work_targets(
|
|
220
|
+
execution_master_zone,
|
|
221
|
+
runtime_options,
|
|
222
|
+
)
|
|
210
223
|
self._results = TargetOutput.model_validate(
|
|
211
224
|
extract_results(execution_master_zone, state_id=sid)
|
|
212
225
|
)
|
|
@@ -273,6 +286,10 @@ class PinchProblem:
|
|
|
273
286
|
elif service_func is not None:
|
|
274
287
|
service_func(zone, runtime_options)
|
|
275
288
|
|
|
289
|
+
self._attach_process_component_work_targets(
|
|
290
|
+
execution_master_zone,
|
|
291
|
+
runtime_options,
|
|
292
|
+
)
|
|
276
293
|
self._results = TargetOutput.model_validate(
|
|
277
294
|
extract_results(execution_master_zone, state_id=sid)
|
|
278
295
|
)
|
|
@@ -308,6 +325,51 @@ class PinchProblem:
|
|
|
308
325
|
return selected_master_zone
|
|
309
326
|
return selected_master_zone.get_subzone(application_zone)
|
|
310
327
|
|
|
328
|
+
def _attach_process_component_work_targets(
|
|
329
|
+
self,
|
|
330
|
+
zone: "Zone",
|
|
331
|
+
runtime_options: Optional[dict[str, Any]],
|
|
332
|
+
) -> None:
|
|
333
|
+
if not self._process_components:
|
|
334
|
+
return
|
|
335
|
+
state_id = (runtime_options or {}).get("state_id")
|
|
336
|
+
state_idx = (runtime_options or {}).get("idx")
|
|
337
|
+
for current_zone in self._walk_zone_tree(zone):
|
|
338
|
+
component_work = self._process_component_work_for_zone(
|
|
339
|
+
current_zone,
|
|
340
|
+
state_id=state_id,
|
|
341
|
+
state_idx=state_idx,
|
|
342
|
+
)
|
|
343
|
+
for target in current_zone.targets.values():
|
|
344
|
+
if hasattr(target, "process_component_work_target"):
|
|
345
|
+
target.process_component_work_target = component_work
|
|
346
|
+
if (
|
|
347
|
+
component_work > 0.0
|
|
348
|
+
and hasattr(target, "work_target")
|
|
349
|
+
and getattr(target, "work_target", None) is None
|
|
350
|
+
):
|
|
351
|
+
target.work_target = component_work
|
|
352
|
+
|
|
353
|
+
def _process_component_work_for_zone(
|
|
354
|
+
self,
|
|
355
|
+
zone: "Zone",
|
|
356
|
+
*,
|
|
357
|
+
state_id: str | None,
|
|
358
|
+
state_idx: int | None,
|
|
359
|
+
) -> float:
|
|
360
|
+
total = 0.0
|
|
361
|
+
for component in self._process_components.values():
|
|
362
|
+
work_for_zone = getattr(component, "work_for_zone", None)
|
|
363
|
+
if work_for_zone is None:
|
|
364
|
+
continue
|
|
365
|
+
total += float(work_for_zone(zone, state_id=state_id, state_idx=state_idx))
|
|
366
|
+
return total
|
|
367
|
+
|
|
368
|
+
def _walk_zone_tree(self, zone: "Zone"):
|
|
369
|
+
yield zone
|
|
370
|
+
for subzone in zone.subzones.values():
|
|
371
|
+
yield from self._walk_zone_tree(subzone)
|
|
372
|
+
|
|
311
373
|
def _build_execution_master_zone(self) -> "Zone":
|
|
312
374
|
if self._problem_data is None and self._master_zone is None:
|
|
313
375
|
raise RuntimeError("No input loaded. Call load(...) first.")
|
|
@@ -374,7 +436,7 @@ class PinchProblem:
|
|
|
374
436
|
options: Optional[dict[str, Any]],
|
|
375
437
|
*,
|
|
376
438
|
zone: "Zone",
|
|
377
|
-
) -> tuple[dict[str, Any], str | None
|
|
439
|
+
) -> tuple[dict[str, Any], str | None]:
|
|
378
440
|
runtime_options = dict(options or {})
|
|
379
441
|
idx, sid = get_state_index(state_ids=zone.state_ids, args=runtime_options)
|
|
380
442
|
runtime_options["idx"] = idx
|
|
@@ -583,6 +645,11 @@ class PinchProblem:
|
|
|
583
645
|
"""Return the prepared root zone after a successful ``load()`` pass."""
|
|
584
646
|
return self._master_zone
|
|
585
647
|
|
|
648
|
+
@property
|
|
649
|
+
def process_components(self) -> dict[str, Any]:
|
|
650
|
+
"""Memory-only process components applied to the prepared model."""
|
|
651
|
+
return self._process_components
|
|
652
|
+
|
|
586
653
|
@property
|
|
587
654
|
def hot_streams(self) -> StreamCollection:
|
|
588
655
|
"""Hot process streams on the root analysis zone."""
|
|
@@ -755,6 +822,7 @@ class PinchProblem:
|
|
|
755
822
|
"""Revalidate, reconstruct the zone tree, and clear cached results."""
|
|
756
823
|
self._validated_data = self.validate()
|
|
757
824
|
self._master_zone = self._data_preprocessing()
|
|
825
|
+
self._process_components = {}
|
|
758
826
|
self._results = None
|
|
759
827
|
return self._master_zone
|
|
760
828
|
|