### Install OpenPinch and notebook extra Source: https://openpinch.readthedocs.io/en/latest/guides/first-solve-cli.html Install the OpenPinch package and the optional notebook extra required to run notebooks. ```bash python -m pip install openpinch python -m pip install "openpinch[notebook]" ``` -------------------------------- ### Install HEN synthesis extra and IDAES extensions Source: https://openpinch.readthedocs.io/en/latest/guides/heat-exchanger-network-synthesis.html Install the openpinch[synthesis] package and IDAES solver extensions before using solver-backed methods. ```bash python -m pip install "openpinch[synthesis]" idaes get-extensions ``` -------------------------------- ### Install OpenPinch with TESPy optional dependency Source: https://openpinch.readthedocs.io/en/latest/guides/heat-pump-workflows.html Installs the optional TESPy simulator dependency. Only needed when using the TESPy targeting backend. ```bash python -m pip install "openpinch[tespy]" ``` -------------------------------- ### Module imports and setup Source: https://openpinch.readthedocs.io/en/latest/_modules/OpenPinch/analysis/heat_pumps/service.html Imports necessary modules and defines the public API functions for heat pump and refrigeration targeting. ```python """Public entrypoint for heat pump and refrigeration targeting.""" from __future__ import annotations from copy import deepcopy import numpy as np from ...analysis.numerics import get_period_index from ...analysis.targeting.cascade import create_problem_table_with_t_int from ...contracts.hpr import HeatPumpTargetOutputs, HPRSearchBudget from ...domain.configuration import Configuration, tol from ...domain.enums import ( GraphType, HeatPumpAndRefrigerationCycle, ProblemTableLabel, TargetType, ) from ...domain.hpr import HPRLoadSummary, HPRResidualData, HPRThermalSlice from ...domain.problem_table import ProblemTable from ...domain.targets import ( DirectHeatPumpTarget, DirectRefrigerationTarget, IndirectHeatPumpTarget, IndirectRefrigerationTarget, ) from ...domain.zone import Zone from ..targeting.cascade import ( get_process_heat_cascade, get_utility_heat_cascade, ) from ._multiperiod.execution import get_multiperiod_hpr_targets from ._multiperiod.preparation import ( build_multiperiod_hpr_cases, idle_period_weight, period_case_by_id, period_id_for_index, ) from .common.load_selection import resolve_hpr_target_load from .common.postprocessing import _get_hpr_residual_utility_summary from .common.preprocessing import ( construct_HPRTargetInputs, ) from .optimisation_adapter import translate_hpr_output from .performance_maps.targeting import ( normalize_hpr_simulation_backend, preflight_tespy_hpr_targeting, ) from .targeting.brayton import ( optimise_brayton_heat_pump_placement, ) from .targeting.cascade_carnot import ( optimise_cascade_carnot_heat_pump_placement, ) from .targeting.cascade_vapour_compression import ( optimise_cascade_heat_pump_placement, ) from .targeting.parallel_carnot import ( optimise_parallel_carnot_heat_pump_placement, ) from .targeting.parallel_vapour_compression import ( optimise_parallel_heat_pump_placement, ) from .targeting.vapour_compression_mvr import ( optimise_vapour_compression_mvr_heat_pump_placement, ) __all__ = [ "compute_direct_heat_pump_or_refrigeration_target", "compute_indirect_heat_pump_or_refrigeration_target", ] ################################################################################ # Public API ################################################################################ ``` -------------------------------- ### Install synthesis extra with pip Source: https://openpinch.readthedocs.io/en/latest/developer/synthesis-dependency-policy.html Installs the optional synthesis dependencies for OpenPinch using pip. This is required for users who need the heat exchanger network synthesis functionality. ```bash python -m pip install "openpinch[synthesis]" ``` -------------------------------- ### Discover configuration options with config_options() Source: https://openpinch.readthedocs.io/en/latest/api/schemas-and-config.html Use config_options() to inspect supported flat TargetInput.options keys. The example filters options by group 'hpr' to get heat-pump related fields. ```python from OpenPinch.presentation.configuration import configuration_options as config_options options = config_options() hpr_options = [field for field in options if field.group == "hpr"] ``` -------------------------------- ### Stage-wise model setup and PDM solve method Source: https://openpinch.readthedocs.io/en/latest/_modules/OpenPinch/analysis/heat_exchanger_networks/models/problem.html This snippet shows the beginning of a method that sets up a stage-wise model, retrieving the stage count from the parent solution if not explicitly provided, and then solving PDM sides and amalgamating them. It also updates the arguments with the amalgamated model name and settings. ```python stages = self.stages if stages is None and self.parent is not None: stages = getattr( self.parent.case, "stages", getattr(self.parent.case, "S", None), ) if stages is None: raise ValueError( "Stage-wise models require an explicit stage count or a parent " "solution." ) factory = self._model_factory( model_factories, "stagewise", default="StageWiseModel", ) self.case = factory(**self.args, stages=stages) if self.parent is not None: self.case.set_initial_values_for_variables(self.parent.case) def _solve_pdm(self, *, print_output: bool = True) -> None: """Solve PDM sides and amalgamate them before stage reduction.""" if self.above.side_required: self.above.optimise(print_output=print_output) if self.below.side_required: self.below.optimise(print_output=print_output) self.case = self.above.amalgamate_networks( below_case=self.below, above_case=self.above, ) self.case.get_post_process() if print_output: self.case.output_to_cmd_line() self.args.update( { "name": f"PDM amalgamated {self.dTmin}", "minimisation_goal": "total utility", "tol": self.tol, } ) ``` -------------------------------- ### _prepare_direct_integration_profile function Source: https://openpinch.readthedocs.io/en/latest/_modules/OpenPinch/analysis/targeting/direct.html Prepares a utility-independent direct load profile for repeated replay. It gets the period index from zone.period_ids and args, combines hot and cold streams, and builds the profile with insert_constant_heat_intervals set to False. ```python def _prepare_direct_integration_profile( zone: Zone, args: dict | None = None, ) -> _PreparedDirectIntegrationProfile: """Prepare one utility-independent direct load profile for repeated replay.""" idx, _sid = get_period_index(period_ids=zone.period_ids, args=args) process_streams = zone.hot_streams + zone.cold_streams return _build_direct_integration_profile( zone, idx=idx, temperature_streams=process_streams, insert_constant_heat_intervals=False, ) ``` -------------------------------- ### Carnot warm start initialization for parallel vapour compression Source: https://openpinch.readthedocs.io/en/latest/_modules/OpenPinch/analysis/heat_pumps/targeting/parallel_vapour_compression.html Initializes the optimization variables for a parallel vapour compression heat pump cycle. Clips the Carnot warm start condensing temperature into the subcritical range, maps evaporator temperatures, and encodes seed duties against availability. Returns the packed variables and bounds. ```python # The Carnot warm start knows nothing about critical points, so clip it # into the refrigerant's subcritical condensing range. x_cond = np.clip( map_T_arr_to_x_arr(init_res.T_cond, *condensing_temperature_search_range(args)), 0.0, 1.0, ).tolist() x_evap = map_T_arr_to_x_arr( init_res.T_evap[::-1], args.T_hot[-1], args.T_hot[0] ).tolist() x_subcool = [0.0] * int(args.n_cond) init_primary_duty = init_res.Q_cond if is_heat_pumping else init_res.Q_evap x_ihx = [0.0] * int(args.n_cond) pack_kwargs = { "x_cond": x_cond, "x_evap": x_evap, "x_subcool": x_subcool, "x_ihx": x_ihx, } split_key = "x_heat_split" if is_heat_pumping else "x_cool_split" pack_kwargs[split_key] = np.ones(int(args.n_cond), dtype=float) # Encode the seed duties against the availability at the seed's own # temperatures, exactly as the search will decode them. state = _parse_parallel_hp_state_temperatures(layout.pack(**pack_kwargs), args) Q_available = state.Q_heat_available if is_heat_pumping else state.Q_cool_available pack_kwargs[split_key] = encode_available_fractions( np.asarray(init_primary_duty, dtype=float), Q_available ) return clip_to_bounds(layout.pack(**pack_kwargs), bnds), bnds ``` -------------------------------- ### Function _get_cascade_hp_opt_setup Source: https://openpinch.readthedocs.io/en/latest/_modules/OpenPinch/analysis/heat_pumps/targeting/cascade_vapour_compression.html Builds the optimization setup for cascade heat pump targeting. It constructs the vector layout, bounds, and an initial guess from a previous result, clipping temperatures into subcritical ranges and encoding seed duties. ```python def _get_cascade_hp_opt_setup( init_res: HPRBackendResult | None, args: HeatPumpTargetInputs, ) -> tuple[np.ndarray | None, list]: is_heat_pumping = getattr(args, "is_heat_pumping", True) n_cond = int(args.n_cond) n_evap = int(args.n_evap) n_heat, n_cool = _cascade_process_control_counts( n_cond=n_cond, n_evap=n_evap, is_heat_pumping=is_heat_pumping, ) layout = HPRoptVectorLayout( n_cond=n_cond, n_evap=n_evap, n_subcool=n_cond, n_heat_split=n_heat, n_cool_split=n_cool, n_ihx=n_cond + n_evap - 1, ) bnds = layout.build_bounds( x_cond=(0.0, 1.0), x_evap=(0.0, 1.0), x_subcool=(0.0, 1.0), x_heat_split=(0.0, 1.0), x_cool_split=(0.0, 1.0), x_ihx=(0.0, 0.0) if getattr(args, "simulation_backend", "coolprop") == "tespy" else (0.0, 1.0), ) if init_res is None: return None, bnds # The Carnot warm start knows nothing about critical points, so clip it # into the refrigerant's subcritical condensing range. x_cond = np.clip( map_T_arr_to_x_arr(init_res.T_cond, *condensing_temperature_search_range(args)), 0.0, 1.0, ).tolist() x_evap = map_T_arr_to_x_arr( init_res.T_evap[::-1], args.T_hot[-1], args.T_hot[0] ).tolist() x_subcool = [0.0] * int(args.n_cond) init_heat = init_res.Q_cond if is_heat_pumping else init_res.Q_cond[:n_heat] init_cool = init_res.Q_evap[:n_cool] if is_heat_pumping else init_res.Q_evap x_ihx = [0.0] * (int(args.n_cond) + int(args.n_evap) - 1) pack_kwargs = { "x_cond": x_cond, "x_evap": x_evap, "x_subcool": x_subcool, "x_ihx": x_ihx, } if n_heat: pack_kwargs["x_heat_split"] = np.ones(n_heat, dtype=float) if n_cool: pack_kwargs["x_cool_split"] = np.ones(n_cool, dtype=float) # Encode the seed duties against the availability at the seed's own # temperatures, exactly as the search will decode them. state = _parse_cascade_hp_state_variables(layout.pack(**pack_kwargs), args) if n_heat: pack_kwargs["x_heat_split"] = encode_available_fractions( np.asarray(init_heat, dtype=float)[:n_heat], state.Q_heat_available ) if n_cool: pack_kwargs["x_cool_split"] = encode_available_fractions( np.asarray(init_cool, dtype=float)[:n_cool], state.Q_cool_available ) return clip_to_bounds(layout.pack(**pack_kwargs), bnds), bnds ``` -------------------------------- ### Thermodynamic and zero-recovery boundaries example Source: https://openpinch.readthedocs.io/en/latest/guides/heat-recovery-dt-min.html Requests zero recovery to find the zero_recovery_boundary and then requests the thermodynamic limit to find the at_thermodynamic_limit boundary. Demonstrates the status values returned by the method. ```python zero_boundary = problem.target.heat_recovery_dt_min( heat_recovery=0.0, period_id="0", ) maximum = problem.target.heat_recovery_dt_min( heat_recovery=zero_boundary.thermodynamic_limit.model_dump(), period_id="0", ) assert zero_boundary.status == "zero_recovery_boundary" assert maximum.status == "at_thermodynamic_limit" ``` -------------------------------- ### Parallel HP optimization setup helper function Source: https://openpinch.readthedocs.io/en/latest/_modules/OpenPinch/analysis/heat_pumps/targeting/parallel_vapour_compression.html Builds the bounds for the optimization variables using the parallel layout. Returns None for the initial guess if no initial result is provided. ```python def _get_parallel_hp_opt_setup( init_res: HPRBackendResult | None, args: HeatPumpTargetInputs, ) -> tuple[np.ndarray | None, list]: is_heat_pumping = getattr(args, "is_heat_pumping", True) layout = _parallel_layout(args) bnds = layout.build_bounds( x_cond=(0.0, 1.0), x_evap=(0.0, 1.0), x_subcool=(0.0, 1.0), x_heat_split=(0.0, 1.0), x_cool_split=(0.0, 1.0), x_ihx=(0.0, 1.0), ) if init_res is None: return None, bnds ``` -------------------------------- ### Build distribution with uv run scripts/build_dist.py Source: https://openpinch.readthedocs.io/en/latest/developer/build-and-coverage.html Run from the repository root to build the wheel and source distribution. CI installs the generated wheel on Ubuntu, Windows, and macOS, verifying package import, command-line help, and packaged resources. ```bash uv run scripts/build_dist.py ``` -------------------------------- ### Run complete heat-integration study with PinchProblem Source: https://openpinch.readthedocs.io/en/latest/getting-started.html Install the package, then run a complete heat-integration study. The named target call is the only analysis step; validation, summary, metrics, report, and plot operations inspect prepared or cached state and never decide which analysis to run. ```python from OpenPinch import PinchProblem problem = PinchProblem("basic_pinch.json", project_name="Site") problem.validate() problem.target.all_heat_integration() summary = problem.summary_frame() metrics = problem.metrics() report = problem.report() figure = problem.plot.grand_composite_curve() ``` -------------------------------- ### Runnable cogeneration workflow with PinchProblem Source: https://openpinch.readthedocs.io/en/latest/guides/cogeneration-workflows.html This snippet shows the complete workflow for cogeneration targeting: load the sample case, perform all heat integration, get the total site heat integration base, run cogeneration targeting, and retrieve the summary frame. It requires the base thermal case to be solved first. ```python from OpenPinch import PinchProblem problem = PinchProblem("pulp_mill.json") problem.target.all_heat_integration() base = problem.target.total_site_heat_integration() cogeneration_target = problem.target.cogeneration(base_target=base) summary = problem.summary_frame() ``` -------------------------------- ### Module imports and unit registry setup Source: https://openpinch.readthedocs.io/en/latest/_modules/OpenPinch/domain/value.html Imports required libraries and sets up a Pint UnitRegistry with custom currency units (USD, NZD). The registry is set as the application registry and a Quantity alias is defined. Unit definitions are best-effort and ignore conflicts. ```python """Unit-aware scalar and multiperiod value wrapper powered by Pint quantities.""" from __future__ import annotations from collections.abc import Mapping from typing import Any import numpy as np from pint import UnitRegistry, set_application_registry from pint.errors import DimensionalityError, PintError from ._value import coercion as _value_coercion from ._value import units as _value_units from ._value.coercion import is_bool_like as _is_bool_like from ._value.units import unit_object as _registry_unit_object ureg = UnitRegistry() # Pint reports definition/redefinition conflicts as ``PintError`` subclasses # (``DefinitionError``, ``RedefinitionError``, ``DefinitionSyntaxError``); a # conflict must never break import, so each definition is best-effort. try: ureg.define("USD = [currency]") except PintError: # pragma: no cover - Pint may already know this unit. pass try: ureg.define("NZD = [currency]") except PintError: # pragma: no cover - Pint may already know this unit. pass set_application_registry(ureg) Q_ = ureg.Quantity # type: ignore _SERIALIZED_SCALAR_KEYS = {"value", "unit"} _SERIALIZED_PERIOD_KEYS = {"values", "period_ids", "weights", "unit"} def _unit_object(unit: str): return _registry_unit_object(ureg, unit) ``` -------------------------------- ### Single period heat_recovery_dt_min example Source: https://openpinch.readthedocs.io/en/latest/guides/heat-recovery-dt-min.html Shows how to request a heat recovery target and obtain the equivalent global dt_min. The result includes dt_min, requested and achieved heat recovery, thermodynamic limit, and status. The method does not modify the problem's cached results. ```python from OpenPinch import PinchProblem problem = PinchProblem("basic_pinch.json", project_name="Site") ordinary = problem.target.direct_heat_integration(period_id="0") cached_results_before = problem.results result = problem.target.heat_recovery_dt_min( heat_recovery={"value": 4_000.0, "unit": "kW"}, zone="Site", period_id="0", ) result.dt_min result.requested_heat_recovery result.achieved_heat_recovery result.thermodynamic_limit result.status assert problem.results is cached_results_before assert problem.period_results == {} ``` -------------------------------- ### All periods heat_recovery_dt_min example Source: https://openpinch.readthedocs.io/en/latest/guides/heat-recovery-dt-min.html Shows how to use the all_periods method to compute zero-recovery boundaries and then request a recovery per period. The workers parameter must be a positive integer and parallel workers return the same ordered results as workers=1. ```python periods = PinchProblem( "crude_preheat_train_multiperiod.json", project_name="Crude Train", ) zero_boundaries = ( periods.target.all_periods.heat_recovery_dt_min( heat_recovery=0.0, ) ) recovery_by_period = { period_id: 0.8 * boundary.thermodynamic_limit.value for period_id, boundary in zero_boundaries.items() } period_results = ( periods.target.all_periods.heat_recovery_dt_min( heat_recovery=recovery_by_period, workers=2, ) ) ``` -------------------------------- ### PinchDecompModel class definition Source: https://openpinch.readthedocs.io/en/latest/_modules/OpenPinch/analysis/heat_exchanger_networks/models/pinch_decomposition.html Defines the PinchDecompModel class with its constructor and all methods. The constructor accepts parameters for name, framework, solver, solver arrays, dTmin, z_restriction, min_dqda, minimisation_goal, non_isothermal_model, integers, tol, pinch_loc, pinch_decomposition, stage_selection, and optional solver_options. Methods include setup, get_model_parameters_from_solver_arrays, calculate_pinch, set_preprocessing, set_stage_wise_superstructure, set_obj, get_post_process, and amalgamate_networks. ```python """Pinch-decomposition heat-exchanger-network model coordinator.""" from __future__ import annotations from collections.abc import Mapping, Sequence from typing import Any, Literal from ..solver.arrays import PreparedSolverArrays from ..solver.pinch_design_decomposition import PinchDesignDecomposition from ._pinch_design import amalgamation as _amalgamation from ._pinch_design import equations as _equations from ._pinch_design import postprocess as _postprocess from ._pinch_design import preprocessing as _preprocessing from .base import BaseHeatExchangerNetworkModel from .stagewise import StageWiseModel [docs] class PinchDecompModel(BaseHeatExchangerNetworkModel): """Source-compatible private PDM slice for one pinch side.""" def __init__( self, *, name: str, framework: Literal["PDM"], solver: Literal["couenne", "ipopt-pyomo", "ipopt-GEKKO", "apopt"], solver_arrays: PreparedSolverArrays, dTmin: float, z_restriction: list | None, min_dqda: float, minimisation_goal: Literal[ "hot utility", "cold utility", "total utility", "heat recovery", "min units", ], non_isothermal_model: bool, integers: bool, tol: float, pinch_loc: Literal["above", "below"], pinch_decomposition: PinchDesignDecomposition, stage_selection: Literal["automated"] | list[int] | tuple[int, int], solver_options: Mapping[str, Any] | Sequence[str] | None = None, ) -> None: self.pinch_loc = pinch_loc self.pinch_decomposition = pinch_decomposition self.stage_selection = stage_selection super().__init__( name=name, framework=framework, solver=solver, solver_arrays=solver_arrays, dTmin=dTmin, z_restriction=z_restriction, min_dqda=min_dqda, minimisation_goal=minimisation_goal, non_isothermal_model=non_isothermal_model, integers=integers, tol=tol, solver_options=solver_options, ) [docs] def setup(self) -> None: self.set_blank_input_parameters() self.get_model_parameters_from_solver_arrays() self.calculate_pinch() self.set_preprocessing() self.set_match_restrictions(self.z_restriction) self.set_stage_wise_superstructure() self.set_obj() [docs] def get_model_parameters_from_solver_arrays(self) -> None: super().get_model_parameters_from_solver_arrays() self.T_h_in_OG = self.T_h_in.copy() self.T_h_out_OG = self.T_h_out.copy() self.T_c_in_OG = self.T_c_in.copy() self.T_c_out_OG = self.T_c_out.copy() [docs] def calculate_pinch(self) -> None: """Read target values from the private OpenPinch decomposition.""" return _preprocessing.calculate_pinch(self) [docs] def set_preprocessing(self) -> None: """Pre-process PDM superstructure parameters.""" return _preprocessing.set_preprocessing(self) [docs] def set_stage_wise_superstructure(self) -> None: """Create PDM variables, constraints, and binaries.""" return _equations.set_stage_wise_superstructure(self) def _set_multiperiod_stage_wise_superstructure(self) -> None: """Delegate _set_multiperiod_stage_wise_superstructure to its owner helper.""" return _equations._set_multiperiod_stage_wise_superstructure(self) [docs] def set_obj(self) -> None: """Attach PDM objective expressions.""" return _equations.set_obj(self) [docs] def get_post_process(self) -> None: """Extract source PDM side arrays after a successful solve.""" return _postprocess.get_post_process(self) def _get_multiperiod_post_process(self) -> None: """Delegate _get_multiperiod_post_process to its owner helper.""" return _postprocess._get_multiperiod_post_process(self) def _active_binary_value(self, value) -> float: """Delegate _active_binary_value to its owner helper.""" return _postprocess._active_binary_value(self, value) [docs] def amalgamate_networks( self, *, below_case: "PinchDecompModel", above_case: "PinchDecompModel" ) -> StageWiseModel: """Amalgamate solved above/below-pinch side models into one network.""" return _amalgamation.amalgamate_networks( self, below_case=below_case, above_case=above_case ) __all__ = ["PinchDecompModel"] ``` -------------------------------- ### List sample cases and notebooks Source: https://openpinch.readthedocs.io/en/latest/guides/notebooks-and-sample-cases.html Use the OpenPinch.resources module to print the available sample cases and notebooks. ```python from OpenPinch.resources import ( list_notebooks, list_sample_cases, notebook_metadata, sample_case_metadata, ) print(list_sample_cases()) print(list_notebooks()) ``` -------------------------------- ### Transfer optimized utilities to a new problem with with_utilities_from Source: https://openpinch.readthedocs.io/en/latest/guides/heat-pump-workflows.html Copy utility definitions from an optimized problem into a fresh original-process study. The new problem starts unsolved and does not copy installed HPR equipment or residual results. Period sets must match; ordered values are aligned by ID. Use `project_name=` to override the receiver's name. ```python new_problem = problem.with_utilities_from(optimized) new_results = new_problem.target.all_heat_integration() ``` -------------------------------- ### PinchDecompModel.setup Source: https://openpinch.readthedocs.io/en/latest/reference/api-classes.html Creates concrete equation variables, constraints, and objective. Returns None. ```APIDOC ## PinchDecompModel.setup ### Description Create concrete equation variables, constraints, and objective. ### Method setup ### Return Type None ``` -------------------------------- ### Golden Path: Create workspace, run cases, and compare Source: https://openpinch.readthedocs.io/en/latest/api/pinchworkspace.html Shows the typical workflow: create a workspace, retrieve a baseline case, run direct heat integration, create a retrofit scenario with a dt_cont_multiplier override, run it, and compare the two cases. ```python from OpenPinch import PinchWorkspace workspace = PinchWorkspace("crude_preheat_train.json", project_name="Site") baseline = workspace.case("baseline") baseline.target.direct_heat_integration() retrofit = workspace.scenario("retrofit", dt_cont_multiplier=0.8) retrofit.target.direct_heat_integration() comparison = workspace.compare_cases("baseline", "retrofit") ``` -------------------------------- ### OpenPinch.resources.list_sample_cases() Source: https://openpinch.readthedocs.io/en/latest/api/cli-and-resources.html Returns a list of packaged sample-case filenames. ```APIDOC ## OpenPinch.resources.list_sample_cases() ### Description Returns the packaged sample-case filenames. ### Method Function call ### Endpoint OpenPinch.resources.list_sample_cases() ### Parameters None ### Return Type list[str] ### Return Example ["case1.json", "case2.json"] ``` -------------------------------- ### _find_next_available_utility function Source: https://openpinch.readthedocs.io/en/latest/_modules/OpenPinch/analysis/targeting/direct.html Returns the index of the next utility that still has remaining duty. Starts searching from the given start index. Returns -1 if no utilities exist. ```python def _find_next_available_utility( start: int, utilities: StreamCollection, remaining: List[float] ) -> int: """Return the index of the next utility that still has remaining duty.""" if not utilities: return -1 for idx in range(max(start, 0), len(utilities)): if remaining[idx] > tol: return idx return len(utilities) - 1 ``` -------------------------------- ### OpenPinch.optimisation.service.run_multistart_minimisation Source: https://openpinch.readthedocs.io/en/latest/reference/api-utils.html Runs one backend and returns finite, bounded, feasible candidates in order. ```APIDOC ## OpenPinch.optimisation.service.run_multistart_minimisation ### Description Run one backend and return finite, bounded, feasible candidates in order. ### Method Function ### Signature `run_multistart_minimisation(problem, *, method=OptimisationMethod.DUAL_ANNEALING, options=None)` ### Parameters - **problem** (OptimisationProblem) - Required - The optimisation problem to solve. - **method** (OptimisationMethod | str) - Optional - The optimisation method to use. - **options** (OptimisationOptions | Mapping[str, object] | None) - Optional - Options for the optimisation. ### Return Type OptimisationResult ``` -------------------------------- ### OpenPinch.__main__.main(argv=None) Source: https://openpinch.readthedocs.io/en/latest/api/cli-and-resources.html Execute the OpenPinch CLI. Accepts an optional list of arguments and returns an integer exit code. ```APIDOC ## main(argv=None) ### Description Execute the OpenPinch CLI. ### Method Function ### Signature `OpenPinch.__main__.main(argv=None)` ### Parameters - **argv** (list[str] | None) - Optional - List of command-line arguments. ### Return Type int ### Return Value Exit code of the CLI execution. ``` -------------------------------- ### Identify positive run boundaries in pocket profile Source: https://openpinch.readthedocs.io/en/latest/_modules/OpenPinch/analysis/targeting/grand_composite.html This function finds the start and stop indices of a positive run in the pocket profile array, starting from a given index. It uses a tolerance `tol` to determine positivity and returns the run boundaries. ```python run_start = start_idx + int(positive_idx[0]) non_positive_idx = np.flatnonzero(h_pockets[run_start:] <= tol) run_stop = ( run_start + int(non_positive_idx[0]) if non_positive_idx.size > 0 else h_pockets.size ) return run_start, run_stop ``` -------------------------------- ### OpenPinch.analysis.economics.compute_capital_cost Source: https://openpinch.readthedocs.io/en/latest/reference/api-utils.html Determines capital cost from installed capacity and unit-count assumptions. ```APIDOC ## OpenPinch.analysis.economics.compute_capital_cost ### Description Determines capital cost from installed capacity and unit-count assumptions. ### Method Function ### Signature compute_capital_cost(area, num_units, fixed_cost_factor, variable_cost_factor, n_exp_factor) ### Parameters - **area** (float) - Required - Area. - **num_units** (int) - Required - Number of units. - **fixed_cost_factor** (float) - Required - Fixed cost factor. - **variable_cost_factor** (float) - Required - Variable cost factor. - **n_exp_factor** (float) - Required - Exponent factor. ### Return Type Value ``` -------------------------------- ### Zone.import_hot_and_cold_streams_from_sub_zones() Source: https://openpinch.readthedocs.io/en/latest/api/domain-model.html Gets referenced hot and cold streams across multiple subzones. ```APIDOC ## Zone.import_hot_and_cold_streams_from_sub_zones(get_net_streams=False, is_n_zone_depth=True, is_new_stream_collection=True) ### Description Get referenced hot and cold streams across multiple subzones. ### Method import_hot_and_cold_streams_from_sub_zones() ### Parameters - **get_net_streams** (bool) - Optional - Description not provided. - **is_n_zone_depth** (bool) - Optional - Description not provided. - **is_new_stream_collection** (bool) - Optional - Description not provided. ### Returns None ``` -------------------------------- ### Copy sample cases using OpenPinch.resources Source: https://openpinch.readthedocs.io/en/latest/examples/sample-cases.html Use copy_sample_case and list_sample_cases from OpenPinch.resources to browse or copy packaged cases to a local editable copy. ```python from OpenPinch.resources import copy_sample_case, list_sample_cases print(list_sample_cases()) copy_sample_case("basic_pinch.json", "basic_pinch.json") ``` -------------------------------- ### Configuration.__init__ Source: https://openpinch.readthedocs.io/en/latest/api/schemas-and-config.html Initializes a Configuration instance with optional flat options and top zone name/identifier. ```APIDOC ## Configuration.__init__ ### Description Initializes a Configuration instance with optional flat options and top zone name/identifier. ### Method Constructor ### Signature `Configuration(options=None, top_zone_name='Site', top_zone_identifier='Site')` ### Parameters - **options** (dict | None) - Optional - Flat user-facing options. - **top_zone_name** (str) - Optional - Name of the top zone. Defaults to 'Site'. - **top_zone_identifier** (str) - Optional - Identifier of the top zone. Defaults to 'Site'. ### Return Type Configuration instance ``` -------------------------------- ### calc_hpr_capital_cost Source: https://openpinch.readthedocs.io/en/latest/_modules/OpenPinch/analysis/heat_pumps/common/shared.html Returns the installed capital cost of the heat-pump machines as a sum of the individual machine capital costs. ```APIDOC ## calc_hpr_capital_cost ### Description Returns the installed capital cost of the heat-pump machines. This is the sum of `calc_hpr_machine_capital_costs`. ### Parameters - **units** (Sequence[HPRCostUnit]) - Required - The heat pump cost units. - **args** (HeatPumpTargetInputs) - Required - The heat pump target inputs. ### Returns - **Value** - The total installed capital cost in dollars. ``` -------------------------------- ### Build HTML documentation with uv run scripts/build_docs.py Source: https://openpinch.readthedocs.io/en/latest/developer/build-and-coverage.html Run from the repository root to build the HTML documentation. The generated site is written to docs/_build/html. The helper runs Sphinx with --fail-on-warning --keep-going to fail on warnings before publication. ```bash uv run scripts/build_docs.py ``` -------------------------------- ### Runnable workflow for basic pinch case Source: https://openpinch.readthedocs.io/en/latest/guides/graphing-and-interpretation.html Sets up a PinchProblem from basic_pinch.json, runs all heat integration targeting, and retrieves summary and plot objects. Requires openpinch[notebook] or openpinch[dashboard]. ```python from OpenPinch import PinchProblem problem = PinchProblem("basic_pinch.json") problem.target.all_heat_integration() summary = problem.summary_frame() gcc = problem.plot.grand_composite_curve() cc = problem.plot.composite_curve() catalog = problem.plot.catalog() ``` -------------------------------- ### design.utility(name) Source: https://openpinch.readthedocs.io/en/latest/api/schemas-and-config.html Get utility duties for the selected network. Convenience method that always refers to the selected network. ```APIDOC ## Get Utility Duties ### Description Get utility duties for the selected network. Convenience method that always refers to the selected network. ### Method utility ### Endpoint N/A (SDK method) ### Parameters - **name** (str) - Required - Name of the utility. ### Response #### Success Response - **utility_duties** (object) - Utility duties for the selected network. #### Response Example ```json { "utility_duties": {} } ``` ``` -------------------------------- ### Choosing Between Interfaces Source: https://openpinch.readthedocs.io/en/latest/api/service-layer.html Guidance on when to use PinchProblem, add_process_mvr, and concrete analysis owners. ```APIDOC ## Choosing Between Interfaces ### Description Guidance on selecting the appropriate interface for different use cases. ### Recommendations - Use `OpenPinch.PinchProblem` for supported application integration. - Use `problem.components.add_process_mvr(...)` when the study needs direct gas/vapour MVR stream replacement before ordinary target reruns. - Use concrete analysis owners for repository development or advanced research. ### Additional Notes The service layer is the best place to understand how the narrative workflow maps onto the actual analysis pipeline. ``` -------------------------------- ### ColumnViewByIndex class Source: https://openpinch.readthedocs.io/en/latest/_modules/OpenPinch/domain/problem_table.html Exposes read/write access to columns addressed by integer index. Uses parent.data[:, idx] for get and set. ```python class ColumnViewByIndex: """Expose read/write access to columns addressed by integer index.""" def __init__(self, parent: "ProblemTable"): self.parent = parent def __getitem__(self, idx): return self.parent.data[:, idx] def __setitem__(self, idx, values): self.parent.data[:, idx] = values ``` -------------------------------- ### Configuration.from_options - Build runtime configuration from flat options Source: https://openpinch.readthedocs.io/en/latest/_modules/OpenPinch/domain/configuration.html Classmethod that constructs a Configuration instance from flat user-facing options, passing through top_zone_name and top_zone_identifier. Returns a new Configuration object. ```python @classmethod def from_options( cls, options: dict | None = None, *, top_zone_name: str = CONFIG_FIELD_SPECS["PROBLEM_TOP_ZONE_NAME"].default, top_zone_identifier: str = CONFIG_FIELD_SPECS[ "PROBLEM_TOP_ZONE_IDENTIFIER" ].default, ) -> "Configuration": """Build a runtime configuration from flat user-facing options.""" return cls( options=options, top_zone_name=top_zone_name, top_zone_identifier=top_zone_identifier, ) ``` -------------------------------- ### Streamlit app entry point for OpenPinch dashboard Source: https://openpinch.readthedocs.io/en/latest/_modules/streamlit_app.html Main script to run with `streamlit run streamlit_app.py`. Loads a bundled demonstration case from a configured path, validates the file exists, and launches the interactive dashboard via `problem.show_dashboard()`. Uses `@st.cache_resource` to load and solve the pinch problem once per session. ```python """Streamlit entry point for exploring OpenPinch analysis results. Run with ``streamlit run streamlit_app.py`` to load the bundled demonstration case and launch the interactive dashboard defined in ``OpenPinch.presentation.dashboard``. """ from __future__ import annotations from pathlib import Path import streamlit as st from OpenPinch.application.problem import PinchProblem # Current case. Update this path if you want to switch datasets. PROBLEM_FILE = Path("examples/OpenPinchWkbs/Ziyatdinov et al (example 2).xlsb") @st.cache_resource def _load_problem(problem_path: str) -> PinchProblem: """Load and solve the pinch problem once per Streamlit session.""" problem = PinchProblem(source=problem_path) problem.target.all_heat_integration() return problem [docs] def validate_problem_path(problem_path) -> None: """Stop the Streamlit app early when the configured problem file is missing.""" if not problem_path.exists(): st.error( f"Problem file not found at {problem_path}. " "Update PROBLEM_FILE to point to a valid case." ) st.stop() if __name__ == "__main__": problem_path = PROBLEM_FILE validate_problem_path(problem_path) problem = _load_problem(str(problem_path)) problem.show_dashboard() ``` -------------------------------- ### Find next positive run Source: https://openpinch.readthedocs.io/en/latest/_modules/OpenPinch/analysis/targeting/grand_composite.html Returns the start and stop indices for the next contiguous positive pocket run, or None if none exists. ```python def _next_positive_run( h_pockets: np.ndarray, start_idx: int = 0, ) -> tuple[int, int] | None: """Return ``(start, stop)`` for the next contiguous positive pocket run.""" positive_idx = np.flatnonzero(h_pockets[start_idx:] > tol) if positive_idx.size == 0: return None ``` -------------------------------- ### calc_hpr_machine_capital_costs Source: https://openpinch.readthedocs.io/en/latest/_modules/OpenPinch/analysis/heat_pumps/common/shared.html Returns the installed capital cost of each heat-pump machine in dollars. Each machine's cost is calculated based on its capacity, temperature, and stage counts. ```APIDOC ## calc_hpr_machine_capital_costs ### Description Returns the installed capital cost of each heat-pump machine, in $. Each machine costs: `F_inst * C_eq * (Q_cap / 1 MW)^exp * (fixed_share + stage_share * (n_closed + n_mvr)) * f_T` with `f_T = 1 + temp_factor * max(0, T_hot_max - temp_base) / 100 K`. The machines are costed separately, and a machine with no capacity costs nothing. ### Parameters - **units** (Sequence[HPRCostUnit]) - Required - The heat pump cost units. - **args** (HeatPumpTargetInputs) - Required - The heat pump target inputs. ### Returns - **tuple[float, ...]** - A tuple of installed capital costs for each machine in dollars. ``` -------------------------------- ### Basic PinchProblem usage Source: https://openpinch.readthedocs.io/en/latest/reference/api-core.html Shows how to import PinchProblem and PinchWorkspace from OpenPinch, create a PinchProblem with a JSON file and project name, perform all heat integration, and retrieve a summary frame. ```python from OpenPinch import PinchProblem, PinchWorkspace problem = PinchProblem("basic_pinch.json", project_name="Site") problem.target.all_heat_integration() summary = problem.summary_frame() ``` -------------------------------- ### Module imports and public API for grand_composite Source: https://openpinch.readthedocs.io/en/latest/_modules/OpenPinch/analysis/targeting/grand_composite.html Imports numpy and internal OpenPinch modules, defines the public API list, and starts the module with a docstring. ```python """Determine various forms of the grand composite curve.""" from __future__ import annotations import numpy as np from ...domain._problem_table.types import ProblemTableUpdateKwargs from ...domain.configuration import tol from ...domain.enums import ProblemTableLabel from ...domain.problem_table import ProblemTable from ..numerics import delta_with_zero_at_start, linear_interpolation __all__ = [ "get_additional_GCCs", "get_GCC_without_pockets", "get_GCC_with_partial_pockets", "get_GCC_with_vertical_heat_transfer", "get_GCC_needing_utility", "get_GGC_pockets", "get_seperated_gcc_heat_load_profiles", ] ################################################################################ # Public API ################################################################################ ``` -------------------------------- ### Initialize Vapour Compression Cycle Parameters Source: https://openpinch.readthedocs.io/en/latest/_modules/OpenPinch/analysis/heat_pumps/cycles/vapour_compression_cycle.html Sets up the cycle parameters: refrigerant, evaporator/condenser temperatures, temperature differences, heat duties (heat pump vs refrigeration), compressor efficiency, and internal heat exchanger temperature difference. Validates that evaporator pressure is below condenser pressure. ```python self._refrigerant = refrigerant self._T_evap = self._convert_C_to_K(T_evap) self._T_cond = self._convert_C_to_K(T_cond) self._dtcont = float(dtcont) self._dT_superheat = dT_superheat self._dT_subcool = dT_subcool if is_heat_pump: self._Q_heat = np.float64(1.0) if Q_heat is None else Q_heat self._Q_cas_heat = Q_cas_heat if Q_cas_heat is not None else 0.0 self._Q_cool = Q_cool self._Q_cas_cool = 0.0 self._Q_cond = self._Q_heat + self._Q_cas_heat else: # is refrigeration self._Q_heat = Q_heat self._Q_cas_heat = 0.0 self._Q_cool = np.float64(1.0) if Q_cool is None else Q_cool self._Q_cas_cool = Q_cas_cool if Q_cas_cool is not None else 0.0 self._Q_evap = self._Q_cool + self._Q_cas_cool self._eta_comp = eta_comp self._ihx_gas_dt = max( min( dT_ihx_gas_side, self._T_cond - self._T_evap - self._dT_subcool - self._dT_superheat - self._dtcont * 2, ), 0.0, ) P_lo = self._get_P_sat_from_T(self._T_evap, Q=1.0) P_hi = self._get_P_sat_from_T(self._T_cond, Q=0.0) if P_lo > P_hi: raise ValueError("Evaporator pressure must be below condenser pressure.") ``` -------------------------------- ### _normalise_curve helper function Source: https://openpinch.readthedocs.io/en/latest/_modules/OpenPinch/analysis/targeting/temperature_driving_force.html Orients a composite curve and shifts heat flow so it starts at zero. Raises ValueError if heat-flow and temperature arrays do not have equal length. ```python def _normalise_curve( h_vals: np.ndarray, t_vals: np.ndarray ) -> tuple[np.ndarray, np.ndarray]: """Orient a composite curve and shift heat flow so it starts at zero. Parameters ---------- h_vals : np.ndarray Heat-flow coordinates. t_vals : np.ndarray Temperature coordinates aligned with ``h_vals``. Returns ------- tuple[np.ndarray, np.ndarray] Normalized heat-flow and temperature arrays. Raises ------ ValueError If ``h_vals`` and ``t_vals`` do not have equal length. """ if h_vals.size != t_vals.size: raise ValueError( "Composite curve temperature and heat arrays must be the same length." ) ``` -------------------------------- ### Get cogeneration candidate order Source: https://openpinch.readthedocs.io/en/latest/_modules/OpenPinch/analysis/power/service.html Returns the exact cogeneration target search order for a call, either a single target type if provided or the full implicit order. ```python def _get_cogeneration_candidate_order( base_target_type: str | None, ) -> tuple[str, ...]: """Return the exact cogeneration target search order for this call.""" if base_target_type is not None: return (base_target_type,) return _COGENERATION_TARGET_ORDER ``` -------------------------------- ### StageWiseModel.setup Source: https://openpinch.readthedocs.io/en/latest/reference/api-classes.html Creates concrete equation variables, constraints, and objective for the model. ```APIDOC ## StageWiseModel.setup ### Description Create concrete equation variables, constraints, and objective. ### Method setup() ### Return Type None ``` -------------------------------- ### Typical Preparation and Solve Pattern Source: https://openpinch.readthedocs.io/en/latest/api/service-layer.html Shows the standard workflow for preparing input data and running direct and indirect heat integration services. The services mutate the zone in place and record period metadata. ```python from OpenPinch.contracts.input import TargetInput from OpenPinch.analysis import ( data_preprocessing_service, direct_heat_integration_service, indirect_heat_integration_service, ) source_data = {"streams": [...], "utilities": [...]} input_data = TargetInput.model_validate(source_data) zone = data_preprocessing_service(input_data, project_name="Example") direct_heat_integration_service(zone, {"period_id": "peak"}) indirect_heat_integration_service(zone, {"period_id": "peak"}) ``` -------------------------------- ### Configuration.from_options Source: https://openpinch.readthedocs.io/en/latest/api/schemas-and-config.html Class method to build a runtime configuration from flat user-facing options. ```APIDOC ## Configuration.from_options ### Description Build a runtime configuration from flat user-facing options. ### Method Classmethod ### Signature `from_options(options=None, *, top_zone_name='Site', top_zone_identifier='Site')` ### Parameters - **options** (dict | None) - Optional - Flat user-facing options. - **top_zone_name** (str) - Optional - Name of the top zone. Defaults to 'Site'. - **top_zone_identifier** (str) - Optional - Identifier of the top zone. Defaults to 'Site'. ### Return Type Configuration ```