OpenPinch 0.7.0__tar.gz → 0.7.2__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 (429) hide show
  1. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/adapters/io/csv.py +13 -14
  2. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/adapters/io/records.py +41 -4
  3. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/adapters/io/tabular.py +127 -1
  4. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/adapters/io/workbook.py +14 -18
  5. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/analysis/energy_transfer/diagram.py +9 -0
  6. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/analysis/exergy/service.py +126 -5
  7. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/analysis/graphs/composite.py +15 -7
  8. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/analysis/heat_exchanger_networks/execution/executor.py +27 -18
  9. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/analysis/heat_exchanger_networks/extraction/metadata.py +2 -2
  10. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/analysis/heat_exchanger_networks/extraction/period_state.py +87 -58
  11. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/analysis/heat_exchanger_networks/extraction/segment_area.py +2 -1
  12. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/analysis/heat_exchanger_networks/extraction/service.py +31 -19
  13. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/analysis/heat_exchanger_networks/extraction/utility.py +18 -4
  14. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/analysis/heat_exchanger_networks/models/_base/alpha.py +1 -1
  15. openpinch-0.7.2/OpenPinch/analysis/heat_exchanger_networks/models/_base/approach.py +499 -0
  16. openpinch-0.7.2/OpenPinch/analysis/heat_exchanger_networks/models/_base/area.py +469 -0
  17. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/analysis/heat_exchanger_networks/models/_base/costing.py +32 -16
  18. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/analysis/heat_exchanger_networks/models/_base/parameters.py +2 -0
  19. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/analysis/heat_exchanger_networks/models/_base/piecewise.py +146 -118
  20. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/analysis/heat_exchanger_networks/models/_base/superstructure.py +46 -43
  21. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/analysis/heat_exchanger_networks/models/_pinch_design/amalgamation.py +42 -22
  22. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/analysis/heat_exchanger_networks/models/_pinch_design/equations.py +64 -11
  23. openpinch-0.7.2/OpenPinch/analysis/heat_exchanger_networks/models/_pinch_design/postprocess.py +115 -0
  24. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/analysis/heat_exchanger_networks/models/_pinch_design/preprocessing.py +18 -8
  25. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/analysis/heat_exchanger_networks/models/_stagewise/equations.py +10 -8
  26. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/analysis/heat_exchanger_networks/models/_stagewise/evolution.py +7 -0
  27. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/analysis/heat_exchanger_networks/models/_stagewise/objectives.py +146 -132
  28. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/analysis/heat_exchanger_networks/models/_stagewise/postprocess.py +8 -130
  29. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/analysis/heat_exchanger_networks/models/_stagewise/setup.py +35 -22
  30. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/analysis/heat_exchanger_networks/models/_stagewise/verification.py +67 -84
  31. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/analysis/heat_exchanger_networks/models/_stagewise/warm_start.py +65 -87
  32. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/analysis/heat_exchanger_networks/models/base.py +20 -3
  33. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/analysis/heat_exchanger_networks/models/problem.py +58 -13
  34. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/analysis/heat_exchanger_networks/reporting/ranking.py +1 -9
  35. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/analysis/heat_exchanger_networks/reporting/verification.py +55 -34
  36. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/analysis/heat_exchanger_networks/results/assembly.py +17 -11
  37. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/analysis/heat_exchanger_networks/solver/arrays.py +4 -5
  38. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/analysis/heat_exchanger_networks/solver/backend.py +11 -8
  39. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/analysis/heat_exchanger_networks/solver/piecewise.py +13 -5
  40. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/analysis/heat_exchanger_networks/targeting/pinch_design_method.py +19 -7
  41. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/analysis/heat_pumps/_multiperiod/aggregation.py +1 -0
  42. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/analysis/heat_pumps/_multiperiod/execution.py +7 -1
  43. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/analysis/heat_pumps/_multiperiod/preparation.py +63 -30
  44. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/analysis/heat_pumps/common/shared.py +23 -1
  45. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/analysis/heat_pumps/cycles/carnot_cycles.py +53 -19
  46. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/analysis/heat_pumps/cycles/cascade_vapour_compression_cycle.py +2 -2
  47. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/analysis/heat_pumps/cycles/vapour_compression_cycle.py +15 -6
  48. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/analysis/heat_pumps/optimisation_adapter.py +35 -5
  49. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/analysis/heat_pumps/service.py +7 -0
  50. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/analysis/heat_pumps/targeting/_carnot_common.py +10 -1
  51. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/analysis/targeting/area_cost.py +23 -9
  52. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/analysis/targeting/direct.py +31 -16
  53. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/analysis/targeting/grand_composite.py +22 -8
  54. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/analysis/targeting/indirect.py +112 -28
  55. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/analysis/targeting/temperature_driving_force.py +23 -81
  56. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/analysis/targeting/utilities.py +12 -0
  57. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/analysis/utility_placement/bounds.py +19 -0
  58. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/analysis/utility_placement/thermodynamics.py +15 -17
  59. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/application/_problem/accessors/design.py +11 -4
  60. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/application/_problem/input/construction.py +23 -7
  61. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/application/_problem/input/semantics.py +165 -24
  62. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/application/_problem/output/reporting.py +22 -3
  63. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/application/_problem/periods/aggregation.py +45 -10
  64. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/application/_problem/targeting/state.py +9 -3
  65. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/application/commands.py +132 -24
  66. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/application/problem.py +22 -1
  67. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/application/utility_placement.py +110 -21
  68. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/contracts/hpr.py +3 -0
  69. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/contracts/input.py +27 -4
  70. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/contracts/reporting.py +6 -1
  71. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/contracts/synthesis/view.py +16 -0
  72. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/contracts/units.py +48 -1
  73. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/contracts/utility_placement.py +15 -3
  74. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/contracts/workspace.py +1 -0
  75. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/domain/_problem_table/intervals.py +47 -108
  76. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/domain/_stream/linearisation.py +10 -4
  77. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/domain/_stream/segment.py +1 -1
  78. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/domain/_stream/value_state.py +7 -0
  79. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/domain/_value/units.py +68 -0
  80. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/domain/configuration.py +33 -0
  81. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/domain/configuration_fields.py +5 -0
  82. openpinch-0.7.2/OpenPinch/domain/solver_options.py +134 -0
  83. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/domain/stream.py +1 -1
  84. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/domain/targets.py +1 -0
  85. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/domain/value.py +21 -4
  86. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/domain/zone.py +21 -25
  87. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/optimisation/backends/_multistart.py +3 -0
  88. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/optimisation/backends/bayesian.py +3 -0
  89. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/optimisation/backends/cma_es.py +3 -0
  90. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/optimisation/backends/dual_annealing.py +3 -0
  91. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/optimisation/backends/rbf.py +3 -0
  92. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/optimisation/candidates.py +29 -9
  93. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/optimisation/execution.py +61 -1
  94. openpinch-0.7.2/OpenPinch/optimisation/feasibility.py +106 -0
  95. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/optimisation/models.py +15 -0
  96. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/optimisation/service.py +41 -61
  97. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/presentation/configuration.py +16 -15
  98. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/presentation/configuration_text.py +21 -8
  99. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/presentation/dashboard/exports.py +2 -0
  100. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/presentation/network_grid/geometry.py +6 -0
  101. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/presentation/network_grid/layout.py +45 -9
  102. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/presentation/network_grid/state.py +3 -0
  103. openpinch-0.7.2/OpenPinch/presentation/reporting/excel_text.py +29 -0
  104. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/presentation/reporting/problem_table.py +2 -0
  105. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/presentation/reporting/thermal.py +1 -0
  106. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/presentation/reporting/workbook.py +3 -1
  107. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/tutorials/notebooks/17_multiperiod_hen_synthesis.ipynb +3 -1
  108. {openpinch-0.7.0 → openpinch-0.7.2}/PKG-INFO +12 -1
  109. {openpinch-0.7.0 → openpinch-0.7.2}/README.md +11 -0
  110. {openpinch-0.7.0 → openpinch-0.7.2}/pyproject.toml +1 -1
  111. openpinch-0.7.0/OpenPinch/analysis/heat_exchanger_networks/models/_base/approach.py +0 -382
  112. openpinch-0.7.0/OpenPinch/analysis/heat_exchanger_networks/models/_base/area.py +0 -316
  113. openpinch-0.7.0/OpenPinch/analysis/heat_exchanger_networks/models/_pinch_design/postprocess.py +0 -202
  114. {openpinch-0.7.0 → openpinch-0.7.2}/.gitignore +0 -0
  115. {openpinch-0.7.0 → openpinch-0.7.2}/LICENSE +0 -0
  116. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/__init__.py +0 -0
  117. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/__main__.py +0 -0
  118. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/adapters/__init__.py +0 -0
  119. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/adapters/io/__init__.py +0 -0
  120. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/adapters/io/file_exports.py +0 -0
  121. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/adapters/io/json.py +0 -0
  122. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/adapters/io/problem_sources.py +0 -0
  123. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/adapters/io/workspace_bundles.py +0 -0
  124. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/adapters/optional_dependencies.py +0 -0
  125. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/analysis/__init__.py +0 -0
  126. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/analysis/economics.py +0 -0
  127. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/analysis/energy_transfer/__init__.py +0 -0
  128. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/analysis/energy_transfer/cascade.py +0 -0
  129. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/analysis/energy_transfer/selection.py +0 -0
  130. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/analysis/energy_transfer/serialization.py +0 -0
  131. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/analysis/energy_transfer/service.py +0 -0
  132. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/analysis/exergy/__init__.py +0 -0
  133. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/analysis/graphs/__init__.py +0 -0
  134. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/analysis/graphs/grand_composite.py +0 -0
  135. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/analysis/graphs/metadata.py +0 -0
  136. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/analysis/graphs/primitives.py +0 -0
  137. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/analysis/graphs/service.py +0 -0
  138. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/analysis/graphs/specifications.py +0 -0
  139. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/analysis/heat_exchanger_networks/__init__.py +0 -0
  140. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/analysis/heat_exchanger_networks/context.py +0 -0
  141. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/analysis/heat_exchanger_networks/controllability.py +0 -0
  142. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/analysis/heat_exchanger_networks/duty_optimisation.py +0 -0
  143. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/analysis/heat_exchanger_networks/errors.py +0 -0
  144. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/analysis/heat_exchanger_networks/execution/__init__.py +0 -0
  145. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/analysis/heat_exchanger_networks/execution/fake_executor.py +0 -0
  146. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/analysis/heat_exchanger_networks/execution/fallbacks.py +0 -0
  147. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/analysis/heat_exchanger_networks/execution/pathways.py +0 -0
  148. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/analysis/heat_exchanger_networks/execution/settings.py +0 -0
  149. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/analysis/heat_exchanger_networks/execution/task_builders.py +0 -0
  150. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/analysis/heat_exchanger_networks/extraction/__init__.py +0 -0
  151. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/analysis/heat_exchanger_networks/extraction/recovery.py +0 -0
  152. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/analysis/heat_exchanger_networks/indexing.py +0 -0
  153. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/analysis/heat_exchanger_networks/models/__init__.py +0 -0
  154. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/analysis/heat_exchanger_networks/models/_base/__init__.py +0 -0
  155. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/analysis/heat_exchanger_networks/models/_base/execution.py +0 -0
  156. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/analysis/heat_exchanger_networks/models/_pinch_design/__init__.py +0 -0
  157. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/analysis/heat_exchanger_networks/models/_stagewise/__init__.py +0 -0
  158. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/analysis/heat_exchanger_networks/models/fixed_structure.py +0 -0
  159. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/analysis/heat_exchanger_networks/models/packed_pinch_design.py +0 -0
  160. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/analysis/heat_exchanger_networks/models/packed_stagewise.py +0 -0
  161. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/analysis/heat_exchanger_networks/models/pinch_decomposition.py +0 -0
  162. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/analysis/heat_exchanger_networks/models/stage_packing.py +0 -0
  163. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/analysis/heat_exchanger_networks/models/stagewise.py +0 -0
  164. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/analysis/heat_exchanger_networks/models/thermal_profiles.py +0 -0
  165. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/analysis/heat_exchanger_networks/prepared_problem.py +0 -0
  166. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/analysis/heat_exchanger_networks/reporting/__init__.py +0 -0
  167. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/analysis/heat_exchanger_networks/reporting/exports.py +0 -0
  168. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/analysis/heat_exchanger_networks/results/__init__.py +0 -0
  169. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/analysis/heat_exchanger_networks/results/seeds.py +0 -0
  170. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/analysis/heat_exchanger_networks/results/selection.py +0 -0
  171. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/analysis/heat_exchanger_networks/service.py +0 -0
  172. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/analysis/heat_exchanger_networks/solver/__init__.py +0 -0
  173. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/analysis/heat_exchanger_networks/solver/dependencies.py +0 -0
  174. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/analysis/heat_exchanger_networks/solver/pinch_design_decomposition.py +0 -0
  175. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/analysis/heat_exchanger_networks/targeting/__init__.py +0 -0
  176. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/analysis/heat_exchanger_networks/targeting/_stage.py +0 -0
  177. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/analysis/heat_exchanger_networks/targeting/network_evolution_method.py +0 -0
  178. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/analysis/heat_exchanger_networks/targeting/open_hens_method.py +0 -0
  179. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/analysis/heat_exchanger_networks/targeting/thermal_derivative_method.py +0 -0
  180. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/analysis/heat_exchanger_networks/targeting/topology.py +0 -0
  181. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/analysis/heat_pumps/__init__.py +0 -0
  182. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/analysis/heat_pumps/_multiperiod/__init__.py +0 -0
  183. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/analysis/heat_pumps/_multiperiod/setup.py +0 -0
  184. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/analysis/heat_pumps/_multiperiod/state.py +0 -0
  185. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/analysis/heat_pumps/_process_mvr/__init__.py +0 -0
  186. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/analysis/heat_pumps/_process_mvr/membership.py +0 -0
  187. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/analysis/heat_pumps/_process_mvr/replacement_streams.py +0 -0
  188. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/analysis/heat_pumps/_process_mvr/selection.py +0 -0
  189. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/analysis/heat_pumps/_process_mvr/state.py +0 -0
  190. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/analysis/heat_pumps/_process_mvr/values.py +0 -0
  191. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/analysis/heat_pumps/_process_mvr/work.py +0 -0
  192. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/analysis/heat_pumps/common/__init__.py +0 -0
  193. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/analysis/heat_pumps/common/_shared/__init__.py +0 -0
  194. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/analysis/heat_pumps/common/_shared/air.py +0 -0
  195. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/analysis/heat_pumps/common/_shared/plotting.py +0 -0
  196. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/analysis/heat_pumps/common/_shared/streams.py +0 -0
  197. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/analysis/heat_pumps/common/encoding.py +0 -0
  198. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/analysis/heat_pumps/common/layout.py +0 -0
  199. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/analysis/heat_pumps/common/load_selection.py +0 -0
  200. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/analysis/heat_pumps/common/multi_vc_objective.py +0 -0
  201. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/analysis/heat_pumps/common/postprocessing.py +0 -0
  202. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/analysis/heat_pumps/common/preprocessing.py +0 -0
  203. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/analysis/heat_pumps/common/units.py +0 -0
  204. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/analysis/heat_pumps/components.py +0 -0
  205. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/analysis/heat_pumps/cycles/__init__.py +0 -0
  206. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/analysis/heat_pumps/cycles/_multi_cycle_base.py +0 -0
  207. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/analysis/heat_pumps/cycles/brayton_heat_pump.py +0 -0
  208. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/analysis/heat_pumps/cycles/mechanical_vapour_recompression_cycle.py +0 -0
  209. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/analysis/heat_pumps/cycles/parallel_vapour_compression_cycles.py +0 -0
  210. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/analysis/heat_pumps/cycles/vapour_compression_mvr_cascade.py +0 -0
  211. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/analysis/heat_pumps/direct_mvr/__init__.py +0 -0
  212. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/analysis/heat_pumps/direct_mvr/execution.py +0 -0
  213. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/analysis/heat_pumps/direct_mvr/models.py +0 -0
  214. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/analysis/heat_pumps/direct_mvr/thermodynamics.py +0 -0
  215. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/analysis/heat_pumps/direct_mvr/units.py +0 -0
  216. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/analysis/heat_pumps/performance_maps/__init__.py +0 -0
  217. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/analysis/heat_pumps/performance_maps/adapters/__init__.py +0 -0
  218. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/analysis/heat_pumps/performance_maps/adapters/coolprop.py +0 -0
  219. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/analysis/heat_pumps/performance_maps/adapters/tespy.py +0 -0
  220. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/analysis/heat_pumps/performance_maps/characteristics/__init__.py +0 -0
  221. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/analysis/heat_pumps/performance_maps/characteristics/openpinch-single-stage-compressor-v1.json +0 -0
  222. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/analysis/heat_pumps/performance_maps/context.py +0 -0
  223. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/analysis/heat_pumps/performance_maps/coolprop_preflight.py +0 -0
  224. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/analysis/heat_pumps/performance_maps/errors.py +0 -0
  225. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/analysis/heat_pumps/performance_maps/factory.py +0 -0
  226. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/analysis/heat_pumps/performance_maps/fluids.py +0 -0
  227. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/analysis/heat_pumps/performance_maps/generation.py +0 -0
  228. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/analysis/heat_pumps/performance_maps/models.py +0 -0
  229. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/analysis/heat_pumps/performance_maps/points.py +0 -0
  230. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/analysis/heat_pumps/performance_maps/protocols.py +0 -0
  231. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/analysis/heat_pumps/performance_maps/provenance.py +0 -0
  232. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/analysis/heat_pumps/performance_maps/resources.py +0 -0
  233. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/analysis/heat_pumps/performance_maps/settings.py +0 -0
  234. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/analysis/heat_pumps/performance_maps/target_basis.py +0 -0
  235. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/analysis/heat_pumps/performance_maps/target_records.py +0 -0
  236. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/analysis/heat_pumps/performance_maps/targeting.py +0 -0
  237. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/analysis/heat_pumps/performance_maps/targeting_models.py +0 -0
  238. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/analysis/heat_pumps/process_mvr.py +0 -0
  239. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/analysis/heat_pumps/targeting/__init__.py +0 -0
  240. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/analysis/heat_pumps/targeting/brayton.py +0 -0
  241. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/analysis/heat_pumps/targeting/cascade_carnot.py +0 -0
  242. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/analysis/heat_pumps/targeting/cascade_vapour_compression.py +0 -0
  243. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/analysis/heat_pumps/targeting/parallel_carnot.py +0 -0
  244. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/analysis/heat_pumps/targeting/parallel_vapour_compression.py +0 -0
  245. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/analysis/heat_pumps/targeting/vapour_compression_mvr.py +0 -0
  246. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/analysis/heat_transfer.py +0 -0
  247. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/analysis/numerics.py +0 -0
  248. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/analysis/orc/__init__.py +0 -0
  249. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/analysis/orc/carnot.py +0 -0
  250. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/analysis/orc/costing.py +0 -0
  251. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/analysis/orc/cycle.py +0 -0
  252. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/analysis/orc/inputs.py +0 -0
  253. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/analysis/orc/profile.py +0 -0
  254. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/analysis/orc/search.py +0 -0
  255. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/analysis/orc/service.py +0 -0
  256. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/analysis/orc/simulated.py +0 -0
  257. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/analysis/parallelism.py +0 -0
  258. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/analysis/power/__init__.py +0 -0
  259. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/analysis/power/service.py +0 -0
  260. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/analysis/power/steam_turbine.py +0 -0
  261. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/analysis/targeting/__init__.py +0 -0
  262. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/analysis/targeting/cascade.py +0 -0
  263. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/analysis/targeting/context.py +0 -0
  264. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/analysis/targeting/heat_recovery_dt_min.py +0 -0
  265. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/analysis/targeting/residual.py +0 -0
  266. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/analysis/thermodynamics/__init__.py +0 -0
  267. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/analysis/thermodynamics/water.py +0 -0
  268. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/analysis/utility_placement/__init__.py +0 -0
  269. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/analysis/utility_placement/allocation.py +0 -0
  270. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/analysis/utility_placement/codec.py +0 -0
  271. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/analysis/utility_placement/context.py +0 -0
  272. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/analysis/utility_placement/errors.py +0 -0
  273. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/analysis/utility_placement/evaluation.py +0 -0
  274. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/analysis/utility_placement/normalization.py +0 -0
  275. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/analysis/utility_placement/optimisation.py +0 -0
  276. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/analysis/utility_placement/penalties.py +0 -0
  277. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/analysis/utility_placement/profiles.py +0 -0
  278. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/analysis/utility_placement/replay.py +0 -0
  279. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/analysis/utility_placement/service.py +0 -0
  280. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/analysis/utility_placement/start_points.py +0 -0
  281. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/analysis/utility_placement/units.py +0 -0
  282. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/application/__init__.py +0 -0
  283. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/application/_problem/__init__.py +0 -0
  284. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/application/_problem/accessors/__init__.py +0 -0
  285. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/application/_problem/accessors/component.py +0 -0
  286. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/application/_problem/accessors/plot.py +0 -0
  287. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/application/_problem/accessors/target.py +0 -0
  288. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/application/_problem/arguments.py +0 -0
  289. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/application/_problem/input/__init__.py +0 -0
  290. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/application/_problem/input/canonicalization.py +0 -0
  291. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/application/_problem/input/loading.py +0 -0
  292. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/application/_problem/input/segments.py +0 -0
  293. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/application/_problem/input/utilities.py +0 -0
  294. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/application/_problem/input/validation.py +0 -0
  295. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/application/_problem/output/__init__.py +0 -0
  296. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/application/_problem/output/result_extraction.py +0 -0
  297. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/application/_problem/periods/__init__.py +0 -0
  298. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/application/_problem/targeting/__init__.py +0 -0
  299. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/application/_problem/targeting/catalog.py +0 -0
  300. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/application/_problem/targeting/dispatch.py +0 -0
  301. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/application/_problem/targeting/execution.py +0 -0
  302. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/application/_problem/targeting/provenance.py +0 -0
  303. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/application/_workspace/__init__.py +0 -0
  304. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/application/_workspace/case_inputs.py +0 -0
  305. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/application/_workspace/state.py +0 -0
  306. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/application/case_derivation.py +0 -0
  307. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/application/heat_recovery_dt_min.py +0 -0
  308. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/application/hpr_selection.py +0 -0
  309. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/application/residual_utility.py +0 -0
  310. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/application/targeting.py +0 -0
  311. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/application/workspace.py +0 -0
  312. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/contracts/__init__.py +0 -0
  313. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/contracts/common.py +0 -0
  314. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/contracts/controllability.py +0 -0
  315. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/contracts/graphs.py +0 -0
  316. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/contracts/heat_recovery_dt_min.py +0 -0
  317. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/contracts/hpr_performance_map.py +0 -0
  318. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/contracts/output.py +0 -0
  319. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/contracts/report_metrics.py +0 -0
  320. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/contracts/report_units.py +0 -0
  321. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/contracts/resources/__init__.py +0 -0
  322. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/contracts/resources/hpr_performance_map/__init__.py +0 -0
  323. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/contracts/resources/hpr_performance_map/heat-pump-1.0.json +0 -0
  324. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/contracts/resources/hpr_performance_map/refrigeration-1.0.json +0 -0
  325. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/contracts/resources/hpr_performance_map/schema-1.0.json +0 -0
  326. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/contracts/schema.py +0 -0
  327. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/contracts/synthesis/__init__.py +0 -0
  328. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/contracts/synthesis/common.py +0 -0
  329. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/contracts/synthesis/method.py +0 -0
  330. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/contracts/synthesis/result.py +0 -0
  331. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/contracts/synthesis/task.py +0 -0
  332. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/contracts/synthesis/topology.py +0 -0
  333. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/contracts/turbine.py +0 -0
  334. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/domain/__init__.py +0 -0
  335. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/domain/_heat_exchanger/__init__.py +0 -0
  336. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/domain/_heat_exchanger/area.py +0 -0
  337. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/domain/_heat_exchanger/period_state.py +0 -0
  338. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/domain/_heat_exchanger/rules.py +0 -0
  339. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/domain/_heat_exchanger_network/__init__.py +0 -0
  340. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/domain/_heat_exchanger_network/types.py +0 -0
  341. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/domain/_problem_table/__init__.py +0 -0
  342. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/domain/_problem_table/constants.py +0 -0
  343. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/domain/_problem_table/equality.py +0 -0
  344. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/domain/_problem_table/types.py +0 -0
  345. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/domain/_stream/__init__.py +0 -0
  346. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/domain/_stream/profile.py +0 -0
  347. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/domain/_stream/segments.py +0 -0
  348. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/domain/_stream/thermodynamics.py +0 -0
  349. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/domain/_stream_collection/__init__.py +0 -0
  350. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/domain/_stream_collection/filters.py +0 -0
  351. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/domain/_stream_collection/numeric_view.py +0 -0
  352. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/domain/_stream_collection/serialization.py +0 -0
  353. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/domain/_stream_collection/sorting.py +0 -0
  354. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/domain/_validation.py +0 -0
  355. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/domain/_value/__init__.py +0 -0
  356. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/domain/_value/coercion.py +0 -0
  357. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/domain/_value/resolution.py +0 -0
  358. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/domain/analysis.py +0 -0
  359. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/domain/enums.py +0 -0
  360. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/domain/fluids.py +0 -0
  361. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/domain/heat_exchanger.py +0 -0
  362. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/domain/heat_exchanger_network.py +0 -0
  363. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/domain/hpr.py +0 -0
  364. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/domain/problem_table.py +0 -0
  365. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/domain/progress.py +0 -0
  366. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/domain/stream_collection.py +0 -0
  367. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/optimisation/__init__.py +0 -0
  368. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/optimisation/backends/__init__.py +0 -0
  369. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/optimisation/backends/_surrogate.py +0 -0
  370. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/optimisation/backends/protocol.py +0 -0
  371. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/optimisation/errors.py +0 -0
  372. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/presentation/__init__.py +0 -0
  373. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/presentation/dashboard/__init__.py +0 -0
  374. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/presentation/dashboard/dashboard.css +0 -0
  375. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/presentation/dashboard/dependencies.py +0 -0
  376. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/presentation/dashboard/rendering.py +0 -0
  377. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/presentation/dashboard/state.py +0 -0
  378. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/presentation/graphs/__init__.py +0 -0
  379. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/presentation/graphs/plotly.py +0 -0
  380. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/presentation/graphs/problem.py +0 -0
  381. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/presentation/graphs/simple.py +0 -0
  382. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/presentation/network_grid/__init__.py +0 -0
  383. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/presentation/network_grid/_constants.py +0 -0
  384. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/presentation/network_grid/dependencies.py +0 -0
  385. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/presentation/network_grid/labels.py +0 -0
  386. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/presentation/network_grid/plotly.py +0 -0
  387. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/presentation/network_grid/rendering.py +0 -0
  388. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/presentation/network_grid/results.py +0 -0
  389. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/presentation/network_grid/service.py +0 -0
  390. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/presentation/network_grid/temperatures.py +0 -0
  391. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/presentation/network_grid/view.py +0 -0
  392. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/presentation/reporting/__init__.py +0 -0
  393. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/presentation/reporting/adapters.py +0 -0
  394. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/presentation/reporting/hpr.py +0 -0
  395. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/presentation/reporting/results.py +0 -0
  396. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/presentation/reporting/specialist.py +0 -0
  397. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/presentation/reporting/stream_collection.py +0 -0
  398. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/resources.py +0 -0
  399. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/tutorials/__init__.py +0 -0
  400. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/tutorials/notebooks/01_first_solve_and_core_curves.ipynb +0 -0
  401. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/tutorials/notebooks/02_focused_direct_and_total_site.ipynb +0 -0
  402. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/tutorials/notebooks/03_multisegment_streams.ipynb +0 -0
  403. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/tutorials/notebooks/04_workspace_cases_and_scenarios.ipynb +0 -0
  404. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/tutorials/notebooks/05_workspace_persistence.ipynb +0 -0
  405. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/tutorials/notebooks/06_multiperiod_heat_integration.ipynb +0 -0
  406. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/tutorials/notebooks/07_area_cost_and_exergy.ipynb +0 -0
  407. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/tutorials/notebooks/08_carnot_heat_pump_and_refrigeration.ipynb +0 -0
  408. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/tutorials/notebooks/09_vapour_compression.ipynb +0 -0
  409. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/tutorials/notebooks/10_multiperiod_heat_pumps.ipynb +0 -0
  410. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/tutorials/notebooks/11_process_mvr_and_cascade.ipynb +0 -0
  411. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/tutorials/notebooks/12_cogeneration.ipynb +0 -0
  412. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/tutorials/notebooks/13_multiperiod_cogeneration.ipynb +0 -0
  413. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/tutorials/notebooks/14_energy_transfer.ipynb +0 -0
  414. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/tutorials/notebooks/15_hen_synthesis_and_selection.ipynb +0 -0
  415. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/tutorials/notebooks/16_advanced_hen_methods.ipynb +0 -0
  416. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/tutorials/notebooks/18_results_plots_reports_exports.ipynb +0 -0
  417. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/tutorials/notebooks/19_utility_placement_optimisation.ipynb +0 -0
  418. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/tutorials/notebooks/__init__.py +0 -0
  419. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/tutorials/sample_cases/Four-stream-Yee-and-Grossmann-1990-1.json +0 -0
  420. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/tutorials/sample_cases/__init__.py +0 -0
  421. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/tutorials/sample_cases/basic_pinch.json +0 -0
  422. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/tutorials/sample_cases/chocolate_factory.json +0 -0
  423. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/tutorials/sample_cases/crude_preheat_train.json +0 -0
  424. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/tutorials/sample_cases/crude_preheat_train_multiperiod.json +0 -0
  425. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/tutorials/sample_cases/dairy_chilling_multiperiod.json +0 -0
  426. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/tutorials/sample_cases/process_mvr.json +0 -0
  427. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/tutorials/sample_cases/pulp_mill.json +0 -0
  428. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/tutorials/sample_cases/zonal_site.json +0 -0
  429. {openpinch-0.7.0 → openpinch-0.7.2}/OpenPinch/tutorials/sample_cases/zonal_site_multiperiod.json +0 -0
@@ -13,11 +13,14 @@ from .records import (
13
13
  validate_utility_data,
14
14
  )
15
15
  from .tabular import (
16
- get_column_names_and_units as _get_column_names_and_units,
16
+ problem_frame_from_sheet as _problem_frame_from_sheet,
17
17
  )
18
18
  from .tabular import (
19
19
  problem_records_from_frame as _write_problem_to_dict_and_list,
20
20
  )
21
+ from .tabular import (
22
+ summary_frame_from_sheet as _summary_frame_from_sheet,
23
+ )
21
24
  from .tabular import (
22
25
  target_records_from_frame as _write_targets_to_dict_and_list,
23
26
  )
@@ -121,18 +124,15 @@ def _parse_csv_with_units(
121
124
  "(names, units, and data rows)."
122
125
  )
123
126
 
124
- col_names, col_units = _get_column_names_and_units(
125
- df_full,
126
- sheet_name=kind,
127
- row_units=row_units,
128
- )
129
- df_data: pd.DataFrame = df_full.iloc[row_data:].copy()
130
-
131
- for index in range(len(df_data.columns), len(col_names), -1):
132
- df_data = df_data.drop(columns=index - 1)
133
- # Columns the table leaves out stay absent so the input contract applies
134
- # its defaults; padding them with 0 forced dt_cont and htc to zero.
135
- df_data.columns = col_names[: len(df_data.columns)]
127
+ if kind == "Summary":
128
+ df_data, units_map = _summary_frame_from_sheet(df_full, row_units, row_data)
129
+ else:
130
+ # Columns the table leaves out stay absent so the input contract
131
+ # applies its defaults; padding them with 0 forced dt_cont and htc to
132
+ # zero.
133
+ df_data, units_map = _problem_frame_from_sheet(
134
+ df_full, kind, row_units, row_data
135
+ )
136
136
 
137
137
  def to_number_maybe(value):
138
138
  if isinstance(value, str):
@@ -151,7 +151,6 @@ def _parse_csv_with_units(
151
151
  text_columns = [name for name in ("zone", "name", "type") if name in df_data]
152
152
  numeric_columns = [name for name in df_data.columns if name not in text_columns]
153
153
  df_data[numeric_columns] = df_data[numeric_columns].map(to_number_maybe)
154
- units_map = dict(zip(col_names, col_units))
155
154
  if kind == "Summary":
156
155
  return _write_targets_to_dict_and_list(df_data, units_map, project_name)
157
156
  return _write_problem_to_dict_and_list(df_data, units_map)
@@ -53,6 +53,7 @@ def validate_stream_data(sd: pd.DataFrame):
53
53
  append = cleaned.append
54
54
  previous_zone = None
55
55
  used_names = set()
56
+ original_names: dict[tuple, str] = {}
56
57
  next_default_name_idx = 1
57
58
 
58
59
  for record in records:
@@ -84,6 +85,15 @@ def validate_stream_data(sd: pd.DataFrame):
84
85
  )
85
86
  else:
86
87
  record["name"] = _normalize_label(name, default_stream_prefix)
88
+ key = (record["zone"], record["name"])
89
+ original = _label_text(name)
90
+ earlier = original_names.setdefault(key, original)
91
+ if earlier != original:
92
+ raise ValueError(
93
+ f"Stream names {earlier!r} and {original!r} in zone "
94
+ f"{record['zone']!r} both become {record['name']!r}; "
95
+ "rename one of them."
96
+ )
87
97
 
88
98
  used_names.add(record["name"])
89
99
  append(record)
@@ -121,24 +131,51 @@ def validate_utility_data(ud: pd.DataFrame):
121
131
  valid = ~name_series.isna()
122
132
  if valid.any():
123
133
  valid &= name_series.astype(str).str.strip() != ""
134
+ records = ud.replace({pd.NA: None}).to_dict(orient="records")
135
+ for row, record in enumerate(records, start=1):
136
+ if not valid.iloc[row - 1]:
137
+ _warn_unnamed_utility(row, record)
124
138
  ud = ud.loc[valid].reset_index(drop=True)
125
139
  return ud
126
140
 
127
141
  cleaned = []
128
142
  append = cleaned.append
129
- for record in ud:
143
+ for row, record in enumerate(ud, start=1):
130
144
  if not isinstance(record, dict):
131
145
  continue
132
146
  name = record.get("name")
133
- if name is None or pd.isna(name):
134
- continue
135
- if isinstance(name, str) and not name.strip():
147
+ if (
148
+ name is None
149
+ or pd.isna(name)
150
+ or (isinstance(name, str) and not name.strip())
151
+ ):
152
+ _warn_unnamed_utility(row, record)
136
153
  continue
137
154
  record = {**record, "name": _label_text(name)}
138
155
  append(record)
139
156
  return cleaned
140
157
 
141
158
 
159
+ def _warn_unnamed_utility(row: int, record: dict) -> None:
160
+ """Warn when a utility row dropped for having no name carries data."""
161
+ if any(_cell_has_data(value) for key, value in record.items() if key != "name"):
162
+ warnings.warn(
163
+ f"Utility data: utility row {row} has no name and was ignored.",
164
+ UserWarning,
165
+ stacklevel=4,
166
+ )
167
+
168
+
169
+ def _cell_has_data(value) -> bool:
170
+ if isinstance(value, dict):
171
+ value = value.get("value", value.get("values"))
172
+ if isinstance(value, str):
173
+ return bool(value.strip())
174
+ if isinstance(value, (list, tuple)):
175
+ return any(_cell_has_data(item) for item in value)
176
+ return value is not None and not pd.isna(value)
177
+
178
+
142
179
  def _label_text(value) -> str:
143
180
  """Return a name as text; spreadsheets give numeric names as numbers."""
144
181
  if isinstance(value, float) and value.is_integer():
@@ -2,8 +2,11 @@
2
2
 
3
3
  from __future__ import annotations
4
4
 
5
+ import re
6
+ import warnings
5
7
  from typing import Any, Optional
6
8
 
9
+ import numpy as np
7
10
  import pandas as pd
8
11
 
9
12
  from ...domain.enums import IntegrationType, TargetMethod, ZoneType
@@ -59,12 +62,126 @@ def get_column_names_and_units(df_full, sheet_name, row_units=1):
59
62
  return col_names, col_units
60
63
 
61
64
 
65
+ # Header spellings accepted for each column, compared after lower-casing,
66
+ # reading a Greek delta as "d", dropping any bracketed unit and removing
67
+ # everything but letters and digits.
68
+ _HEADER_ALIASES = {
69
+ "Stream Data": {
70
+ "zone": ("process zone", "zone name"),
71
+ "name": ("stream", "stream name"),
72
+ "t_supply": ("ts", "tsupply", "supply temperature", "tin"),
73
+ "t_target": ("tt", "ttarget", "target temperature", "tout"),
74
+ "heat_flow": ("dh", "heat load", "duty", "q"),
75
+ "heat_capacity_flowrate": ("cp", "mcp", "fcp", "heat capacity flowrate"),
76
+ "dt_cont": ("dtcont", "dt contribution"),
77
+ "htc": ("heat transfer coefficient",),
78
+ },
79
+ "Utility Data": {
80
+ "name": ("utility", "utility name"),
81
+ "type": ("utility type",),
82
+ "t_supply": ("ts", "tsupply", "supply temperature", "tin"),
83
+ "t_target": ("tt", "ttarget", "target temperature", "tout"),
84
+ "dt_cont": ("dtcont", "dt contribution"),
85
+ "price": ("cost", "unit price"),
86
+ "htc": ("heat transfer coefficient",),
87
+ "heat_flow": ("dh", "heat load", "duty", "q"),
88
+ },
89
+ }
90
+
91
+
92
+ def _header_key(text: str) -> str:
93
+ key = re.sub(r"\(.*?\)|\[.*?\]", "", text).lower().replace("δ", "d")
94
+ return re.sub(r"[^a-z0-9]", "", key)
95
+
96
+
97
+ def _map_header_row(header: list, sheet_name: str) -> Optional[list]:
98
+ """Return the field name for each column, or None when no header is known."""
99
+ aliases = {
100
+ _header_key(alias): field
101
+ for field, names in _HEADER_ALIASES[sheet_name].items()
102
+ for alias in (field, *names)
103
+ }
104
+ labels = [
105
+ cell.strip() if isinstance(cell, str) and cell.strip() else None
106
+ for cell in header
107
+ ]
108
+ fields = [aliases.get(_header_key(label)) if label else None for label in labels]
109
+ if not any(fields):
110
+ return None
111
+ for field in set(filter(None, fields)):
112
+ columns = [label for label, f in zip(labels, fields) if f == field]
113
+ if len(columns) > 1:
114
+ raise ValueError(
115
+ f"{sheet_name} has more than one column for {field!r}: "
116
+ f"{', '.join(repr(column) for column in columns)}."
117
+ )
118
+ unknown = [label for label, f in zip(labels, fields) if label and not f]
119
+ if unknown:
120
+ warnings.warn(
121
+ f"{sheet_name} column(s) {', '.join(repr(u) for u in unknown)} "
122
+ "are not recognised and were ignored.",
123
+ UserWarning,
124
+ stacklevel=4,
125
+ )
126
+ return fields
127
+
128
+
129
+ def problem_frame_from_sheet(
130
+ df_full: pd.DataFrame,
131
+ sheet_name: str,
132
+ row_units: int,
133
+ row_data: int,
134
+ ) -> tuple[pd.DataFrame, dict]:
135
+ """Select a stream or utility table's columns and their units.
136
+
137
+ Columns are matched by the header text in the first row. A header with no
138
+ recognised column name falls back to the fixed column order.
139
+ """
140
+ col_names, _ = get_column_names_and_units(df_full, sheet_name, row_units)
141
+ raw_units = [
142
+ unit.replace("°", "deg") if isinstance(unit, str) else None
143
+ for unit in df_full.iloc[row_units].tolist()
144
+ ]
145
+ fields = _map_header_row(df_full.iloc[0].tolist(), sheet_name)
146
+ if fields is None:
147
+ warnings.warn(
148
+ f"{sheet_name} header row has no recognised column names; columns "
149
+ f"are read in the order {', '.join(col_names)}.",
150
+ UserWarning,
151
+ stacklevel=3,
152
+ )
153
+ count = min(len(df_full.columns), len(col_names))
154
+ fields = col_names[:count] + [None] * (len(df_full.columns) - count)
155
+ positions = [index for index, field in enumerate(fields) if field]
156
+ df_data = df_full.iloc[row_data:, positions].copy()
157
+ df_data.columns = [fields[index] for index in positions]
158
+ units_map = {fields[index]: raw_units[index] for index in positions}
159
+ return df_data, units_map
160
+
161
+
162
+ def summary_frame_from_sheet(
163
+ df_full: pd.DataFrame,
164
+ row_units: int,
165
+ row_data: int,
166
+ ) -> tuple[pd.DataFrame, dict]:
167
+ """Name a summary table's columns by position and collect their units."""
168
+ col_names, col_units = get_column_names_and_units(df_full, "Summary", row_units)
169
+ df_data = df_full.iloc[row_data:, : len(col_names)].copy()
170
+ df_data.columns = col_names[: len(df_data.columns)]
171
+ return df_data, dict(zip(col_names, col_units))
172
+
173
+
62
174
  def problem_records_from_frame(df_data: pd.DataFrame, units_map: dict) -> list:
63
175
  """Convert stream/utility tables into JSON-ready row dictionaries with units."""
64
176
  if df_data.empty:
65
177
  return []
66
178
 
67
179
  clean_df = df_data.replace({pd.NA: None}).reset_index(drop=True)
180
+ # Blank separator rows (empty or whitespace-only cells) carry no data; drop
181
+ # them before numeric cells are wrapped as {"value": None, ...} records.
182
+ clean_df = clean_df.loc[~clean_df.map(_is_blank_cell).all(axis=1)]
183
+ if clean_df.empty:
184
+ return []
68
185
  records = clean_df.to_dict(orient="records")
69
186
 
70
187
  for column_name, unit in units_map.items():
@@ -78,11 +195,14 @@ def problem_records_from_frame(df_data: pd.DataFrame, units_map: dict) -> list:
78
195
  lambda value: isinstance(value, str) and not value.strip()
79
196
  )
80
197
  invalid = numeric_values.isna() & ~missing
198
+ invalid |= column_values.map(
199
+ lambda value: isinstance(value, (bool, np.bool_))
200
+ )
81
201
  if invalid.any():
82
202
  row = invalid[invalid].index[0]
83
203
  raise ValueError(
84
204
  f"Column {column_name!r} has a non-numeric value at "
85
- f"data row {row + 1}: {column_values.iloc[row]!r}."
205
+ f"data row {row + 1}: {column_values.loc[row]!r}."
86
206
  )
87
207
  value_entries = [
88
208
  {"value": (None if pd.isna(val) else float(val)), "unit": unit}
@@ -97,6 +217,12 @@ def problem_records_from_frame(df_data: pd.DataFrame, units_map: dict) -> list:
97
217
  return records
98
218
 
99
219
 
220
+ def _is_blank_cell(value: Any) -> bool:
221
+ if isinstance(value, str):
222
+ return not value.strip()
223
+ return value is None or bool(pd.isna(value))
224
+
225
+
100
226
  def target_records_from_frame(
101
227
  df_data: pd.DataFrame,
102
228
  units_map: dict,
@@ -11,11 +11,14 @@ from .records import (
11
11
  validate_utility_data,
12
12
  )
13
13
  from .tabular import (
14
- get_column_names_and_units as _get_column_names_and_units,
14
+ problem_frame_from_sheet as _problem_frame_from_sheet,
15
15
  )
16
16
  from .tabular import (
17
17
  problem_records_from_frame as _write_problem_to_dict_and_list,
18
18
  )
19
+ from .tabular import (
20
+ summary_frame_from_sheet as _summary_frame_from_sheet,
21
+ )
19
22
  from .tabular import (
20
23
  target_records_from_frame as _write_targets_to_dict_and_list,
21
24
  )
@@ -102,23 +105,16 @@ def _parse_sheet_with_units(
102
105
  "(names, units, and data rows)."
103
106
  )
104
107
 
105
- col_names, col_units = _get_column_names_and_units(
106
- df_full,
107
- sheet_name,
108
- row_units,
109
- )
110
- df_data: pd.DataFrame = df_full.iloc[row_data:].copy()
111
-
112
- for index in range(len(df_data.columns), len(col_names), -1):
113
- df_data = df_data.drop(columns=index - 1)
114
- # Columns the table leaves out stay absent so the input contract applies
115
- # its defaults; padding them with 0 forced dt_cont and htc to zero.
116
- df_data.columns = col_names[: len(df_data.columns)]
117
-
118
- units_map = dict(zip(col_names, col_units))
119
- if sheet_name != "Summary":
120
- # Keep every numeric column's unit (price included), as the CSV reader
121
- # does; text columns carry no unit.
108
+ if sheet_name == "Summary":
109
+ df_data, units_map = _summary_frame_from_sheet(df_full, row_units, row_data)
110
+ else:
111
+ # Columns the table leaves out stay absent so the input contract
112
+ # applies its defaults; padding them with 0 forced dt_cont and htc to
113
+ # zero. Numeric columns keep their unit (price included), as the CSV
114
+ # reader does; text columns carry no unit.
115
+ df_data, units_map = _problem_frame_from_sheet(
116
+ df_full, sheet_name, row_units, row_data
117
+ )
122
118
  units_map = {
123
119
  key: unit
124
120
  for key, unit in units_map.items()
@@ -259,6 +259,15 @@ def get_base_problem_table(
259
259
 
260
260
 
261
261
  def get_source_problem_table(target: UtilitySummaryTarget) -> ProblemTable:
262
+ # The target's own table is unrounded; the saved GCC graph is rounded to
263
+ # 4 dp for plotting, so it is only a fallback.
264
+ table = getattr(target, "pt", None)
265
+ if (
266
+ isinstance(table, ProblemTable)
267
+ and has_problem_table_values(table, ProblemTableLabel.T)
268
+ and has_problem_table_values(table, ProblemTableLabel.H_NET)
269
+ ):
270
+ return table
262
271
  graphs = getattr(target, "graphs", None)
263
272
  if isinstance(graphs, dict):
264
273
  gcc = graphs.get(GraphType.GCC.value)
@@ -332,11 +332,9 @@ def apply_exergy_targeting(target: Any, *, dt_cont_multiplier: float = 1.0) -> A
332
332
  }
333
333
  target.exergy_sources = float(nlp_output["source_total"])
334
334
  target.exergy_sinks = float(nlp_output["sink_total"])
335
- target.ETE = (
336
- (target.exergy_sinks / target.exergy_sources)
337
- if target.exergy_sources > tol
338
- else 0.0
339
- )
335
+ gained, released = _balanced_curve_exergy_transfer(target, spec)
336
+ target.exergy_released = released
337
+ target.ETE = gained / released if released > tol else 0.0
340
338
  target.exergy_req_min = float(gcc_output[ProblemTableLabel.X_GCC.value][0])
341
339
  target.exergy_des_min = float(gcc_output[ProblemTableLabel.X_GCC.value][-1])
342
340
  return target
@@ -515,6 +513,129 @@ def _resolve_target_exergy_spec(
515
513
  return None
516
514
 
517
515
 
516
+ def _balanced_curve_exergy_transfer(
517
+ target: Any, spec: dict[str, Any]
518
+ ) -> tuple[float, float]:
519
+ """Return the exergy gained and released across the BCC heat transfer (kW).
520
+
521
+ The Balanced Composite Curves are the process composites plus the
522
+ utilities (and any heat-pump streams), relative to ``ENV_TEMPERATURE``.
523
+ Above ambient the hot curve releases exergy and the cold curve gains it;
524
+ below ambient the roles swap, since cooling a stream there raises its
525
+ exergy. The ratio of gained to released exergy is the exergy transfer
526
+ effectiveness: at most 1, and the shortfall is the exergy destroyed in
527
+ heat transfer. A Direct Integration target uses its real-temperature
528
+ table. Other targets use their own table, un-shifted interval by interval
529
+ as the exergy curves are.
530
+ """
531
+ t_env = spec["t_env"]
532
+ pt_real = getattr(target, "pt_real", None)
533
+ real_columns = (
534
+ [
535
+ _optional_column(pt_real, label)
536
+ for label in (
537
+ ProblemTableLabel.H_HOT,
538
+ ProblemTableLabel.H_HOT_UT,
539
+ ProblemTableLabel.H_COLD,
540
+ ProblemTableLabel.H_COLD_UT,
541
+ )
542
+ ]
543
+ if pt_real is not None
544
+ else [None]
545
+ )
546
+ if all(column is not None for column in real_columns):
547
+ t_vals = _to_float_array(pt_real[ProblemTableLabel.T])
548
+ hot_curves = [(real_columns[0], 0.0), (real_columns[1], 0.0)]
549
+ cold_curves = [(real_columns[2], 0.0), (real_columns[3], 0.0)]
550
+ else:
551
+ pt = target.pt
552
+ t_vals = spec["temperatures"]
553
+ n_intervals = max(len(t_vals) - 1, 0)
554
+ if spec["gcc_is_real"]:
555
+ hot_offset = cold_offset = np.zeros(n_intervals, dtype=float)
556
+ else:
557
+ hot_offset = _interval_shifts(
558
+ spec.get("hot_shifts"), n_intervals, spec["dt_cont_shift"]
559
+ )
560
+ cold_offset = -_interval_shifts(
561
+ spec.get("cold_shifts"), n_intervals, spec["dt_cont_shift"]
562
+ )
563
+ # Heat-pump streams carry no temperature contribution.
564
+ hot_curves = [
565
+ (_optional_column(pt, ProblemTableLabel.H_HOT), hot_offset),
566
+ (_optional_column(pt, ProblemTableLabel.H_HOT_UT), hot_offset),
567
+ (_optional_column(pt, ProblemTableLabel.H_HOT_HP), 0.0),
568
+ ]
569
+ cold_curves = [
570
+ (_optional_column(pt, ProblemTableLabel.H_COLD), cold_offset),
571
+ (_optional_column(pt, ProblemTableLabel.H_COLD_UT), cold_offset),
572
+ (_optional_column(pt, ProblemTableLabel.H_COLD_HP), 0.0),
573
+ ]
574
+ gained = released = 0.0
575
+ for curves, is_hot in ((hot_curves, True), (cold_curves, False)):
576
+ for h_vals, offset in curves:
577
+ if h_vals is None:
578
+ continue
579
+ # Above ambient the hot curve releases and the cold curve gains;
580
+ # below ambient it is the other way round.
581
+ above, below = _curve_exergy_changes(t_vals, h_vals, offset, t_env)
582
+ if is_hot:
583
+ released += above
584
+ gained += below
585
+ else:
586
+ released += below
587
+ gained += above
588
+ return float(gained), float(released)
589
+
590
+
591
+ def _curve_exergy_changes(
592
+ t_vals: np.ndarray,
593
+ h_vals: np.ndarray,
594
+ offset: np.ndarray | float,
595
+ t_env: float,
596
+ ) -> tuple[float, float]:
597
+ """Return the (above-ambient, below-ambient) exergy of a T-H curve, in kW.
598
+
599
+ Each interval contributes dH (1 - T0/T) integrated along it, which is
600
+ positive above ambient and negative below; intervals that cross ambient
601
+ are split there, and the below-ambient part is returned as a magnitude.
602
+ ``t_vals`` are in degC, falling down the table, and ``offset`` moves each
603
+ interval onto real temperatures. A linear segment is integrated exactly.
604
+ """
605
+ h_vals = np.nan_to_num(np.asarray(h_vals, dtype=float))
606
+ t_abs = np.asarray(t_vals, dtype=float) + C_to_K
607
+ t_upper = t_abs[:-1] + offset
608
+ t_lower = t_abs[1:] + offset
609
+ t_ref = t_env + C_to_K
610
+ delta_h = h_vals[:-1] - h_vals[1:]
611
+ above = below = 0.0
612
+ for t_u, t_l, d_h in zip(
613
+ np.broadcast_to(t_upper, delta_h.shape),
614
+ np.broadcast_to(t_lower, delta_h.shape),
615
+ delta_h,
616
+ ):
617
+ if abs(d_h) <= tol:
618
+ continue
619
+ span = t_u - t_l
620
+ if abs(span) <= tol:
621
+ pieces = [d_h * (1.0 - t_ref / t_u)]
622
+ else:
623
+ cp = d_h / span
624
+ hi, lo = max(t_u, t_l), min(t_u, t_l)
625
+ bounds = [hi, t_ref, lo] if lo < t_ref < hi else [hi, lo]
626
+ sign = 1.0 if span > 0 else -1.0
627
+ pieces = [
628
+ sign * cp * ((a - b) - t_ref * np.log(a / b))
629
+ for a, b in zip(bounds[:-1], bounds[1:])
630
+ ]
631
+ for piece in pieces:
632
+ if piece >= 0.0:
633
+ above += piece
634
+ else:
635
+ below -= piece
636
+ return float(above), float(below)
637
+
638
+
518
639
  def _zone_stream_shift_sources(target: Any) -> tuple[list, list, int | None] | None:
519
640
  """Return the hot and cold streams (with utilities) behind ``target``."""
520
641
  zone = getattr(target, "parent_zone", None)
@@ -6,7 +6,7 @@ from typing import Any
6
6
 
7
7
  import numpy as np
8
8
 
9
- from ...domain.configuration import tol
9
+ from ...domain.configuration import duty_tolerance, heat_resolution, tol
10
10
  from ...domain.enums import (
11
11
  ArrowHead,
12
12
  GraphType,
@@ -34,12 +34,16 @@ def clean_composite_curve_ends(
34
34
  """Remove redundant constant-enthalpy points from curve ends."""
35
35
  y_vals = np.asarray(y_vals)
36
36
  x_vals = np.asarray(x_vals)
37
- # Compare the duty range with tol (variance is in kW^2, so a small curve
38
- # with a real duty would vanish).
39
- if x_vals.size == 0 or np.ptp(x_vals) <= tol:
37
+ # Compare the duty range with the heat-flow resolution (variance is in
38
+ # kW^2, so a small curve with a real duty would vanish).
39
+ if x_vals.size == 0 or np.ptp(x_vals) <= heat_resolution(x_vals):
40
40
  return np.array([]), np.array([])
41
- start_mask = ~np.isclose(x_vals, x_vals[0] * np.ones(len(x_vals)), atol=tol)
42
- end_mask = ~np.isclose(x_vals, x_vals[-1] * np.ones(len(x_vals)), atol=tol)
41
+ # Only rounding residue counts as constant at the ends: an absolute
42
+ # tolerance relative to the duty, not np.isclose's default rtol=1e-5 of
43
+ # the end value, which trimmed real end segments on MW-scale curves.
44
+ atol = max(duty_tolerance(x_vals), np.finfo(float).eps)
45
+ start_mask = np.abs(x_vals - x_vals[0]) > atol
46
+ end_mask = np.abs(x_vals - x_vals[-1]) > atol
43
47
  start = np.flatnonzero(start_mask)[0] - 1
44
48
  end = np.flatnonzero(end_mask)[-1] + 1
45
49
  return y_vals[start : end + 1], x_vals[start : end + 1]
@@ -114,7 +118,11 @@ def _make_composite_graph(
114
118
  for field, stream_loc in zip(fields, stream_types):
115
119
  column_key = _column_key(field)
116
120
  x_vals = _column_to_list(data, column_key)
117
- if _should_plot_series(x_vals):
121
+ # A constant series (for example the cold composite of a problem with
122
+ # only hot streams) has no curve to draw.
123
+ if _should_plot_series(x_vals) and np.ptp(
124
+ np.asarray(x_vals, dtype=float)
125
+ ) > heat_resolution(x_vals):
118
126
  segments.extend(
119
127
  _graph_cc(
120
128
  key,
@@ -12,6 +12,7 @@ from ....contracts.synthesis.task import (
12
12
  )
13
13
  from ....domain.enums import HeatExchangerNetworkDesignMethod
14
14
  from ....domain.progress import emit as emit_progress
15
+ from ...parallelism import supported_workers
15
16
  from ..errors import WorkflowContractError
16
17
  from ..models.problem import InternalHeatExchangerNetworkProblem
17
18
  from ..solver.arrays import PreparedSolverArrays, problem_to_solver_arrays
@@ -73,7 +74,8 @@ class LocalSynthesisExecutor:
73
74
  self.stage_order.append(tasks[0].method)
74
75
  self._report_progress(tasks, 0)
75
76
 
76
- worker_count = max(1, min(int(max_parallel), len(tasks)))
77
+ max_parallel = supported_workers(int(max_parallel))
78
+ worker_count = max(1, min(max_parallel, len(tasks)))
77
79
  if worker_count > 1 and _can_build_tasks_in_workers(tasks, parent_outcomes):
78
80
  solve_inputs = (
79
81
  (
@@ -85,10 +87,11 @@ class LocalSynthesisExecutor:
85
87
  )
86
88
  for task in tasks
87
89
  )
88
- with self.worker_pool_factory(worker_count) as pool:
89
- solved_results = self._reported(
90
- tasks, pool.map(_build_and_solve_root_task, solve_inputs), 0
91
- )
90
+ solved_results = self._reported(
91
+ tasks,
92
+ self._map(_build_and_solve_root_task, solve_inputs, worker_count),
93
+ 0,
94
+ )
92
95
  return self._collect_outcomes(tasks, solved_results, {})
93
96
 
94
97
  built: list[
@@ -123,8 +126,8 @@ class LocalSynthesisExecutor:
123
126
  if task.task_id is not None:
124
127
  failed[task.task_id] = _failed_task_outcome(task, str(exc))
125
128
 
126
- worker_count = max(1, min(int(max_parallel), len(built)))
127
- branch_parallel = max(1, int(max_parallel)) if worker_count == 1 else 1
129
+ worker_count = max(1, min(max_parallel, len(built)))
130
+ branch_parallel = max(1, max_parallel) if worker_count == 1 else 1
128
131
  solve_inputs = (
129
132
  (
130
133
  task,
@@ -137,20 +140,26 @@ class LocalSynthesisExecutor:
137
140
  )
138
141
  # Tasks that failed to build count as done.
139
142
  done = len(tasks) - len(built)
140
- if worker_count == 1:
141
- solved_results = self._reported(
142
- tasks,
143
- (_solve_built_task(worker_args) for worker_args in solve_inputs),
144
- done,
145
- )
146
- else:
147
- with self.worker_pool_factory(worker_count) as pool:
148
- solved_results = self._reported(
149
- tasks, pool.map(_solve_built_task, solve_inputs), done
150
- )
143
+ solved_results = self._reported(
144
+ tasks, self._map(_solve_built_task, solve_inputs, worker_count), done
145
+ )
151
146
 
152
147
  return self._collect_outcomes(tasks, solved_results, failed)
153
148
 
149
+ def _map(self, func, inputs, worker_count: int):
150
+ """Yield ``func`` over ``inputs``, in a worker pool when one can start."""
151
+ if worker_count > 1:
152
+ try:
153
+ pool = self.worker_pool_factory(worker_count)
154
+ except NotImplementedError as exc:
155
+ logger.debug("Solving HEN tasks serially: %s", exc)
156
+ else:
157
+ with pool:
158
+ yield from pool.map(func, inputs)
159
+ return
160
+ for worker_args in inputs:
161
+ yield func(worker_args)
162
+
154
163
  def _reported(
155
164
  self,
156
165
  tasks: Sequence[HeatExchangerNetworkSynthesisTask],
@@ -252,9 +252,9 @@ def _identities_by_axis(
252
252
  )
253
253
 
254
254
 
255
- def _single_utility(identities: tuple[str, ...], label: str) -> str:
255
+ def _utilities(identities: tuple[str, ...], label: str) -> tuple[str, ...]:
256
256
  if not identities:
257
257
  raise ValueError(
258
258
  f"solved heat exchanger network extraction requires at least one {label}."
259
259
  )
260
- return identities[0]
260
+ return identities