OpenPinch 0.2.2__tar.gz → 0.2.3__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 (130) hide show
  1. {openpinch-0.2.2 → openpinch-0.2.3}/OpenPinch/classes/stream.py +41 -61
  2. {openpinch-0.2.2 → openpinch-0.2.3}/OpenPinch/classes/stream_collection.py +1 -1
  3. {openpinch-0.2.2 → openpinch-0.2.3}/OpenPinch/lib/enums.py +1 -0
  4. {openpinch-0.2.2 → openpinch-0.2.3}/OpenPinch/services/common/utility_targeting.py +20 -12
  5. {openpinch-0.2.2 → openpinch-0.2.3}/OpenPinch/services/indirect_heat_integration/indirect_integration_entry.py +2 -2
  6. {openpinch-0.2.2 → openpinch-0.2.3}/PKG-INFO +1 -1
  7. {openpinch-0.2.2 → openpinch-0.2.3}/pyproject.toml +1 -1
  8. {openpinch-0.2.2 → openpinch-0.2.3}/.gitignore +0 -0
  9. {openpinch-0.2.2 → openpinch-0.2.3}/LICENSE +0 -0
  10. {openpinch-0.2.2 → openpinch-0.2.3}/OpenPinch/__init__.py +0 -0
  11. {openpinch-0.2.2 → openpinch-0.2.3}/OpenPinch/__main__.py +0 -0
  12. {openpinch-0.2.2 → openpinch-0.2.3}/OpenPinch/classes/__init__.py +0 -0
  13. {openpinch-0.2.2 → openpinch-0.2.3}/OpenPinch/classes/_problem/__init__.py +0 -0
  14. {openpinch-0.2.2 → openpinch-0.2.3}/OpenPinch/classes/_problem/_loading.py +0 -0
  15. {openpinch-0.2.2 → openpinch-0.2.3}/OpenPinch/classes/_problem/_output.py +0 -0
  16. {openpinch-0.2.2 → openpinch-0.2.3}/OpenPinch/classes/_problem/_plot_accessor.py +0 -0
  17. {openpinch-0.2.2 → openpinch-0.2.3}/OpenPinch/classes/_problem/_result_extraction.py +0 -0
  18. {openpinch-0.2.2 → openpinch-0.2.3}/OpenPinch/classes/_problem/_target_accessor.py +0 -0
  19. {openpinch-0.2.2 → openpinch-0.2.3}/OpenPinch/classes/_problem/_target_dispatch.py +0 -0
  20. {openpinch-0.2.2 → openpinch-0.2.3}/OpenPinch/classes/_problem/_validation.py +0 -0
  21. {openpinch-0.2.2 → openpinch-0.2.3}/OpenPinch/classes/_problem_table/__init__.py +0 -0
  22. {openpinch-0.2.2 → openpinch-0.2.3}/OpenPinch/classes/_problem_table/_problem_table_constants.py +0 -0
  23. {openpinch-0.2.2 → openpinch-0.2.3}/OpenPinch/classes/_stream_collection/__init__.py +0 -0
  24. {openpinch-0.2.2 → openpinch-0.2.3}/OpenPinch/classes/_stream_collection/_helpers.py +0 -0
  25. {openpinch-0.2.2 → openpinch-0.2.3}/OpenPinch/classes/_workspace/__init__.py +0 -0
  26. {openpinch-0.2.2 → openpinch-0.2.3}/OpenPinch/classes/_workspace/execution.py +0 -0
  27. {openpinch-0.2.2 → openpinch-0.2.3}/OpenPinch/classes/_workspace/payloads.py +0 -0
  28. {openpinch-0.2.2 → openpinch-0.2.3}/OpenPinch/classes/_workspace/views.py +0 -0
  29. {openpinch-0.2.2 → openpinch-0.2.3}/OpenPinch/classes/pinch_problem.py +0 -0
  30. {openpinch-0.2.2 → openpinch-0.2.3}/OpenPinch/classes/pinch_workspace.py +0 -0
  31. {openpinch-0.2.2 → openpinch-0.2.3}/OpenPinch/classes/problem_table.py +0 -0
  32. {openpinch-0.2.2 → openpinch-0.2.3}/OpenPinch/classes/value.py +0 -0
  33. {openpinch-0.2.2 → openpinch-0.2.3}/OpenPinch/classes/zone.py +0 -0
  34. {openpinch-0.2.2 → openpinch-0.2.3}/OpenPinch/data/__init__.py +0 -0
  35. {openpinch-0.2.2 → openpinch-0.2.3}/OpenPinch/data/notebooks/01_basic_pinch_and_dtcont_sensitivity.ipynb +0 -0
  36. {openpinch-0.2.2 → openpinch-0.2.3}/OpenPinch/data/notebooks/02_total_site_targets_and_sugcc.ipynb +0 -0
  37. {openpinch-0.2.2 → openpinch-0.2.3}/OpenPinch/data/notebooks/03_carnot_hpr_comparison.ipynb +0 -0
  38. {openpinch-0.2.2 → openpinch-0.2.3}/OpenPinch/data/notebooks/04_multistate_targeting_and_state_comparison.ipynb +0 -0
  39. {openpinch-0.2.2 → openpinch-0.2.3}/OpenPinch/data/notebooks/05_schema_service_and_output_workflows.ipynb +0 -0
  40. {openpinch-0.2.2 → openpinch-0.2.3}/OpenPinch/data/notebooks/__init__.py +0 -0
  41. {openpinch-0.2.2 → openpinch-0.2.3}/OpenPinch/data/sample_cases/__init__.py +0 -0
  42. {openpinch-0.2.2 → openpinch-0.2.3}/OpenPinch/data/sample_cases/basic_pinch.json +0 -0
  43. {openpinch-0.2.2 → openpinch-0.2.3}/OpenPinch/data/sample_cases/chocolate_factory.json +0 -0
  44. {openpinch-0.2.2 → openpinch-0.2.3}/OpenPinch/data/sample_cases/crude_preheat_train.json +0 -0
  45. {openpinch-0.2.2 → openpinch-0.2.3}/OpenPinch/data/sample_cases/crude_preheat_train_multistate.json +0 -0
  46. {openpinch-0.2.2 → openpinch-0.2.3}/OpenPinch/data/sample_cases/heat_pump_targeting.json +0 -0
  47. {openpinch-0.2.2 → openpinch-0.2.3}/OpenPinch/data/sample_cases/pulp_mill.json +0 -0
  48. {openpinch-0.2.2 → openpinch-0.2.3}/OpenPinch/data/sample_cases/zonal_site.json +0 -0
  49. {openpinch-0.2.2 → openpinch-0.2.3}/OpenPinch/data/sample_cases/zonal_site_multistate.json +0 -0
  50. {openpinch-0.2.2 → openpinch-0.2.3}/OpenPinch/lib/__init__.py +0 -0
  51. {openpinch-0.2.2 → openpinch-0.2.3}/OpenPinch/lib/config.py +0 -0
  52. {openpinch-0.2.2 → openpinch-0.2.3}/OpenPinch/lib/config_metadata.py +0 -0
  53. {openpinch-0.2.2 → openpinch-0.2.3}/OpenPinch/lib/problem_table_types.py +0 -0
  54. {openpinch-0.2.2 → openpinch-0.2.3}/OpenPinch/lib/schemas/__init__.py +0 -0
  55. {openpinch-0.2.2 → openpinch-0.2.3}/OpenPinch/lib/schemas/common.py +0 -0
  56. {openpinch-0.2.2 → openpinch-0.2.3}/OpenPinch/lib/schemas/graphs.py +0 -0
  57. {openpinch-0.2.2 → openpinch-0.2.3}/OpenPinch/lib/schemas/hpr.py +0 -0
  58. {openpinch-0.2.2 → openpinch-0.2.3}/OpenPinch/lib/schemas/io.py +0 -0
  59. {openpinch-0.2.2 → openpinch-0.2.3}/OpenPinch/lib/schemas/report_units.py +0 -0
  60. {openpinch-0.2.2 → openpinch-0.2.3}/OpenPinch/lib/schemas/reporting.py +0 -0
  61. {openpinch-0.2.2 → openpinch-0.2.3}/OpenPinch/lib/schemas/targets.py +0 -0
  62. {openpinch-0.2.2 → openpinch-0.2.3}/OpenPinch/lib/schemas/turbine.py +0 -0
  63. {openpinch-0.2.2 → openpinch-0.2.3}/OpenPinch/lib/schemas/workspace.py +0 -0
  64. {openpinch-0.2.2 → openpinch-0.2.3}/OpenPinch/lib/unit_system.py +0 -0
  65. {openpinch-0.2.2 → openpinch-0.2.3}/OpenPinch/main.py +0 -0
  66. {openpinch-0.2.2 → openpinch-0.2.3}/OpenPinch/resources.py +0 -0
  67. {openpinch-0.2.2 → openpinch-0.2.3}/OpenPinch/services/__init__.py +0 -0
  68. {openpinch-0.2.2 → openpinch-0.2.3}/OpenPinch/services/common/__init__.py +0 -0
  69. {openpinch-0.2.2 → openpinch-0.2.3}/OpenPinch/services/common/capital_cost_and_area_targeting.py +0 -0
  70. {openpinch-0.2.2 → openpinch-0.2.3}/OpenPinch/services/common/gcc_manipulation.py +0 -0
  71. {openpinch-0.2.2 → openpinch-0.2.3}/OpenPinch/services/common/graph_data.py +0 -0
  72. {openpinch-0.2.2 → openpinch-0.2.3}/OpenPinch/services/common/graph_series_meta.py +0 -0
  73. {openpinch-0.2.2 → openpinch-0.2.3}/OpenPinch/services/common/miscellaneous.py +0 -0
  74. {openpinch-0.2.2 → openpinch-0.2.3}/OpenPinch/services/common/problem_table_analysis.py +0 -0
  75. {openpinch-0.2.2 → openpinch-0.2.3}/OpenPinch/services/common/temperature_driving_force.py +0 -0
  76. {openpinch-0.2.2 → openpinch-0.2.3}/OpenPinch/services/direct_heat_integration/__init__.py +0 -0
  77. {openpinch-0.2.2 → openpinch-0.2.3}/OpenPinch/services/direct_heat_integration/direct_integration_entry.py +0 -0
  78. {openpinch-0.2.2 → openpinch-0.2.3}/OpenPinch/services/exergy_analysis/__init__.py +0 -0
  79. {openpinch-0.2.2 → openpinch-0.2.3}/OpenPinch/services/exergy_analysis/exergy_targeting_entry.py +0 -0
  80. {openpinch-0.2.2 → openpinch-0.2.3}/OpenPinch/services/heat_pump_integration/__init__.py +0 -0
  81. {openpinch-0.2.2 → openpinch-0.2.3}/OpenPinch/services/heat_pump_integration/common/__init__.py +0 -0
  82. {openpinch-0.2.2 → openpinch-0.2.3}/OpenPinch/services/heat_pump_integration/common/encoding.py +0 -0
  83. {openpinch-0.2.2 → openpinch-0.2.3}/OpenPinch/services/heat_pump_integration/common/layout.py +0 -0
  84. {openpinch-0.2.2 → openpinch-0.2.3}/OpenPinch/services/heat_pump_integration/common/postprocessing.py +0 -0
  85. {openpinch-0.2.2 → openpinch-0.2.3}/OpenPinch/services/heat_pump_integration/common/preprocessing.py +0 -0
  86. {openpinch-0.2.2 → openpinch-0.2.3}/OpenPinch/services/heat_pump_integration/common/shared.py +0 -0
  87. {openpinch-0.2.2 → openpinch-0.2.3}/OpenPinch/services/heat_pump_integration/heat_pump_and_refrigeration_entry.py +0 -0
  88. {openpinch-0.2.2 → openpinch-0.2.3}/OpenPinch/services/heat_pump_integration/targeting_services/__init__.py +0 -0
  89. {openpinch-0.2.2 → openpinch-0.2.3}/OpenPinch/services/heat_pump_integration/targeting_services/brayton.py +0 -0
  90. {openpinch-0.2.2 → openpinch-0.2.3}/OpenPinch/services/heat_pump_integration/targeting_services/cascade_vapour_compression.py +0 -0
  91. {openpinch-0.2.2 → openpinch-0.2.3}/OpenPinch/services/heat_pump_integration/targeting_services/multi_simple_carnot.py +0 -0
  92. {openpinch-0.2.2 → openpinch-0.2.3}/OpenPinch/services/heat_pump_integration/targeting_services/multi_simple_vapour_compression.py +0 -0
  93. {openpinch-0.2.2 → openpinch-0.2.3}/OpenPinch/services/heat_pump_integration/targeting_services/multi_temperature_carnot.py +0 -0
  94. {openpinch-0.2.2 → openpinch-0.2.3}/OpenPinch/services/heat_pump_integration/unit_models/__init__.py +0 -0
  95. {openpinch-0.2.2 → openpinch-0.2.3}/OpenPinch/services/heat_pump_integration/unit_models/brayton_heat_pump.py +0 -0
  96. {openpinch-0.2.2 → openpinch-0.2.3}/OpenPinch/services/heat_pump_integration/unit_models/cascade_vapour_compression_cycle.py +0 -0
  97. {openpinch-0.2.2 → openpinch-0.2.3}/OpenPinch/services/heat_pump_integration/unit_models/parallel_vapour_compression_cycles.py +0 -0
  98. {openpinch-0.2.2 → openpinch-0.2.3}/OpenPinch/services/heat_pump_integration/unit_models/vapour_compression_cycle.py +0 -0
  99. {openpinch-0.2.2 → openpinch-0.2.3}/OpenPinch/services/indirect_heat_integration/__init__.py +0 -0
  100. {openpinch-0.2.2 → openpinch-0.2.3}/OpenPinch/services/input_data_processing/__init__.py +0 -0
  101. {openpinch-0.2.2 → openpinch-0.2.3}/OpenPinch/services/input_data_processing/_canonicalization.py +0 -0
  102. {openpinch-0.2.2 → openpinch-0.2.3}/OpenPinch/services/input_data_processing/_utility_preparation.py +0 -0
  103. {openpinch-0.2.2 → openpinch-0.2.3}/OpenPinch/services/input_data_processing/data_preparation.py +0 -0
  104. {openpinch-0.2.2 → openpinch-0.2.3}/OpenPinch/services/power_cogeneration/__init__.py +0 -0
  105. {openpinch-0.2.2 → openpinch-0.2.3}/OpenPinch/services/power_cogeneration/power_cogeneration_analysis.py +0 -0
  106. {openpinch-0.2.2 → openpinch-0.2.3}/OpenPinch/services/power_cogeneration/unit_models/multi_stage_steam_turbine.py +0 -0
  107. {openpinch-0.2.2 → openpinch-0.2.3}/OpenPinch/services/power_cogeneration_analysis/power_cogeneration_analysis.py +0 -0
  108. {openpinch-0.2.2 → openpinch-0.2.3}/OpenPinch/services/services_entry.py +0 -0
  109. {openpinch-0.2.2 → openpinch-0.2.3}/OpenPinch/streamlit_webviewer/web_graphing.py +0 -0
  110. {openpinch-0.2.2 → openpinch-0.2.3}/OpenPinch/utils/__init__.py +0 -0
  111. {openpinch-0.2.2 → openpinch-0.2.3}/OpenPinch/utils/_tabular_input.py +0 -0
  112. {openpinch-0.2.2 → openpinch-0.2.3}/OpenPinch/utils/bb_optimisers/__init__.py +0 -0
  113. {openpinch-0.2.2 → openpinch-0.2.3}/OpenPinch/utils/bb_optimisers/bayesian_optimisation.py +0 -0
  114. {openpinch-0.2.2 → openpinch-0.2.3}/OpenPinch/utils/bb_optimisers/cmaes.py +0 -0
  115. {openpinch-0.2.2 → openpinch-0.2.3}/OpenPinch/utils/bb_optimisers/common.py +0 -0
  116. {openpinch-0.2.2 → openpinch-0.2.3}/OpenPinch/utils/bb_optimisers/dual_annealing.py +0 -0
  117. {openpinch-0.2.2 → openpinch-0.2.3}/OpenPinch/utils/bb_optimisers/rbf_surrogate.py +0 -0
  118. {openpinch-0.2.2 → openpinch-0.2.3}/OpenPinch/utils/blackbox_minimisers.py +0 -0
  119. {openpinch-0.2.2 → openpinch-0.2.3}/OpenPinch/utils/costing.py +0 -0
  120. {openpinch-0.2.2 → openpinch-0.2.3}/OpenPinch/utils/csv_to_json.py +0 -0
  121. {openpinch-0.2.2 → openpinch-0.2.3}/OpenPinch/utils/decorators.py +0 -0
  122. {openpinch-0.2.2 → openpinch-0.2.3}/OpenPinch/utils/export.py +0 -0
  123. {openpinch-0.2.2 → openpinch-0.2.3}/OpenPinch/utils/heat_exchanger.py +0 -0
  124. {openpinch-0.2.2 → openpinch-0.2.3}/OpenPinch/utils/input_validation.py +0 -0
  125. {openpinch-0.2.2 → openpinch-0.2.3}/OpenPinch/utils/plots.py +0 -0
  126. {openpinch-0.2.2 → openpinch-0.2.3}/OpenPinch/utils/stream_linearisation.py +0 -0
  127. {openpinch-0.2.2 → openpinch-0.2.3}/OpenPinch/utils/value_resolution.py +0 -0
  128. {openpinch-0.2.2 → openpinch-0.2.3}/OpenPinch/utils/water_properties.py +0 -0
  129. {openpinch-0.2.2 → openpinch-0.2.3}/OpenPinch/utils/wkbook_to_json.py +0 -0
  130. {openpinch-0.2.2 → openpinch-0.2.3}/README.md +0 -0
@@ -329,7 +329,7 @@ class Stream:
329
329
  """Activate or deactivate the stream for downstream analysis."""
330
330
  self._active = bool(value)
331
331
 
332
- # === Computed Temperature Bounds ===
332
+ # === Computed Temperature Properties ===
333
333
 
334
334
  @property
335
335
  def t_min(self) -> Optional[Value]:
@@ -351,6 +351,11 @@ class Stream:
351
351
  """Shifted maximum temperature."""
352
352
  return self._copy_value(self._t_max_star)
353
353
 
354
+ @property
355
+ def t_entr_mean(self) -> Optional[Value]:
356
+ """Entropic mean temperature of supply and target temperatures."""
357
+ return self._copy_value(self._t_entr_mean)
358
+
354
359
  # === Readable Alias Properties ===
355
360
 
356
361
  @property
@@ -393,6 +398,11 @@ class Stream:
393
398
  """Alias for the shifted minimum stream temperature."""
394
399
  return self.t_min_star
395
400
 
401
+ @property
402
+ def entropic_mean_temperature(self) -> Optional[Value]:
403
+ """Alias for the entropic mean temperature."""
404
+ return self.t_entr_mean
405
+
396
406
  @property
397
407
  def shifted_maximum_temperature(self) -> Optional[Value]:
398
408
  """Alias for the shifted maximum stream temperature."""
@@ -485,22 +495,22 @@ class Stream:
485
495
  np.full(int(self._num_states), float(parsed.value), dtype=float),
486
496
  unit=parsed.unit,
487
497
  )
488
- if self.state_ids is None or (
489
- len(self.state_ids) == 1 and len(self.state_ids) != parsed.num_states
498
+ if self._weights is None or (
499
+ len(self._weights) == 1 and len(self._weights) != parsed.num_states
490
500
  ):
491
501
  self._num_states = parsed.num_states
492
502
  self._state_ids = {str(i): i for i in range(self._num_states)}
493
503
  self._weights = np.ones(self._num_states, dtype=float)
494
504
 
495
- if len(self.state_ids) > 1 and len(self.state_ids) != parsed.num_states:
496
- raise ValueError("State IDs length must match the number of states.")
505
+ if len(self._weights) > 1 and len(self._weights) != parsed.num_states:
506
+ raise ValueError("Weights length must match the number of states.")
497
507
 
498
- setattr(self, internal_name, parsed)
508
+ setattr(self, internal_name, parsed.to(self._VALUE_UNITS[internal_name]))
499
509
  self._validate_num_states()
500
510
  if update_derived:
501
511
  self.update_derived_properties()
502
512
 
503
- def set_value_attr_at_state_idx(
513
+ def set_value_attr_at_idx(
504
514
  self,
505
515
  attr_name: str,
506
516
  value: float | Value | np.ndarray = None,
@@ -560,13 +570,22 @@ class Stream:
560
570
  def _calculate_missing_properties(self) -> None:
561
571
  """Calculate any missing core properties from available data."""
562
572
  if self._t_supply is None:
563
- return
564
-
573
+ if self._t_target is None:
574
+ self._t_supply = Value(15, "degC").to(self._VALUE_UNITS["_t_supply"])
575
+ else:
576
+ self._t_supply = Value(self._t_target).to(
577
+ self._VALUE_UNITS["_t_supply"]
578
+ )
565
579
  if self._t_target is None:
566
- self._t_target = Value(self._t_supply)
567
-
580
+ self._t_target = Value(self._t_supply).to(self._VALUE_UNITS["_t_target"])
568
581
  if self._dt_cont is None:
569
582
  self._dt_cont = Value(0.0, unit=self._VALUE_UNITS["_dt_cont"])
583
+ if self._heat_flow is None:
584
+ self._heat_flow = Value(0.0, unit=self._VALUE_UNITS["_heat_flow"])
585
+ if self._htc is None:
586
+ self._htc = Value(1.0, unit=self._VALUE_UNITS["_htc"])
587
+ if self._price is None:
588
+ self._price = Value(0.0, unit=self._VALUE_UNITS["_price"])
570
589
 
571
590
  state_size = self._state_vector_size()
572
591
  if self._heat_flow is None:
@@ -594,66 +613,22 @@ class Stream:
594
613
  adjusted_target_arr[hot_mask] = t_supply_arr[hot_mask] - 0.01
595
614
  self._t_target = self._build_value(
596
615
  adjusted_target_arr,
597
- unit=self._VALUE_UNITS["_t_target"],
598
- )
616
+ unit=self._t_supply.unit,
617
+ ).to(self._VALUE_UNITS["_t_target"])
599
618
 
600
619
  def update_derived_properties(self) -> None:
601
- if self._t_supply is None:
602
- return
603
- if self._t_target is None:
604
- self._t_target = Value(self._t_supply)
605
- if self._dt_cont is None:
606
- self._dt_cont = Value(0.0, unit=self._VALUE_UNITS["_dt_cont"])
607
- if self._heat_flow is None:
608
- self._heat_flow = Value(0.0, unit=self._VALUE_UNITS["_heat_flow"])
609
- if self._htc is None:
610
- self._htc = Value(1.0, unit=self._VALUE_UNITS["_htc"])
611
- if self._price is None:
612
- self._price = Value(0.0, unit=self._VALUE_UNITS["_price"])
613
-
614
620
  state_size = self._state_vector_size()
615
621
  t_supply = self._value_array(self._t_supply, size=state_size)
616
622
  t_target = self._value_array(self._t_target, size=state_size)
617
623
  heat_flow = self._value_array(self._heat_flow, size=state_size)
618
- dt_cont = self._value_array(self._dt_cont, size=state_size)
619
624
  htc = self._value_array(self._htc, size=state_size)
620
625
  price = self._value_array(self._price, size=state_size)
621
626
 
622
- dt_cont_act = dt_cont * float(self._dt_cont_multiplier)
623
- self._dt_cont_act = self._build_value(
624
- dt_cont_act,
625
- unit=self._VALUE_UNITS["_dt_cont_act"],
626
- )
627
+ dt_cont_act = self._dt_cont * float(self._dt_cont_multiplier)
628
+ self._dt_cont_act = dt_cont_act.to(self._VALUE_UNITS["_dt_cont_act"])
627
629
 
628
630
  hot_states = t_supply > t_target + _TEMPERATURE_EQUAL_TOL
629
631
  cold_states = t_supply < t_target - _TEMPERATURE_EQUAL_TOL
630
- neutral_states = ~(hot_states | cold_states)
631
- active_classes = {
632
- label
633
- for label, mask in (
634
- (ST.Hot.value, hot_states),
635
- (ST.Cold.value, cold_states),
636
- (ST.Both.value, neutral_states),
637
- )
638
- if np.any(mask)
639
- }
640
- if len(active_classes) > 1:
641
- labels = (
642
- list(self._state_ids.keys())
643
- if self._state_ids is not None and len(self._state_ids) == state_size
644
- else [str(idx) for idx in range(state_size)]
645
- )
646
- hot_state_ids = [sid for sid, active in zip(labels, hot_states) if active]
647
- cold_state_ids = [sid for sid, active in zip(labels, cold_states) if active]
648
- neutral_state_ids = [
649
- sid for sid, active in zip(labels, neutral_states) if active
650
- ]
651
- raise ValueError(
652
- "Stream states must classify consistently. "
653
- f"Hot={hot_state_ids}, "
654
- f"Cold={cold_state_ids}, "
655
- f"Neutral={neutral_state_ids}."
656
- )
657
632
 
658
633
  if np.any(hot_states):
659
634
  self._type = ST.Hot.value
@@ -668,7 +643,7 @@ class Stream:
668
643
  t_min_star = t_min + dt_cont_act
669
644
  t_max_star = t_max + dt_cont_act
670
645
  else:
671
- self._type = ST.Both.value
646
+ self._type = ST.Neutral.value
672
647
  t_min = t_supply
673
648
  t_max = t_target
674
649
  t_min_star = t_min.copy()
@@ -703,6 +678,11 @@ class Stream:
703
678
  t_max_star,
704
679
  unit=self._VALUE_UNITS["_t_max_star"],
705
680
  )
681
+ Ts_K = self._t_supply.to("K")
682
+ Tt_K = self._t_target.to("K")
683
+ self._t_entr_mean = ((Ts_K - Tt_K) / (np.log(Ts_K) - np.log(Tt_K))).to(
684
+ self._t_supply.unit
685
+ )
706
686
  self._cp = self._build_value(cp, unit=self._VALUE_UNITS["_cp"])
707
687
  self._htr = self._build_value(htr, unit=self._VALUE_UNITS["_htr"])
708
688
  self._rcp_prod = self._build_value(
@@ -767,7 +747,7 @@ class Stream:
767
747
  *,
768
748
  state_id: str | None = None,
769
749
  ) -> None:
770
- self.set_value_attr_at_state_idx(
750
+ self.set_value_attr_at_idx(
771
751
  attr_name,
772
752
  value,
773
753
  idx=self.get_state_index(state_id),
@@ -175,7 +175,7 @@ class StreamCollection:
175
175
  if idx is None:
176
176
  setattr(stream, attr_name, value)
177
177
  else:
178
- stream.set_value_attr_at_state_idx(attr_name, value, idx=idx)
178
+ stream.set_value_attr_at_idx(attr_name, value, idx=idx)
179
179
  return self
180
180
 
181
181
  def set_sort_key(self, key: Union[str, List[str], Callable], reverse: bool = False):
@@ -113,6 +113,7 @@ class StreamType(Enum):
113
113
  Hot = "Hot"
114
114
  Cold = "Cold"
115
115
  Both = "Both"
116
+ Neutral = "Neutral"
116
117
  Unassigned = ""
117
118
 
118
119
 
@@ -195,7 +195,7 @@ def _assign_utility(
195
195
  Q_assigned,
196
196
  )
197
197
  if Q_ut_max > tol:
198
- u.set_value_attr_at_state_idx(
198
+ u.set_value_attr_at_idx(
199
199
  attr_name="heat_flow",
200
200
  value=Q_ut_max,
201
201
  idx=idx,
@@ -242,19 +242,27 @@ def _maximise_utility_duty(
242
242
  return 0.0
243
243
 
244
244
  dt_tar_valid = dt_tar[valid_mask]
245
- Q_pot_valid = Q_pot[valid_mask]
246
-
247
245
  if dt_tar_valid.max() < 0:
248
246
  return 0.0
249
247
 
250
- Q_ts_max = Q_pot_valid.max()
248
+ def _candidate_limit(q_pot_values: np.ndarray) -> tuple[float, float, float]:
249
+ q_pot_valid = q_pot_values[valid_mask]
250
+ if q_pot_valid.size == 0:
251
+ return 0.0, 0.0, np.inf
252
+
253
+ q_ts_max = q_pot_valid.max()
254
+ q_tt = np.full_like(q_pot_valid, np.inf, dtype=float)
255
+ slope_mask = (-dt_tar_valid) > tol
256
+ if np.any(slope_mask):
257
+ q_tt[slope_mask] = (
258
+ q_pot_valid[slope_mask] / (-dt_tar_valid[slope_mask]) * abs(Tt - Ts)
259
+ )
260
+ q_tt_max = q_tt.min() if q_tt.size > 0 else np.inf
261
+ return min(q_ts_max, q_tt_max), q_ts_max, q_tt_max
251
262
 
252
- Q_tt = np.full_like(Q_pot_valid, np.inf, dtype=float)
253
- slope_mask = (-dt_tar_valid) > tol
254
- if np.any(slope_mask):
255
- Q_tt[slope_mask] = (
256
- Q_pot_valid[slope_mask] / (-dt_tar_valid[slope_mask]) * abs(Tt - Ts)
257
- )
258
- Q_tt_max = Q_tt.min() if Q_tt.size > 0 else np.inf
263
+ q_adj, q_ts_adj, q_tt_adj = _candidate_limit(Q_pot)
264
+ q_cur, _, _ = _candidate_limit(current_H - Q_assigned)
259
265
 
260
- return min(Q_ts_max, Q_tt_max) if dt_tar_valid.max() >= 0 else 0.0
266
+ if np.isfinite(q_tt_adj) and q_tt_adj < q_ts_adj:
267
+ return min(q_adj, q_cur)
268
+ return q_adj
@@ -59,7 +59,7 @@ def compute_total_subzone_utility_targets(
59
59
  utility_cost += t.utility_cost
60
60
 
61
61
  for j in range(len(hot_utilities)):
62
- hot_utilities[j].set_value_attr_at_state_idx(
62
+ hot_utilities[j].set_value_attr_at_idx(
63
63
  attr_name="heat_flow",
64
64
  value=hot_utilities[j].heat_flow[idx]
65
65
  + t.hot_utilities[j].heat_flow[idx],
@@ -67,7 +67,7 @@ def compute_total_subzone_utility_targets(
67
67
  )
68
68
 
69
69
  for j in range(len(cold_utilities)):
70
- cold_utilities[j].set_value_attr_at_state_idx(
70
+ cold_utilities[j].set_value_attr_at_idx(
71
71
  attr_name="heat_flow",
72
72
  value=cold_utilities[j].heat_flow[idx]
73
73
  + t.cold_utilities[j].heat_flow[idx],
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: OpenPinch
3
- Version: 0.2.2
3
+ Version: 0.2.3
4
4
  Summary: An advanced pinch analysis and total site integration toolkit
5
5
  Project-URL: Homepage, https://github.com/waikato-ahuora-smart-energy-systems/OpenPinch
6
6
  Project-URL: Issues, https://github.com/waikato-ahuora-smart-energy-systems/OpenPinch/issues
@@ -34,7 +34,7 @@ dependencies=[
34
34
  "pydantic",
35
35
  "scipy",
36
36
  ]
37
- version = "0.2.2"
37
+ version = "0.2.3"
38
38
  authors = [
39
39
  { name="Tim Walmsley", email="tim.walmsley@waikato.ac.nz" },
40
40
  ]
File without changes
File without changes
File without changes
File without changes