OpenPinch 0.2.8__tar.gz → 0.2.9__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (153) hide show
  1. {openpinch-0.2.8 → openpinch-0.2.9}/OpenPinch/classes/pinch_workspace.py +4 -4
  2. openpinch-0.2.9/OpenPinch/data/notebooks/03_carnot_hpr_comparison.ipynb +347 -0
  3. {openpinch-0.2.8 → openpinch-0.2.9}/OpenPinch/lib/config.py +23 -0
  4. {openpinch-0.2.8 → openpinch-0.2.9}/OpenPinch/lib/config_metadata.py +30 -118
  5. {openpinch-0.2.8 → openpinch-0.2.9}/OpenPinch/lib/schemas/hpr.py +68 -20
  6. {openpinch-0.2.8 → openpinch-0.2.9}/OpenPinch/lib/schemas/reporting.py +12 -0
  7. {openpinch-0.2.8 → openpinch-0.2.9}/OpenPinch/lib/schemas/targets.py +36 -0
  8. {openpinch-0.2.8 → openpinch-0.2.9}/OpenPinch/lib/unit_system.py +32 -2
  9. {openpinch-0.2.8 → openpinch-0.2.9}/OpenPinch/services/heat_pump_integration/common/__init__.py +0 -4
  10. openpinch-0.2.9/OpenPinch/services/heat_pump_integration/common/_shared/__init__.py +1 -0
  11. openpinch-0.2.9/OpenPinch/services/heat_pump_integration/common/_shared/ambient_preallocation.py +253 -0
  12. openpinch-0.2.9/OpenPinch/services/heat_pump_integration/common/_shared/plotting.py +134 -0
  13. openpinch-0.2.9/OpenPinch/services/heat_pump_integration/common/_shared/streams.py +136 -0
  14. {openpinch-0.2.8 → openpinch-0.2.9}/OpenPinch/services/heat_pump_integration/common/encoding.py +4 -6
  15. {openpinch-0.2.8 → openpinch-0.2.9}/OpenPinch/services/heat_pump_integration/common/preprocessing.py +63 -17
  16. {openpinch-0.2.8 → openpinch-0.2.9}/OpenPinch/services/heat_pump_integration/common/shared.py +196 -368
  17. {openpinch-0.2.8 → openpinch-0.2.9}/OpenPinch/services/heat_pump_integration/heat_pump_and_refrigeration_entry.py +7 -1
  18. {openpinch-0.2.8 → openpinch-0.2.9}/OpenPinch/services/heat_pump_integration/targeting_services/cascade_vapour_compression.py +24 -12
  19. {openpinch-0.2.8 → openpinch-0.2.9}/OpenPinch/services/heat_pump_integration/targeting_services/parallel_vapour_compression.py +23 -13
  20. {openpinch-0.2.8 → openpinch-0.2.9}/OpenPinch/services/heat_pump_integration/targeting_services/vapour_compression_mvr.py +29 -12
  21. {openpinch-0.2.8 → openpinch-0.2.9}/OpenPinch/services/heat_pump_integration/unit_models/carnot_cycles.py +6 -9
  22. {openpinch-0.2.8 → openpinch-0.2.9}/OpenPinch/services/heat_pump_integration/unit_models/cascade_vapour_compression_cycle.py +5 -0
  23. {openpinch-0.2.8 → openpinch-0.2.9}/OpenPinch/services/heat_pump_integration/unit_models/parallel_vapour_compression_cycles.py +5 -0
  24. {openpinch-0.2.8 → openpinch-0.2.9}/OpenPinch/services/heat_pump_integration/unit_models/vapour_compression_cycle.py +4 -0
  25. {openpinch-0.2.8 → openpinch-0.2.9}/OpenPinch/services/heat_pump_integration/unit_models/vapour_compression_mvr_cascade.py +22 -29
  26. {openpinch-0.2.8 → openpinch-0.2.9}/OpenPinch/utils/__init__.py +2 -0
  27. openpinch-0.2.9/OpenPinch/utils/costing.py +78 -0
  28. {openpinch-0.2.8 → openpinch-0.2.9}/OpenPinch/utils/export.py +30 -0
  29. {openpinch-0.2.8 → openpinch-0.2.9}/PKG-INFO +6 -2
  30. {openpinch-0.2.8 → openpinch-0.2.9}/README.md +5 -1
  31. {openpinch-0.2.8 → openpinch-0.2.9}/pyproject.toml +2 -1
  32. openpinch-0.2.8/OpenPinch/data/notebooks/03_carnot_hpr_comparison.ipynb +0 -318
  33. openpinch-0.2.8/OpenPinch/utils/costing.py +0 -42
  34. {openpinch-0.2.8 → openpinch-0.2.9}/.gitignore +0 -0
  35. {openpinch-0.2.8 → openpinch-0.2.9}/LICENSE +0 -0
  36. {openpinch-0.2.8 → openpinch-0.2.9}/OpenPinch/__init__.py +0 -0
  37. {openpinch-0.2.8 → openpinch-0.2.9}/OpenPinch/__main__.py +0 -0
  38. {openpinch-0.2.8 → openpinch-0.2.9}/OpenPinch/classes/__init__.py +0 -0
  39. {openpinch-0.2.8 → openpinch-0.2.9}/OpenPinch/classes/_problem/__init__.py +0 -0
  40. {openpinch-0.2.8 → openpinch-0.2.9}/OpenPinch/classes/_problem/_component_accessor.py +0 -0
  41. {openpinch-0.2.8 → openpinch-0.2.9}/OpenPinch/classes/_problem/_loading.py +0 -0
  42. {openpinch-0.2.8 → openpinch-0.2.9}/OpenPinch/classes/_problem/_output.py +0 -0
  43. {openpinch-0.2.8 → openpinch-0.2.9}/OpenPinch/classes/_problem/_plot_accessor.py +0 -0
  44. {openpinch-0.2.8 → openpinch-0.2.9}/OpenPinch/classes/_problem/_result_extraction.py +0 -0
  45. {openpinch-0.2.8 → openpinch-0.2.9}/OpenPinch/classes/_problem/_target_accessor.py +0 -0
  46. {openpinch-0.2.8 → openpinch-0.2.9}/OpenPinch/classes/_problem/_target_dispatch.py +0 -0
  47. {openpinch-0.2.8 → openpinch-0.2.9}/OpenPinch/classes/_problem/_validation.py +0 -0
  48. {openpinch-0.2.8 → openpinch-0.2.9}/OpenPinch/classes/_problem_table/__init__.py +0 -0
  49. {openpinch-0.2.8 → openpinch-0.2.9}/OpenPinch/classes/_problem_table/_problem_table_constants.py +0 -0
  50. {openpinch-0.2.8 → openpinch-0.2.9}/OpenPinch/classes/_stream_collection/__init__.py +0 -0
  51. {openpinch-0.2.8 → openpinch-0.2.9}/OpenPinch/classes/_stream_collection/_helpers.py +0 -0
  52. {openpinch-0.2.8 → openpinch-0.2.9}/OpenPinch/classes/_stream_collection/_numeric_view.py +0 -0
  53. {openpinch-0.2.8 → openpinch-0.2.9}/OpenPinch/classes/_workspace/__init__.py +0 -0
  54. {openpinch-0.2.8 → openpinch-0.2.9}/OpenPinch/classes/_workspace/execution.py +0 -0
  55. {openpinch-0.2.8 → openpinch-0.2.9}/OpenPinch/classes/_workspace/payloads.py +0 -0
  56. {openpinch-0.2.8 → openpinch-0.2.9}/OpenPinch/classes/_workspace/views.py +0 -0
  57. {openpinch-0.2.8 → openpinch-0.2.9}/OpenPinch/classes/pinch_problem.py +0 -0
  58. {openpinch-0.2.8 → openpinch-0.2.9}/OpenPinch/classes/problem_table.py +0 -0
  59. {openpinch-0.2.8 → openpinch-0.2.9}/OpenPinch/classes/stream.py +0 -0
  60. {openpinch-0.2.8 → openpinch-0.2.9}/OpenPinch/classes/stream_collection.py +0 -0
  61. {openpinch-0.2.8 → openpinch-0.2.9}/OpenPinch/classes/value.py +0 -0
  62. {openpinch-0.2.8 → openpinch-0.2.9}/OpenPinch/classes/zone.py +0 -0
  63. {openpinch-0.2.8 → openpinch-0.2.9}/OpenPinch/data/__init__.py +0 -0
  64. {openpinch-0.2.8 → openpinch-0.2.9}/OpenPinch/data/notebooks/01_basic_pinch_and_dtcont_sensitivity.ipynb +0 -0
  65. {openpinch-0.2.8 → openpinch-0.2.9}/OpenPinch/data/notebooks/02_total_site_targets_and_sugcc.ipynb +0 -0
  66. {openpinch-0.2.8 → openpinch-0.2.9}/OpenPinch/data/notebooks/04_multistate_targeting_and_state_comparison.ipynb +0 -0
  67. {openpinch-0.2.8 → openpinch-0.2.9}/OpenPinch/data/notebooks/05_schema_service_and_output_workflows.ipynb +0 -0
  68. {openpinch-0.2.8 → openpinch-0.2.9}/OpenPinch/data/notebooks/06_energy_transfer_analysis.ipynb +0 -0
  69. {openpinch-0.2.8 → openpinch-0.2.9}/OpenPinch/data/notebooks/07_vapour_compression_mvr_cascade_hpr.ipynb +0 -0
  70. {openpinch-0.2.8 → openpinch-0.2.9}/OpenPinch/data/notebooks/08_direct_gas_stream_mvr.ipynb +0 -0
  71. {openpinch-0.2.8 → openpinch-0.2.9}/OpenPinch/data/notebooks/__init__.py +0 -0
  72. {openpinch-0.2.8 → openpinch-0.2.9}/OpenPinch/data/sample_cases/__init__.py +0 -0
  73. {openpinch-0.2.8 → openpinch-0.2.9}/OpenPinch/data/sample_cases/basic_pinch.json +0 -0
  74. {openpinch-0.2.8 → openpinch-0.2.9}/OpenPinch/data/sample_cases/chocolate_factory.json +0 -0
  75. {openpinch-0.2.8 → openpinch-0.2.9}/OpenPinch/data/sample_cases/crude_preheat_train.json +0 -0
  76. {openpinch-0.2.8 → openpinch-0.2.9}/OpenPinch/data/sample_cases/crude_preheat_train_multistate.json +0 -0
  77. {openpinch-0.2.8 → openpinch-0.2.9}/OpenPinch/data/sample_cases/heat_pump_targeting.json +0 -0
  78. {openpinch-0.2.8 → openpinch-0.2.9}/OpenPinch/data/sample_cases/pulp_mill.json +0 -0
  79. {openpinch-0.2.8 → openpinch-0.2.9}/OpenPinch/data/sample_cases/zonal_site.json +0 -0
  80. {openpinch-0.2.8 → openpinch-0.2.9}/OpenPinch/data/sample_cases/zonal_site_multistate.json +0 -0
  81. {openpinch-0.2.8 → openpinch-0.2.9}/OpenPinch/lib/__init__.py +0 -0
  82. {openpinch-0.2.8 → openpinch-0.2.9}/OpenPinch/lib/coolprop_fluids.py +0 -0
  83. {openpinch-0.2.8 → openpinch-0.2.9}/OpenPinch/lib/enums.py +0 -0
  84. {openpinch-0.2.8 → openpinch-0.2.9}/OpenPinch/lib/problem_table_types.py +0 -0
  85. {openpinch-0.2.8 → openpinch-0.2.9}/OpenPinch/lib/schemas/__init__.py +0 -0
  86. {openpinch-0.2.8 → openpinch-0.2.9}/OpenPinch/lib/schemas/common.py +0 -0
  87. {openpinch-0.2.8 → openpinch-0.2.9}/OpenPinch/lib/schemas/graphs.py +0 -0
  88. {openpinch-0.2.8 → openpinch-0.2.9}/OpenPinch/lib/schemas/io.py +0 -0
  89. {openpinch-0.2.8 → openpinch-0.2.9}/OpenPinch/lib/schemas/report_units.py +0 -0
  90. {openpinch-0.2.8 → openpinch-0.2.9}/OpenPinch/lib/schemas/turbine.py +0 -0
  91. {openpinch-0.2.8 → openpinch-0.2.9}/OpenPinch/lib/schemas/workspace.py +0 -0
  92. {openpinch-0.2.8 → openpinch-0.2.9}/OpenPinch/main.py +0 -0
  93. {openpinch-0.2.8 → openpinch-0.2.9}/OpenPinch/resources.py +0 -0
  94. {openpinch-0.2.8 → openpinch-0.2.9}/OpenPinch/services/__init__.py +0 -0
  95. {openpinch-0.2.8 → openpinch-0.2.9}/OpenPinch/services/common/__init__.py +0 -0
  96. {openpinch-0.2.8 → openpinch-0.2.9}/OpenPinch/services/common/capital_cost_and_area_targeting.py +0 -0
  97. {openpinch-0.2.8 → openpinch-0.2.9}/OpenPinch/services/common/gcc_manipulation.py +0 -0
  98. {openpinch-0.2.8 → openpinch-0.2.9}/OpenPinch/services/common/graph_data.py +0 -0
  99. {openpinch-0.2.8 → openpinch-0.2.9}/OpenPinch/services/common/graph_series_meta.py +0 -0
  100. {openpinch-0.2.8 → openpinch-0.2.9}/OpenPinch/services/common/miscellaneous.py +0 -0
  101. {openpinch-0.2.8 → openpinch-0.2.9}/OpenPinch/services/common/problem_table_analysis.py +0 -0
  102. {openpinch-0.2.8 → openpinch-0.2.9}/OpenPinch/services/common/service_orchestration.py +0 -0
  103. {openpinch-0.2.8 → openpinch-0.2.9}/OpenPinch/services/common/temperature_driving_force.py +0 -0
  104. {openpinch-0.2.8 → openpinch-0.2.9}/OpenPinch/services/common/utility_targeting.py +0 -0
  105. {openpinch-0.2.8 → openpinch-0.2.9}/OpenPinch/services/components/__init__.py +0 -0
  106. {openpinch-0.2.8 → openpinch-0.2.9}/OpenPinch/services/components/direct_mvr/__init__.py +0 -0
  107. {openpinch-0.2.8 → openpinch-0.2.9}/OpenPinch/services/components/direct_mvr/direct_gas_mvr.py +0 -0
  108. {openpinch-0.2.8 → openpinch-0.2.9}/OpenPinch/services/components/process_components.py +0 -0
  109. {openpinch-0.2.8 → openpinch-0.2.9}/OpenPinch/services/components/process_mvr.py +0 -0
  110. {openpinch-0.2.8 → openpinch-0.2.9}/OpenPinch/services/direct_heat_integration/__init__.py +0 -0
  111. {openpinch-0.2.8 → openpinch-0.2.9}/OpenPinch/services/direct_heat_integration/direct_integration_entry.py +0 -0
  112. {openpinch-0.2.8 → openpinch-0.2.9}/OpenPinch/services/energy_transfer_analysis/__init__.py +0 -0
  113. {openpinch-0.2.8 → openpinch-0.2.9}/OpenPinch/services/energy_transfer_analysis/energy_transfer_entry.py +0 -0
  114. {openpinch-0.2.8 → openpinch-0.2.9}/OpenPinch/services/exergy_analysis/__init__.py +0 -0
  115. {openpinch-0.2.8 → openpinch-0.2.9}/OpenPinch/services/exergy_analysis/exergy_targeting_entry.py +0 -0
  116. {openpinch-0.2.8 → openpinch-0.2.9}/OpenPinch/services/heat_pump_integration/__init__.py +0 -0
  117. {openpinch-0.2.8 → openpinch-0.2.9}/OpenPinch/services/heat_pump_integration/common/layout.py +0 -0
  118. {openpinch-0.2.8 → openpinch-0.2.9}/OpenPinch/services/heat_pump_integration/common/postprocessing.py +0 -0
  119. {openpinch-0.2.8 → openpinch-0.2.9}/OpenPinch/services/heat_pump_integration/targeting_services/__init__.py +0 -0
  120. {openpinch-0.2.8 → openpinch-0.2.9}/OpenPinch/services/heat_pump_integration/targeting_services/brayton.py +0 -0
  121. {openpinch-0.2.8 → openpinch-0.2.9}/OpenPinch/services/heat_pump_integration/targeting_services/cascade_carnot.py +0 -0
  122. {openpinch-0.2.8 → openpinch-0.2.9}/OpenPinch/services/heat_pump_integration/targeting_services/parallel_carnot.py +0 -0
  123. {openpinch-0.2.8 → openpinch-0.2.9}/OpenPinch/services/heat_pump_integration/unit_models/__init__.py +0 -0
  124. {openpinch-0.2.8 → openpinch-0.2.9}/OpenPinch/services/heat_pump_integration/unit_models/brayton_heat_pump.py +0 -0
  125. {openpinch-0.2.8 → openpinch-0.2.9}/OpenPinch/services/heat_pump_integration/unit_models/mechanical_vapour_recompression_cycle.py +0 -0
  126. {openpinch-0.2.8 → openpinch-0.2.9}/OpenPinch/services/indirect_heat_integration/__init__.py +0 -0
  127. {openpinch-0.2.8 → openpinch-0.2.9}/OpenPinch/services/indirect_heat_integration/indirect_integration_entry.py +0 -0
  128. {openpinch-0.2.8 → openpinch-0.2.9}/OpenPinch/services/input_data_processing/__init__.py +0 -0
  129. {openpinch-0.2.8 → openpinch-0.2.9}/OpenPinch/services/input_data_processing/_canonicalization.py +0 -0
  130. {openpinch-0.2.8 → openpinch-0.2.9}/OpenPinch/services/input_data_processing/_utility_preparation.py +0 -0
  131. {openpinch-0.2.8 → openpinch-0.2.9}/OpenPinch/services/input_data_processing/data_preparation.py +0 -0
  132. {openpinch-0.2.8 → openpinch-0.2.9}/OpenPinch/services/power_cogeneration/__init__.py +0 -0
  133. {openpinch-0.2.8 → openpinch-0.2.9}/OpenPinch/services/power_cogeneration/power_cogeneration_entry.py +0 -0
  134. {openpinch-0.2.8 → openpinch-0.2.9}/OpenPinch/services/power_cogeneration/unit_models/multi_stage_steam_turbine.py +0 -0
  135. {openpinch-0.2.8 → openpinch-0.2.9}/OpenPinch/services/services_entry.py +0 -0
  136. {openpinch-0.2.8 → openpinch-0.2.9}/OpenPinch/streamlit_webviewer/web_graphing.py +0 -0
  137. {openpinch-0.2.8 → openpinch-0.2.9}/OpenPinch/utils/_tabular_input.py +0 -0
  138. {openpinch-0.2.8 → openpinch-0.2.9}/OpenPinch/utils/bb_optimisers/__init__.py +0 -0
  139. {openpinch-0.2.8 → openpinch-0.2.9}/OpenPinch/utils/bb_optimisers/bayesian_optimisation.py +0 -0
  140. {openpinch-0.2.8 → openpinch-0.2.9}/OpenPinch/utils/bb_optimisers/cmaes.py +0 -0
  141. {openpinch-0.2.8 → openpinch-0.2.9}/OpenPinch/utils/bb_optimisers/common.py +0 -0
  142. {openpinch-0.2.8 → openpinch-0.2.9}/OpenPinch/utils/bb_optimisers/dual_annealing.py +0 -0
  143. {openpinch-0.2.8 → openpinch-0.2.9}/OpenPinch/utils/bb_optimisers/rbf_surrogate.py +0 -0
  144. {openpinch-0.2.8 → openpinch-0.2.9}/OpenPinch/utils/blackbox_minimisers.py +0 -0
  145. {openpinch-0.2.8 → openpinch-0.2.9}/OpenPinch/utils/csv_to_json.py +0 -0
  146. {openpinch-0.2.8 → openpinch-0.2.9}/OpenPinch/utils/decorators.py +0 -0
  147. {openpinch-0.2.8 → openpinch-0.2.9}/OpenPinch/utils/heat_exchanger.py +0 -0
  148. {openpinch-0.2.8 → openpinch-0.2.9}/OpenPinch/utils/input_validation.py +0 -0
  149. {openpinch-0.2.8 → openpinch-0.2.9}/OpenPinch/utils/plots.py +0 -0
  150. {openpinch-0.2.8 → openpinch-0.2.9}/OpenPinch/utils/stream_linearisation.py +0 -0
  151. {openpinch-0.2.8 → openpinch-0.2.9}/OpenPinch/utils/value_resolution.py +0 -0
  152. {openpinch-0.2.8 → openpinch-0.2.9}/OpenPinch/utils/water_properties.py +0 -0
  153. {openpinch-0.2.8 → openpinch-0.2.9}/OpenPinch/utils/wkbook_to_json.py +0 -0
@@ -339,14 +339,14 @@ class PinchWorkspace:
339
339
 
340
340
  def copy_case(
341
341
  self,
342
- source_name: str,
343
- new_name: str,
344
342
  *,
343
+ source_name: str = "baseline",
344
+ new_name: str = "new",
345
345
  activate: bool = False,
346
346
  ) -> PinchProblem:
347
347
  """Clone one existing case into a new named case."""
348
- payload = self.get_case_payload(source_name, canonical=True)
349
- return self.load(payload, case_name=new_name, activate=activate)
348
+ data_source = self.get_case_payload(source_name, canonical=True)
349
+ return self.load(data_source, case_name=new_name, activate=activate)
350
350
 
351
351
  def get_case_payload(
352
352
  self,
@@ -0,0 +1,347 @@
1
+ {
2
+ "cells": [
3
+ {
4
+ "cell_type": "markdown",
5
+ "id": "title",
6
+ "metadata": {},
7
+ "source": [
8
+ "# Carnot HPR Comparison\n",
9
+ "This notebook uses the packaged `chocolate_factory.json` case to compare direct versus indirect heat-pump placement and `HPRcycle.CascadeCarnot` versus `HPRcycle.ParallelCarnot` screening. The optimisation is the expensive step, so the notebook creates four named cases, solves each once, and reuses the solved targets for tables and plots."
10
+ ]
11
+ },
12
+ {
13
+ "cell_type": "markdown",
14
+ "id": "context",
15
+ "metadata": {},
16
+ "source": [
17
+ "## Case context\n",
18
+ "`chocolate_factory.json` is a realistic multi-zone plant case with enough structure to support HPR screening. `PinchWorkspace` keeps the study bundle organized, while each named case still behaves like a real `PinchProblem`. The Carnot backends use base duty scales plus bounded split fractions internally, so trial optimiser points are clipped to process availability before cycle work is evaluated."
19
+ ]
20
+ },
21
+ {
22
+ "cell_type": "code",
23
+ "execution_count": null,
24
+ "id": "imports",
25
+ "metadata": {},
26
+ "outputs": [],
27
+ "source": [
28
+ "import pandas as pd\n",
29
+ "\n",
30
+ "from IPython.display import display\n",
31
+ "\n",
32
+ "from OpenPinch import PinchWorkspace\n",
33
+ "from OpenPinch.lib.enums import HPRcycle\n",
34
+ "from OpenPinch.utils import get_scalar_value\n",
35
+ "\n",
36
+ "PROJECT_NAME = \"Chocolate factory\"\n",
37
+ "LOAD_FRACTION = 1.0\n",
38
+ "MAX_HP_MULTISTART = 10\n",
39
+ "N_COND = 3\n",
40
+ "N_EVAP = 3\n",
41
+ "\n",
42
+ "WORKFLOWS = {\n",
43
+ " \"direct_heat_pump\": {\n",
44
+ " \"label\": \"Direct Heat Pump\",\n",
45
+ " \"target_name\": f\"{PROJECT_NAME}/Direct Heat Pump\",\n",
46
+ " \"requires_base_target\": False,\n",
47
+ " },\n",
48
+ " \"indirect_heat_pump\": {\n",
49
+ " \"label\": \"Indirect Heat Pump\",\n",
50
+ " \"target_name\": f\"{PROJECT_NAME}/Indirect Heat Pump\",\n",
51
+ " \"requires_base_target\": True,\n",
52
+ " },\n",
53
+ "}\n",
54
+ "\n",
55
+ "CYCLE_OPTIONS = {\n",
56
+ " \"CascadeCarnot\": HPRcycle.CascadeCarnot,\n",
57
+ " \"ParallelCarnot\": HPRcycle.ParallelCarnot,\n",
58
+ "}"
59
+ ]
60
+ },
61
+ {
62
+ "cell_type": "code",
63
+ "execution_count": null,
64
+ "id": "setup",
65
+ "metadata": {},
66
+ "outputs": [],
67
+ "source": [
68
+ "workspace = PinchWorkspace(source=\"chocolate_factory.json\", project_name=PROJECT_NAME)\n",
69
+ "\n",
70
+ "case_matrix = []\n",
71
+ "for cycle_key, cycle in CYCLE_OPTIONS.items():\n",
72
+ " for workflow_name, workflow in WORKFLOWS.items():\n",
73
+ " case_name = f\"{cycle_key}__{workflow_name}\"\n",
74
+ " case = workspace.copy_case(\n",
75
+ " source_name=\"baseline\",\n",
76
+ " new_name=case_name,\n",
77
+ " activate=False,\n",
78
+ " )\n",
79
+ " case.update_options(\n",
80
+ " {\n",
81
+ " \"HPR_TYPE\": cycle.value,\n",
82
+ " \"HPR_LOAD_VALUE\": LOAD_FRACTION,\n",
83
+ " \"MAX_HP_MULTISTART\": MAX_HP_MULTISTART,\n",
84
+ " \"N_COND\": N_COND,\n",
85
+ " \"N_EVAP\": N_EVAP,\n",
86
+ " }\n",
87
+ " )\n",
88
+ " case_matrix.append(\n",
89
+ " {\n",
90
+ " \"case\": case_name,\n",
91
+ " \"cycle\": cycle_key,\n",
92
+ " \"cycle option\": cycle.value,\n",
93
+ " \"workflow\": workflow_name,\n",
94
+ " \"placement\": workflow[\"label\"],\n",
95
+ " \"target name\": workflow[\"target_name\"],\n",
96
+ " }\n",
97
+ " )\n",
98
+ "\n",
99
+ "pd.DataFrame(case_matrix)"
100
+ ]
101
+ },
102
+ {
103
+ "cell_type": "markdown",
104
+ "id": "solve-one-md",
105
+ "metadata": {},
106
+ "source": [
107
+ "## Solve the four comparison cases once\n",
108
+ "Each row below is one optimiser run. Keep this cell as the single place where the notebook calls `problem.target.direct_heat_pump()` or `problem.target.indirect_heat_pump()`; later cells reuse `SOLVED_TARGETS` and the cached `comparison` frame."
109
+ ]
110
+ },
111
+ {
112
+ "cell_type": "code",
113
+ "execution_count": null,
114
+ "id": "solve-one",
115
+ "metadata": {},
116
+ "outputs": [],
117
+ "source": [
118
+ "SOLVED_TARGETS = {}\n",
119
+ "rows = []\n",
120
+ "\n",
121
+ "for spec in case_matrix:\n",
122
+ " problem = workspace.case(spec[\"case\"])\n",
123
+ " workflow = WORKFLOWS[spec[\"workflow\"]]\n",
124
+ " try:\n",
125
+ " if workflow[\"requires_base_target\"]:\n",
126
+ " problem.target()\n",
127
+ " target = getattr(problem.target, spec[\"workflow\"])()\n",
128
+ " SOLVED_TARGETS[spec[\"case\"]] = target\n",
129
+ " rows.append(\n",
130
+ " {\n",
131
+ " \"cycle\": spec[\"cycle\"],\n",
132
+ " \"placement\": spec[\"placement\"],\n",
133
+ " \"case\": spec[\"case\"],\n",
134
+ " \"target\": target.name,\n",
135
+ " \"Qh\": get_scalar_value(getattr(target, \"Qh\", None)),\n",
136
+ " \"Qc\": get_scalar_value(getattr(target, \"Qc\", None)),\n",
137
+ " \"Qr\": get_scalar_value(getattr(target, \"Qr\", None)),\n",
138
+ " \"hpr_utility_total\": get_scalar_value(\n",
139
+ " getattr(target, \"hpr_utility_total\", None)\n",
140
+ " ),\n",
141
+ " \"hpr_external_utility\": get_scalar_value(\n",
142
+ " getattr(target, \"hpr_external_utility\", None)\n",
143
+ " ),\n",
144
+ " \"hpr_work\": get_scalar_value(getattr(target, \"hpr_work\", None)),\n",
145
+ " \"hpr_cop\": get_scalar_value(getattr(target, \"hpr_cop\", None)),\n",
146
+ " \"hpr_success\": getattr(target, \"hpr_success\", None),\n",
147
+ " \"graph_count\": len(problem.plot.catalog()),\n",
148
+ " \"error\": None,\n",
149
+ " }\n",
150
+ " )\n",
151
+ " except Exception as exc:\n",
152
+ " rows.append(\n",
153
+ " {\n",
154
+ " \"cycle\": spec[\"cycle\"],\n",
155
+ " \"placement\": spec[\"placement\"],\n",
156
+ " \"case\": spec[\"case\"],\n",
157
+ " \"target\": None,\n",
158
+ " \"Qh\": None,\n",
159
+ " \"Qc\": None,\n",
160
+ " \"Qr\": None,\n",
161
+ " \"hpr_utility_total\": None,\n",
162
+ " \"hpr_external_utility\": None,\n",
163
+ " \"hpr_work\": None,\n",
164
+ " \"hpr_cop\": None,\n",
165
+ " \"hpr_success\": False,\n",
166
+ " \"graph_count\": len(problem.plot.catalog()),\n",
167
+ " \"error\": f\"{type(exc).__name__}: {exc}\",\n",
168
+ " }\n",
169
+ " )\n",
170
+ "\n",
171
+ "comparison = pd.DataFrame(rows)\n",
172
+ "comparison"
173
+ ]
174
+ },
175
+ {
176
+ "cell_type": "markdown",
177
+ "id": "plot-md",
178
+ "metadata": {},
179
+ "source": [
180
+ "## Direct versus indirect, Cascade versus Parallel\n",
181
+ "The tables below reuse `comparison`; they do not trigger additional optimisation calls."
182
+ ]
183
+ },
184
+ {
185
+ "cell_type": "code",
186
+ "execution_count": null,
187
+ "id": "plot-one",
188
+ "metadata": {},
189
+ "outputs": [],
190
+ "source": [
191
+ "display(\n",
192
+ " comparison.sort_values([\"placement\", \"cycle\"])[\n",
193
+ " [\n",
194
+ " \"placement\",\n",
195
+ " \"cycle\",\n",
196
+ " \"Qh\",\n",
197
+ " \"Qc\",\n",
198
+ " \"Qr\",\n",
199
+ " \"hpr_work\",\n",
200
+ " \"hpr_cop\",\n",
201
+ " \"hpr_success\",\n",
202
+ " \"error\",\n",
203
+ " ]\n",
204
+ " ]\n",
205
+ ")\n",
206
+ "\n",
207
+ "for metric in [\"Qh\", \"Qc\", \"Qr\", \"hpr_work\", \"hpr_cop\"]:\n",
208
+ " print(metric)\n",
209
+ " display(comparison.pivot(index=\"placement\", columns=\"cycle\", values=metric))"
210
+ ]
211
+ },
212
+ {
213
+ "cell_type": "markdown",
214
+ "id": "direct-comparison-md",
215
+ "metadata": {},
216
+ "source": [
217
+ "## Inspect one solved HPR-aware plot set\n",
218
+ "Pick one of the four solved cases and use the standard public plot accessors. This cell reads the solved target and graph catalog from the previous optimisation cell."
219
+ ]
220
+ },
221
+ {
222
+ "cell_type": "code",
223
+ "execution_count": null,
224
+ "id": "direct-comparison",
225
+ "metadata": {},
226
+ "outputs": [],
227
+ "source": [
228
+ "SELECTED_CASE = \"CascadeCarnot__direct_heat_pump\"\n",
229
+ "selected_problem = workspace.case(SELECTED_CASE)\n",
230
+ "selected_target = SOLVED_TARGETS[SELECTED_CASE]\n",
231
+ "selected_zone_name = comparison.loc[comparison[\"case\"] == SELECTED_CASE, \"placement\"].iloc[0]\n",
232
+ "\n",
233
+ "print(\n",
234
+ " {\n",
235
+ " \"case\": SELECTED_CASE,\n",
236
+ " \"target\": selected_target.name,\n",
237
+ " \"hpr_cop\": get_scalar_value(selected_target.hpr_cop),\n",
238
+ " }\n",
239
+ ")\n",
240
+ "display(selected_problem.plot.net_load_profiles_with_heat_pump(zone_name=selected_zone_name))\n",
241
+ "display(selected_problem.plot.grand_composite_curve_with_heat_pump(zone_name=selected_zone_name))"
242
+ ]
243
+ },
244
+ {
245
+ "cell_type": "markdown",
246
+ "id": "workflow-md",
247
+ "metadata": {},
248
+ "source": [
249
+ "## Which graph families appear after each solved workflow?\n",
250
+ "Use `plot.catalog()` together with the comparison table to see which graph families become available after each target is solved. This stays on the stable graph catalog instead of depending on lower-level cycle internals."
251
+ ]
252
+ },
253
+ {
254
+ "cell_type": "code",
255
+ "execution_count": null,
256
+ "id": "workflow-table",
257
+ "metadata": {},
258
+ "outputs": [],
259
+ "source": [
260
+ "graph_families = []\n",
261
+ "for spec in case_matrix:\n",
262
+ " problem = workspace.case(spec[\"case\"])\n",
263
+ " comparison_row = comparison.loc[comparison[\"case\"] == spec[\"case\"]].iloc[0]\n",
264
+ " if comparison_row[\"error\"] is not None:\n",
265
+ " graph_families.append(\n",
266
+ " {\n",
267
+ " \"placement\": spec[\"placement\"],\n",
268
+ " \"cycle\": spec[\"cycle\"],\n",
269
+ " \"case\": spec[\"case\"],\n",
270
+ " \"graph families\": None,\n",
271
+ " \"graph count\": comparison_row[\"graph_count\"],\n",
272
+ " \"status\": comparison_row[\"error\"],\n",
273
+ " }\n",
274
+ " )\n",
275
+ " continue\n",
276
+ " catalog = problem.plot.catalog()\n",
277
+ " graph_families.append(\n",
278
+ " {\n",
279
+ " \"placement\": spec[\"placement\"],\n",
280
+ " \"cycle\": spec[\"cycle\"],\n",
281
+ " \"case\": spec[\"case\"],\n",
282
+ " \"graph families\": \", \".join(sorted(catalog[\"Graph Type\"].unique())),\n",
283
+ " \"graph count\": len(catalog),\n",
284
+ " \"status\": \"ok\",\n",
285
+ " }\n",
286
+ " )\n",
287
+ "\n",
288
+ "pd.DataFrame(graph_families)"
289
+ ]
290
+ },
291
+ {
292
+ "cell_type": "markdown",
293
+ "id": "catalog-md",
294
+ "metadata": {},
295
+ "source": [
296
+ "## Optional case-to-case deltas\n",
297
+ "`compare_cases()` is useful when the two cases contain the same target name. The example below compares the two direct heat-pump cycle choices without running another optimisation."
298
+ ]
299
+ },
300
+ {
301
+ "cell_type": "code",
302
+ "execution_count": null,
303
+ "id": "catalog-table",
304
+ "metadata": {},
305
+ "outputs": [],
306
+ "source": [
307
+ "workspace.compare_cases(\n",
308
+ " \"CascadeCarnot__direct_heat_pump\",\n",
309
+ " \"ParallelCarnot__direct_heat_pump\",\n",
310
+ " target_name=WORKFLOWS[\"direct_heat_pump\"][\"target_name\"],\n",
311
+ " base_label=\"CascadeCarnot direct\",\n",
312
+ " other_label=\"ParallelCarnot direct\",\n",
313
+ ")"
314
+ ]
315
+ },
316
+ {
317
+ "cell_type": "markdown",
318
+ "id": "interpretation",
319
+ "metadata": {},
320
+ "source": [
321
+ "## Interpretation\n",
322
+ "A useful HPR option improves the utility picture while keeping the cycle work and COP operationally plausible. In this workflow, `PinchWorkspace` keeps the four named study cases, the comparison frame shows placement and cycle effects side by side, and the standard plot accessors show how each solved HPR target changes the net-load and GCC views."
323
+ ]
324
+ }
325
+ ],
326
+ "metadata": {
327
+ "kernelspec": {
328
+ "display_name": "openpinch",
329
+ "language": "python",
330
+ "name": "python3"
331
+ },
332
+ "language_info": {
333
+ "codemirror_mode": {
334
+ "name": "ipython",
335
+ "version": 3
336
+ },
337
+ "file_extension": ".py",
338
+ "mimetype": "text/x-python",
339
+ "name": "python",
340
+ "nbconvert_exporter": "python",
341
+ "pygments_lexer": "ipython3",
342
+ "version": "3.14.1"
343
+ }
344
+ },
345
+ "nbformat": 4,
346
+ "nbformat_minor": 5
347
+ }
@@ -109,6 +109,29 @@ class Configuration:
109
109
  )
110
110
  return value
111
111
 
112
+ @staticmethod
113
+ def _normalise_config_list(values, *, uppercase: bool = False) -> list[str]:
114
+ """Return stripped, non-empty string values from a config list option."""
115
+ normalised = [str(value).strip() for value in values if str(value).strip()]
116
+ if uppercase:
117
+ return [value.upper() for value in normalised]
118
+ return normalised
119
+
120
+ @property
121
+ def hpr_refrigerants(self) -> list[str]:
122
+ """Return HPR refrigerant names normalised for thermodynamic backends."""
123
+ return self._normalise_config_list(self.REFRIGERANTS, uppercase=True)
124
+
125
+ @property
126
+ def hpr_mvr_fluids(self) -> list[str]:
127
+ """Return MVR fluid names normalised for thermodynamic backends."""
128
+ return self._normalise_config_list(self.MVR_FLUIDS) or ["Water"]
129
+
130
+ @property
131
+ def effective_eta_ii_he_carnot(self) -> float:
132
+ """Return the usable Carnot heat-engine efficiency for HPR targeting."""
133
+ return float(self.ETA_II_HE_CARNOT) if self.ALLOW_INTEGRATED_EXPANDER else 0.0
134
+
112
135
 
113
136
  Configuration.__annotations__ = {
114
137
  name: spec.annotation for name, spec in CONFIG_FIELD_SPECS.items()
@@ -29,27 +29,13 @@ class ConfigurationOptionStatus:
29
29
  runtime_status: str
30
30
 
31
31
 
32
+ # fmt: off
32
33
  CONFIG_FIELD_SPECS: dict[str, ConfigurationFieldSpec] = {
33
34
  "TOP_ZONE_NAME": ConfigurationFieldSpec(str, "Site", "general"),
34
- "TOP_ZONE_IDENTIFIER": ConfigurationFieldSpec(
35
- str,
36
- ZT.S.value,
37
- "general",
38
- enum_cls=ZT,
39
- ),
35
+ "TOP_ZONE_IDENTIFIER": ConfigurationFieldSpec(str, ZT.S.value, "general", enum_cls=ZT),
40
36
  "DT_CONT": ConfigurationFieldSpec(float, 5, "general", numeric_min=0.0),
41
- "DT_PHASE_CHANGE": ConfigurationFieldSpec(
42
- float,
43
- 0.01,
44
- "general",
45
- numeric_min=0.0,
46
- ),
47
- "DT_ASSISTED_HT": ConfigurationFieldSpec(
48
- float,
49
- 10.0,
50
- "general",
51
- numeric_min=0.0,
52
- ),
37
+ "DT_PHASE_CHANGE": ConfigurationFieldSpec(float, 0.01, "general", numeric_min=0.0),
38
+ "DT_ASSISTED_HT": ConfigurationFieldSpec(float, 10.0, "general", numeric_min=0.0),
53
39
  "HTC": ConfigurationFieldSpec(float, 1.0, "general", numeric_min=0.0),
54
40
  "T_ENV": ConfigurationFieldSpec(float, 15, "general"),
55
41
  "DT_ENV_CONT": ConfigurationFieldSpec(float, 10, "general", numeric_min=0.0),
@@ -59,17 +45,9 @@ CONFIG_FIELD_SPECS: dict[str, ConfigurationFieldSpec] = {
59
45
  "STATE_IDS": ConfigurationFieldSpec(List[str], ["0"], "general"),
60
46
  "INPUT_UNITS": ConfigurationFieldSpec(dict[str, str], {}, "general"),
61
47
  "OUTPUT_UNITS": ConfigurationFieldSpec(dict[str, str], {}, "general"),
62
- "DO_DIRECT_OPERATION_TARGETING": ConfigurationFieldSpec(
63
- bool,
64
- False,
65
- "targeting",
66
- ),
48
+ "DO_DIRECT_OPERATION_TARGETING": ConfigurationFieldSpec(bool, False, "targeting"),
67
49
  "DO_DIRECT_SITE_TARGETING": ConfigurationFieldSpec(bool, True, "targeting"),
68
- "DO_INDIRECT_PROCESS_TARGETING": ConfigurationFieldSpec(
69
- bool,
70
- False,
71
- "targeting",
72
- ),
50
+ "DO_INDIRECT_PROCESS_TARGETING": ConfigurationFieldSpec(bool, False, "targeting"),
73
51
  "DO_BALANCED_CC": ConfigurationFieldSpec(bool, True, "targeting"),
74
52
  "DO_AREA_TARGETING": ConfigurationFieldSpec(bool, False, "targeting"),
75
53
  "DO_PROCESS_HP_TARGETING": ConfigurationFieldSpec(bool, False, "targeting"),
@@ -77,44 +55,19 @@ CONFIG_FIELD_SPECS: dict[str, ConfigurationFieldSpec] = {
77
55
  "DO_UTILITY_HP_TARGETING": ConfigurationFieldSpec(bool, False, "targeting"),
78
56
  "DO_UTILITY_RFRG_TARGETING": ConfigurationFieldSpec(bool, False, "targeting"),
79
57
  "DO_TURBINE_TARGETING": ConfigurationFieldSpec(bool, False, "targeting"),
80
- "DO_EXERGY_TARGETING": ConfigurationFieldSpec(
81
- bool,
82
- False,
83
- "targeting",
84
- runtime_status="experimental",
85
- ),
58
+ "DO_EXERGY_TARGETING": ConfigurationFieldSpec(bool, False, "targeting", runtime_status="experimental"),
86
59
  "DO_VERTICAL_GCC": ConfigurationFieldSpec(bool, False, "targeting"),
87
60
  "DO_ASSITED_HT": ConfigurationFieldSpec(bool, False, "targeting"),
88
61
  "DO_TURBINE_WORK": ConfigurationFieldSpec(bool, False, "targeting"),
89
- "HPR_TYPE": ConfigurationFieldSpec(
90
- str,
91
- HPRcycle.CascadeCarnot.value,
92
- "hpr",
93
- enum_cls=HPRcycle,
94
- ),
62
+ "HPR_TYPE": ConfigurationFieldSpec(str, HPRcycle.CascadeCarnot.value, "hpr", enum_cls=HPRcycle),
95
63
  "HPR_LOAD_VALUE": ConfigurationFieldSpec(float | str | dict, 1.0, "hpr"),
96
64
  "HPR_LOAD_VALUE_TYPE": ConfigurationFieldSpec(str, "fraction", "hpr"),
97
65
  "REFRIGERANTS": ConfigurationFieldSpec(List[str], ["water", "ammonia"], "hpr"),
98
66
  "MVR_FLUIDS": ConfigurationFieldSpec(List[str], ["Water"], "hpr"),
99
67
  "DO_REFRIGERANT_SORT": ConfigurationFieldSpec(bool, True, "hpr"),
100
- "PRICE_RATIO_HEAT_TO_ELE": ConfigurationFieldSpec(
101
- float,
102
- 1.0,
103
- "hpr",
104
- numeric_min=0.0,
105
- ),
106
- "PRICE_RATIO_COLD_TO_ELE": ConfigurationFieldSpec(
107
- float,
108
- 1.0,
109
- "hpr",
110
- numeric_min=0.0,
111
- ),
112
- "MAX_HP_MULTISTART": ConfigurationFieldSpec(
113
- int,
114
- 10,
115
- "hpr",
116
- numeric_min=0.0,
117
- ),
68
+ "PRICE_RATIO_HEAT_TO_ELE": ConfigurationFieldSpec(float, 1.0, "hpr", numeric_min=0.0),
69
+ "PRICE_RATIO_COLD_TO_ELE": ConfigurationFieldSpec(float, 1.0, "hpr", numeric_min=0.0),
70
+ "MAX_HP_MULTISTART": ConfigurationFieldSpec(int, 10, "hpr", numeric_min=0.0),
118
71
  "N_COND": ConfigurationFieldSpec(int, 3, "hpr", numeric_min=0.0),
119
72
  "N_EVAP": ConfigurationFieldSpec(int, 2, "hpr", numeric_min=0.0),
120
73
  "N_MVR": ConfigurationFieldSpec(int, 1, "hpr", numeric_min=1.0),
@@ -122,80 +75,39 @@ CONFIG_FIELD_SPECS: dict[str, ConfigurationFieldSpec] = {
122
75
  "ETA_MVR_COMP": ConfigurationFieldSpec(float, 0.7, "hpr", numeric_min=0.0),
123
76
  "ETA_EXP": ConfigurationFieldSpec(float, 0.7, "hpr", numeric_min=0.0),
124
77
  "ETA_MOTOR": ConfigurationFieldSpec(float, 0.95, "hpr", numeric_min=0.0),
125
- "ETA_II_HPR_CARNOT": ConfigurationFieldSpec(
126
- float,
127
- 0.5,
128
- "hpr",
129
- numeric_min=0.0,
130
- ),
131
- "ETA_II_HE_CARNOT": ConfigurationFieldSpec(
132
- float,
133
- 0.5,
134
- "hpr",
135
- numeric_min=0.0,
136
- ),
78
+ "ETA_II_HPR_CARNOT": ConfigurationFieldSpec(float, 0.5, "hpr", numeric_min=0.0),
79
+ "ETA_II_HE_CARNOT": ConfigurationFieldSpec(float, 0.5, "hpr", numeric_min=0.0),
137
80
  "DT_CONT_HP": ConfigurationFieldSpec(float, 0.0, "hpr", numeric_min=0.0),
138
81
  "DT_HPR_IHX": ConfigurationFieldSpec(float, 0.0, "hpr", numeric_min=0.0),
139
- "DT_HPR_CASCADE_HX": ConfigurationFieldSpec(
140
- float,
141
- 0.0,
142
- "hpr",
143
- numeric_min=0.0,
144
- ),
145
- "BB_MINIMISER": ConfigurationFieldSpec(
146
- str,
147
- BB_Minimiser.CMAES.value,
148
- "hpr",
149
- enum_cls=BB_Minimiser,
150
- ),
82
+ "DT_HPR_CASCADE_HX": ConfigurationFieldSpec(float, 0.0, "hpr", numeric_min=0.0),
83
+ "ETA_PENALTY": ConfigurationFieldSpec(float, 0.001, "hpr", numeric_min=0.0),
84
+ "RHO_PENALTY": ConfigurationFieldSpec(float, 10.0, "hpr", numeric_min=0.0),
85
+ "BB_MINIMISER": ConfigurationFieldSpec(str, BB_Minimiser.CMAES.value, "hpr", enum_cls=BB_Minimiser),
151
86
  "INITIALISE_SIMULATED_CYCLE": ConfigurationFieldSpec(bool, True, "hpr"),
152
87
  "ALLOW_INTEGRATED_EXPANDER": ConfigurationFieldSpec(bool, False, "hpr"),
88
+ "HPR_COMP_FIXED_COST": ConfigurationFieldSpec(float, 0, "hpr", numeric_min=0.0),
89
+ "HPR_COMP_VARIABLE_COST": ConfigurationFieldSpec(float, 10000, "hpr", numeric_min=0.0),
90
+ "HPR_COMP_COST_EXP": ConfigurationFieldSpec(float, 1.0, "hpr", numeric_min=0.0),
91
+ "HPR_HX_FIXED_COST": ConfigurationFieldSpec(float, 0, "hpr", numeric_min=0.0),
92
+ "HPR_HX_VARIABLE_COST": ConfigurationFieldSpec(float, 10000, "hpr", numeric_min=0.0),
93
+ "HPR_HX_COST_EXP": ConfigurationFieldSpec(float, 1.0, "hpr", numeric_min=0.0),
153
94
  "ELE_PRICE": ConfigurationFieldSpec(float, 200, "costing", numeric_min=0.0),
154
- "UTILITY_PRICE": ConfigurationFieldSpec(
155
- float,
156
- 100,
157
- "costing",
158
- numeric_min=0.0,
159
- ),
160
- "ANNUAL_OP_TIME": ConfigurationFieldSpec(
161
- float,
162
- 8300,
163
- "costing",
164
- numeric_min=0.0,
165
- ),
95
+ "UTILITY_PRICE": ConfigurationFieldSpec(float, 100, "costing", numeric_min=0.0),
96
+ "ANNUAL_OP_TIME": ConfigurationFieldSpec(float, 8300, "costing", numeric_min=0.0),
166
97
  "FIXED_COST": ConfigurationFieldSpec(float, 0, "costing", numeric_min=0.0),
167
- "VARIABLE_COST": ConfigurationFieldSpec(
168
- float,
169
- 10000,
170
- "costing",
171
- numeric_min=0.0,
172
- ),
98
+ "VARIABLE_COST": ConfigurationFieldSpec(float, 10000, "costing", numeric_min=0.0),
173
99
  "COST_EXP": ConfigurationFieldSpec(float, 0.6, "costing", numeric_min=0.0),
174
- "DISCOUNT_RATE": ConfigurationFieldSpec(
175
- float,
176
- 0.07,
177
- "costing",
178
- numeric_min=0.0,
179
- ),
100
+ "DISCOUNT_RATE": ConfigurationFieldSpec(float, 0.07, "costing", numeric_min=0.0),
180
101
  "SERV_LIFE": ConfigurationFieldSpec(float, 20, "costing", numeric_min=0.0),
181
102
  "TURB_T_IN": ConfigurationFieldSpec(float, 450, "turbine"),
182
103
  "TURB_P_IN": ConfigurationFieldSpec(float, 90, "turbine", numeric_min=0.0),
183
104
  "MIN_EFF": ConfigurationFieldSpec(float, 0.1, "turbine", numeric_min=0.0),
184
- "LOAD_FRACTION": ConfigurationFieldSpec(
185
- float,
186
- 1,
187
- "turbine",
188
- numeric_min=0.0,
189
- ),
105
+ "LOAD_FRACTION": ConfigurationFieldSpec(float, 1, "turbine", numeric_min=0.0),
190
106
  "ETA_MECH": ConfigurationFieldSpec(float, 1, "turbine", numeric_min=0.0),
191
- "TURB_MODEL": ConfigurationFieldSpec(
192
- str,
193
- TurbineModel.MEDINA_FLORES.value,
194
- "turbine",
195
- enum_cls=TurbineModel,
196
- ),
107
+ "TURB_MODEL": ConfigurationFieldSpec(str, TurbineModel.MEDINA_FLORES.value, "turbine", enum_cls=TurbineModel),
197
108
  "IS_HIGH_P_COND_FLASH": ConfigurationFieldSpec(bool, False, "turbine"),
198
109
  }
110
+ # fmt: on
199
111
 
200
112
  CONFIG_ENUMS: dict[str, type[Enum]] = {
201
113
  name: spec.enum_cls