OpenPinch 0.0.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- openpinch-0.0.1.dist-info/METADATA +17 -0
- openpinch-0.0.1.dist-info/RECORD +32 -0
- openpinch-0.0.1.dist-info/WHEEL +4 -0
- openpinch-0.0.1.dist-info/licenses/LICENSE +8 -0
- src/__init__.py +1 -0
- src/analysis/__init__.py +13 -0
- src/analysis/additional_analysis.py +421 -0
- src/analysis/data_preparation.py +397 -0
- src/analysis/graphs.py +631 -0
- src/analysis/operation_analysis.py +504 -0
- src/analysis/power_cogeneration_analysis.py +481 -0
- src/analysis/problem_table_analysis.py +235 -0
- src/analysis/process_analysis.py +149 -0
- src/analysis/region_analysis.py +23 -0
- src/analysis/response.py +50 -0
- src/analysis/site_analysis.py +177 -0
- src/analysis/support_methods.py +189 -0
- src/analysis/utility_targeting.py +319 -0
- src/classes/__init__.py +6 -0
- src/classes/parentClasses/__init__.py +2 -0
- src/classes/parentClasses/unit_operation.py +28 -0
- src/classes/problem_table.py +270 -0
- src/classes/stream.py +256 -0
- src/classes/stream_collection.py +139 -0
- src/classes/target.py +310 -0
- src/classes/value.py +121 -0
- src/classes/zone.py +253 -0
- src/main.py +88 -0
- src/utils/__init__.py +3 -0
- src/utils/decorators.py +63 -0
- src/utils/heat_exchanger_eq.py +206 -0
- src/utils/water_properties.py +88 -0
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: OpenPinch
|
|
3
|
+
Version: 0.0.1
|
|
4
|
+
Summary: Package for performing Pinch Analysis on stream data
|
|
5
|
+
Project-URL: Homepage, https://github.com/waikato-ahuora-smart-energy-systems/OpenPinch
|
|
6
|
+
Project-URL: Issues, https://github.com/waikato-ahuora-smart-energy-systems/OpenPinch/issues
|
|
7
|
+
Author-email: Tim Walmsley <tim.walmsley@waikato.ac.nz>
|
|
8
|
+
License-Expression: MIT
|
|
9
|
+
License-File: LICENSE
|
|
10
|
+
Classifier: Operating System :: OS Independent
|
|
11
|
+
Classifier: Programming Language :: Python :: 3
|
|
12
|
+
Requires-Python: >=3.11
|
|
13
|
+
Description-Content-Type: text/markdown
|
|
14
|
+
|
|
15
|
+
# OpenPinch
|
|
16
|
+
OpenPinch is a Python package for pinch analysis and total site integration studies. OpenPinch incorperates advanced methods, including multi-scale targeting, multiple utility targeting for isothermal and non-isothermal utility, assisted heat integration, grand composite curve manipulation, and more. Many of the concepts are also embedded in an Excel workbook through VBA macros--the original development. OpenPinch began as part of Tim Walmsley's PhD study at the University of Waikato. Over the subsquent years, OpenPinch has grew into the form it is today.
|
|
17
|
+
More details to follow.
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
src/__init__.py,sha256=Px_6IhyfVHFHL2XOnU1vSSKeQqjsVsvsOMN7wYbTrx4,36
|
|
2
|
+
src/main.py,sha256=l9yEWcdsSyjGrl81g_LoZuKXqKJtc4mnPXC0RdGSBEI,3371
|
|
3
|
+
src/analysis/__init__.py,sha256=5Vg0dtLFCm-GU5IPwpBMKCIW6iYKBSyA6dHIy7lLZrU,664
|
|
4
|
+
src/analysis/additional_analysis.py,sha256=FLEvRaWNROSeRTAcWbP5u1QE-3gHk5GvxHdFQisdNkk,15311
|
|
5
|
+
src/analysis/data_preparation.py,sha256=T2QQSnuzaSq9xDs-rOTv9kNrweHLIu3XYP4y6DzYEUk,17212
|
|
6
|
+
src/analysis/graphs.py,sha256=lTbepR6knBQknGzCDrmdB_z2duIxWwre5futSCTVMlI,22349
|
|
7
|
+
src/analysis/operation_analysis.py,sha256=ukMwShlV9RIn7uWXxAF0jLGyBEO7V6W3t00CRyYhHGM,19196
|
|
8
|
+
src/analysis/power_cogeneration_analysis.py,sha256=CCCphvvo2g3b9O9RWos1NPQXbv4KeN7Uj0g_Id124kg,17324
|
|
9
|
+
src/analysis/problem_table_analysis.py,sha256=TTlzMpa21qNrtICBwMZzxRE4Vte8kVt_5HWhmAV6IkY,10209
|
|
10
|
+
src/analysis/process_analysis.py,sha256=Orgs0RpKb26zOrbSw_GcOMx-PVTw7kttOF0MTVneTko,6885
|
|
11
|
+
src/analysis/region_analysis.py,sha256=uwA9uPMFB-ZVfmoymNDHs3jLMpd47X30y1pBzUcOdRY,797
|
|
12
|
+
src/analysis/response.py,sha256=E-_F4O8FfzKMo26By39mlllTqGjXTwXmurOHMO-5HtY,1732
|
|
13
|
+
src/analysis/site_analysis.py,sha256=L5lNbIORkbT9UdrocXJgqYkiiWpYLSljmKARlvGhY5s,7032
|
|
14
|
+
src/analysis/support_methods.py,sha256=2jzHvCCikh5PizOg4ocNtZl9Z_xp5GnQSystLm_6h8s,7409
|
|
15
|
+
src/analysis/utility_targeting.py,sha256=BgKSx-GGYPB0QjH4MmNtH7AnOVvsLo6b6jIa3J1ipJQ,13143
|
|
16
|
+
src/classes/__init__.py,sha256=Wjwr1kQwdikR4SaMe9PK7iPrGiktaB3yq2wcz2L40Cc,190
|
|
17
|
+
src/classes/problem_table.py,sha256=V_CK9sX2ZDM-SAkd15XNpoS_rYHxqmTfyoYUkV_P-jA,9236
|
|
18
|
+
src/classes/stream.py,sha256=aw9XtTRdK42DnsLM8wK6B2PvbYRpJhphOiDQehYTVcs,7970
|
|
19
|
+
src/classes/stream_collection.py,sha256=I9G_S-eswVfJpF1-6umb9S4J2jbjAm7Kj2IdZG0Euiw,5017
|
|
20
|
+
src/classes/target.py,sha256=DA0FqzV16OfvHxazLyqk-pIjGyqORyd4_dp9CwXf0IM,10277
|
|
21
|
+
src/classes/value.py,sha256=RO6uuljbClX4fWMEfsBlMvTnNc7lNt4qV6tG3ZBX3XQ,3400
|
|
22
|
+
src/classes/zone.py,sha256=pWkXDs8pmSg5PYuVRMJH-_LxklPBO45VY0SBZEh_EXY,8429
|
|
23
|
+
src/classes/parentClasses/__init__.py,sha256=daEdpEyAJIa8b2VkCqSKcw8PaExcB6Qro80XNes_sHA,2
|
|
24
|
+
src/classes/parentClasses/unit_operation.py,sha256=L-sqzsOPrEXBoCQDoAeSfw5p5wcbSd4tIayVZtosrxo,1015
|
|
25
|
+
src/utils/__init__.py,sha256=U31STGKAgVs5YBIrQ_BTfjKFzBGBn85c2lTLBk3sKgo,106
|
|
26
|
+
src/utils/decorators.py,sha256=GFbyBaV7DoSfiv3a-CzqoM4AmR2kTSKpWDrMvuucP6g,1912
|
|
27
|
+
src/utils/heat_exchanger_eq.py,sha256=34b6Qjn-agi7lTrn9Zzbvuo8irROeORuKl1w0JxtJzg,7328
|
|
28
|
+
src/utils/water_properties.py,sha256=pGEctrFG4r2ISMY_xvTXuOp1GnjwIb3K0RxdVNa_toc,1894
|
|
29
|
+
openpinch-0.0.1.dist-info/METADATA,sha256=HmWtv4xUveKuHYDWryppvqzQd8gt0Pkd5q21bdj3Zgg,1158
|
|
30
|
+
openpinch-0.0.1.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
31
|
+
openpinch-0.0.1.dist-info/licenses/LICENSE,sha256=-_wSPDS49w9xOa0uyB9WWmHa3iduYMbVNhKtvhJrBT8,1061
|
|
32
|
+
openpinch-0.0.1.dist-info/RECORD,,
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
Copyright 2025 Tim Walmsley
|
|
2
|
+
|
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
|
4
|
+
|
|
5
|
+
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
|
6
|
+
|
|
7
|
+
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
8
|
+
|
src/__init__.py
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
from .main import target, visualise
|
src/analysis/__init__.py
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
from .graphs import visualise_graphs
|
|
2
|
+
from .response import output_response
|
|
3
|
+
from .data_preparation import prepare_problem_struture
|
|
4
|
+
from .operation_analysis import get_energy_transfer_retrofit_analysis
|
|
5
|
+
from .problem_table_analysis import problem_table_algorithm, calc_problem_table
|
|
6
|
+
from .process_analysis import get_process_pinch_targets
|
|
7
|
+
from .power_cogeneration_analysis import get_power_cogeneration_above_pinch
|
|
8
|
+
from .site_analysis import get_site_targets
|
|
9
|
+
from .region_analysis import get_regional_targets
|
|
10
|
+
from .utility_targeting import get_zonal_utility_targets, target_utility, calc_GGC_utility
|
|
11
|
+
from .support_methods import *
|
|
12
|
+
from .additional_analysis import *
|
|
13
|
+
|
|
@@ -0,0 +1,421 @@
|
|
|
1
|
+
import pandas as pd
|
|
2
|
+
import numpy as np
|
|
3
|
+
from ..utils import *
|
|
4
|
+
from ..lib.enums import *
|
|
5
|
+
from ..classes import *
|
|
6
|
+
from .support_methods import *
|
|
7
|
+
from .power_cogeneration_analysis import get_power_cogeneration_above_pinch
|
|
8
|
+
|
|
9
|
+
__all__ = ["get_additional_zonal_pinch_analysis"]
|
|
10
|
+
|
|
11
|
+
#######################################################################################################
|
|
12
|
+
# Public API --- TODO
|
|
13
|
+
#######################################################################################################
|
|
14
|
+
|
|
15
|
+
def get_additional_zonal_pinch_analysis(pt: ProblemTable, pt_real: ProblemTable, config: Configuration):
|
|
16
|
+
"""Calculates additional graphs and targets."""
|
|
17
|
+
|
|
18
|
+
# Target heat transfer area and number of exchanger units based on Balanced CC
|
|
19
|
+
area = target_area(pt_real)
|
|
20
|
+
num_units = min_number_hx(pt)
|
|
21
|
+
capital_cost = num_units * config.FC + num_units * config.VC * (area / num_units) ** config.EXP
|
|
22
|
+
annual_capital_cost = capital_cost * capital_recovery_factor(config.DISCOUNT_RATE, config.SERV_LIFE)
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
# # Target exergy supply, rejection, and destruction
|
|
26
|
+
# gcc_x = _calc_exergy_gcc(z, pt_real, bcc, z.graphs[GT.GCC_Act.value])
|
|
27
|
+
# z.add_graph(GT.GCC_X.value, gcc_x)
|
|
28
|
+
|
|
29
|
+
# # Requires review and comparision to previous Excel implementation
|
|
30
|
+
# GCC_AI = None
|
|
31
|
+
# if z.config.AHT_BUTTON_SELECTED:
|
|
32
|
+
# z.add_graph('GCC_AI', GCC_AI)
|
|
33
|
+
|
|
34
|
+
# # Target co-generation of heat and power
|
|
35
|
+
# if z.config.TURBINE_WORK_BUTTON:
|
|
36
|
+
# z = get_power_cogeneration_above_pinch(z)
|
|
37
|
+
|
|
38
|
+
# # Save data for TS profiles based on HT direction
|
|
39
|
+
|
|
40
|
+
return {
|
|
41
|
+
"area": area,
|
|
42
|
+
"num_units": num_units,
|
|
43
|
+
"capital_cost": capital_cost,
|
|
44
|
+
"annual_capital_cost": annual_capital_cost,
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
#######################################################################################################
|
|
49
|
+
# Helper functions
|
|
50
|
+
#######################################################################################################
|
|
51
|
+
|
|
52
|
+
def target_area(z, pt: ProblemTable) -> float:
|
|
53
|
+
"""Estimates a heat transfer area target based on counter-current heat transfer using vectorized pandas operations."""
|
|
54
|
+
if abs(pt['HCC'].iloc[0] - pt['CCC'].iloc[0]) > ZERO:
|
|
55
|
+
raise ValueError("Balanced Composite Curves are imbalanced.")
|
|
56
|
+
|
|
57
|
+
# Collect H_val intervals and sort
|
|
58
|
+
h_vals = pd.Series(pt['HCC'].iloc[:-1].tolist() + pt['CCC'].iloc[:-1].tolist()).sort_values().reset_index(drop=True)
|
|
59
|
+
h_start = h_vals[:-1].values
|
|
60
|
+
h_end = h_vals[1:].values
|
|
61
|
+
dh = h_start - h_end
|
|
62
|
+
|
|
63
|
+
# Interpolate temperatures for each H at both ends
|
|
64
|
+
t_h1 = np.interp(h_start, pt['HCC'], pt['T'])
|
|
65
|
+
t_h2 = np.interp(h_end, pt['HCC'], pt['T'])
|
|
66
|
+
t_c1 = np.interp(h_start, pt['CCC'], pt['T'])
|
|
67
|
+
t_c2 = np.interp(h_end, pt['CCC'], pt['T'])
|
|
68
|
+
|
|
69
|
+
delta_T1 = t_h1 - t_c1
|
|
70
|
+
delta_T2 = t_h2 - t_c2
|
|
71
|
+
|
|
72
|
+
t_lmtd = np.where(
|
|
73
|
+
abs(delta_T1 - delta_T2) < 1e-6,
|
|
74
|
+
(delta_T1 + delta_T2) / 2,
|
|
75
|
+
(delta_T1 - delta_T2) / np.log(delta_T1 / delta_T2)
|
|
76
|
+
)
|
|
77
|
+
|
|
78
|
+
cp_hot = dh / (t_h1 - t_h2)
|
|
79
|
+
cp_cold = dh / (t_c1 - t_c2)
|
|
80
|
+
cp_min = np.minimum(cp_hot, cp_cold)
|
|
81
|
+
cp_max = np.maximum(cp_hot, cp_cold)
|
|
82
|
+
|
|
83
|
+
eff = dh / (cp_min * (t_h1 - t_c2))
|
|
84
|
+
cp_star = cp_min / cp_max
|
|
85
|
+
|
|
86
|
+
if z.config.CF_SELECTED:
|
|
87
|
+
arrangement = HX.CF.value
|
|
88
|
+
elif z.config.PF_SELECTED:
|
|
89
|
+
arrangement = HX.PF.value
|
|
90
|
+
else:
|
|
91
|
+
arrangement = HX.ShellTube.value
|
|
92
|
+
|
|
93
|
+
ntu = np.vectorize(HX_NTU)(arrangement, eff, cp_star)
|
|
94
|
+
|
|
95
|
+
r_hot = np.interp(h_end, pt['HCC'], pt['RH'])
|
|
96
|
+
r_cold = np.interp(h_end, pt['CCC'], pt['RC'])
|
|
97
|
+
u_o = 1 / (r_hot + r_cold)
|
|
98
|
+
|
|
99
|
+
area_segments = ntu * cp_min / u_o
|
|
100
|
+
total_area = np.sum(area_segments)
|
|
101
|
+
|
|
102
|
+
return float(total_area)
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
def min_number_hx(z, pt_df: ProblemTable, bcc_star_df: ProblemTable) -> int:
|
|
106
|
+
"""
|
|
107
|
+
Estimates the minimum number of heat exchangers required for the pinch problem using vectorized interval logic.
|
|
108
|
+
|
|
109
|
+
Args:
|
|
110
|
+
z: Zone with hot/cold streams and utilities.
|
|
111
|
+
pt_df (ProblemTable): Problem table DataFrame with temperature column.
|
|
112
|
+
bcc_star_df (ProblemTable): Balanced Composite Curve data with 'CCC' and 'HCC'.
|
|
113
|
+
|
|
114
|
+
Returns:
|
|
115
|
+
int: Minimum number of exchangers.
|
|
116
|
+
"""
|
|
117
|
+
T_vals = pt_df.iloc[:, 0].values
|
|
118
|
+
CCC = bcc_star_df['CCC'].values
|
|
119
|
+
HCC = bcc_star_df['HCC'].values
|
|
120
|
+
|
|
121
|
+
num_hx = 0
|
|
122
|
+
i = 0
|
|
123
|
+
while i < len(T_vals) - 1:
|
|
124
|
+
if abs(CCC[i + 1] - HCC[i + 1]) > ZERO:
|
|
125
|
+
break
|
|
126
|
+
i += 1
|
|
127
|
+
|
|
128
|
+
i_1 = i
|
|
129
|
+
i += 1
|
|
130
|
+
|
|
131
|
+
while i < len(T_vals):
|
|
132
|
+
i_0 = i_1
|
|
133
|
+
if abs(CCC[i] - HCC[i]) < ZERO or i == len(T_vals) - 1:
|
|
134
|
+
i_1 = i
|
|
135
|
+
T_high, T_low = T_vals[i_0], T_vals[i_1]
|
|
136
|
+
|
|
137
|
+
def count_crossing(streams):
|
|
138
|
+
t_max = np.array([s.t_max_star for s in streams])
|
|
139
|
+
t_min = np.array([s.t_min_star for s in streams])
|
|
140
|
+
return np.sum(
|
|
141
|
+
((t_max > T_low + ZERO) & (t_max <= T_high + ZERO)) |
|
|
142
|
+
((t_min >= T_low - ZERO) & (t_min < T_high - ZERO)) |
|
|
143
|
+
((t_min < T_low - ZERO) & (t_max > T_high + ZERO))
|
|
144
|
+
)
|
|
145
|
+
|
|
146
|
+
num_hx += count_crossing(z.hot_streams)
|
|
147
|
+
num_hx += count_crossing(z.cold_streams)
|
|
148
|
+
|
|
149
|
+
def count_utility_crossing(utilities):
|
|
150
|
+
t_max = np.array([u.t_max_star for u in utilities])
|
|
151
|
+
t_min = np.array([u.t_min_star for u in utilities])
|
|
152
|
+
return np.sum(
|
|
153
|
+
(t_max > T_low + ZERO) & (t_max <= T_high + ZERO) |
|
|
154
|
+
(t_min >= T_low - ZERO) & (t_min < T_high - ZERO)
|
|
155
|
+
)
|
|
156
|
+
|
|
157
|
+
num_hx += count_utility_crossing(z.hot_utilities)
|
|
158
|
+
num_hx += count_utility_crossing(z.cold_utilities)
|
|
159
|
+
num_hx -= 1
|
|
160
|
+
|
|
161
|
+
j = i_1
|
|
162
|
+
while j < len(T_vals) - 1:
|
|
163
|
+
if abs(CCC[j + 1] - HCC[j + 1]) > ZERO:
|
|
164
|
+
break
|
|
165
|
+
j += 1
|
|
166
|
+
|
|
167
|
+
i = j
|
|
168
|
+
i_1 = j
|
|
169
|
+
|
|
170
|
+
i += 1
|
|
171
|
+
|
|
172
|
+
return int(num_hx)
|
|
173
|
+
|
|
174
|
+
|
|
175
|
+
############# Review and testing needed!!!!!!!!!!!!!!
|
|
176
|
+
|
|
177
|
+
|
|
178
|
+
############# Review and testing needed!!!!!!!!!!!!!!
|
|
179
|
+
# def _calc_exergy_gcc(z, pt_real, BCC, GCC_Act):
|
|
180
|
+
# """Determine Exergy Transfer Effectiveness including process and utility streams.
|
|
181
|
+
# """
|
|
182
|
+
# # Exergy Transfer Effectiveness proposed by Marmolejo-Correa, D., Gundersen, T., 2012.
|
|
183
|
+
# # A comparison of exergy efficiency definitions with focus on low temperature processes.
|
|
184
|
+
# # Energy 44, 477–489. https://doi.org/10.1016/j.energy.2012.06.001
|
|
185
|
+
# x_source, x_sink, n_ETE = _calc_total_exergy(BCC)
|
|
186
|
+
# z.exergy_sources = x_source
|
|
187
|
+
# z.exergy_sinks = x_sink
|
|
188
|
+
# z.ETE = n_ETE
|
|
189
|
+
|
|
190
|
+
# GCC_X = z.Calc_ExGCC(GCC_Act)
|
|
191
|
+
# x_source, x_sink, n_ETE = _calc_total_exergy(pt_real, Col_T=0, Col_HCC=4, Col_CCC=7)
|
|
192
|
+
|
|
193
|
+
# z.exergy_req_min = GCC_X[1][1]
|
|
194
|
+
# z.exergy_des_min = GCC_X[1][-1]
|
|
195
|
+
|
|
196
|
+
# return GCC_X
|
|
197
|
+
|
|
198
|
+
############# Review and testing needed!!!!!!!!!!!!!!
|
|
199
|
+
# def _calc_total_exergy(z: Zone, CC, x_source=0, x_sink=0, n_ETE=0, Col_T=0, Col_HCC=2, Col_CCC=4):
|
|
200
|
+
# """Determines the source and sink exergy of a balanced CC."""
|
|
201
|
+
# for i in range(1, len(CC[0])):
|
|
202
|
+
# T_ex1 = compute_exergetic_temperature(CC[Col_T][i - 1], T_ref=z.config.TEMP_REF)
|
|
203
|
+
# T_ex2 = compute_exergetic_temperature(CC[Col_T][i], T_ref=z.config.TEMP_REF)
|
|
204
|
+
# CP_hot = (CC[Col_HCC][i - 1] - CC[Col_HCC][i]) / (CC[Col_T][i - 1] - CC[Col_T][i])
|
|
205
|
+
# CP_cold = (CC[Col_CCC][i - 1] - CC[Col_CCC][i]) / (CC[Col_T][i - 1] - CC[Col_T][i])
|
|
206
|
+
|
|
207
|
+
# if T_ex1 > 0:
|
|
208
|
+
# x_source = x_source + CP_hot * T_ex1
|
|
209
|
+
# x_sink = x_sink + CP_cold * T_ex1
|
|
210
|
+
# else:
|
|
211
|
+
# x_source = x_source + CP_cold * T_ex1
|
|
212
|
+
# x_sink = x_sink + CP_hot * T_ex1
|
|
213
|
+
|
|
214
|
+
# if T_ex2 > 0:
|
|
215
|
+
# x_source = x_source - CP_hot * T_ex2
|
|
216
|
+
# x_sink = x_sink - CP_cold * T_ex2
|
|
217
|
+
# else:
|
|
218
|
+
# x_source = x_source - CP_cold * T_ex2
|
|
219
|
+
# x_sink = x_sink - CP_hot * T_ex2
|
|
220
|
+
|
|
221
|
+
# n_ETE = x_sink / x_source if x_source > ZERO else 0
|
|
222
|
+
|
|
223
|
+
# return x_source, x_sink, n_ETE
|
|
224
|
+
|
|
225
|
+
############# Review and testing needed!!!!!!!!!!!!!!
|
|
226
|
+
def Target_Area(z, BCC):
|
|
227
|
+
"""Estimates a heat transfer area target for a z based on counter-current heat transfer.
|
|
228
|
+
"""
|
|
229
|
+
Area = 0
|
|
230
|
+
|
|
231
|
+
# Calculates the area table
|
|
232
|
+
H_val = [0 for i in range(len(BCC[0]) * 2)]
|
|
233
|
+
|
|
234
|
+
ColT = 0
|
|
235
|
+
ColRH = 1
|
|
236
|
+
ColHCC = 2
|
|
237
|
+
ColRC = 3
|
|
238
|
+
ColCCC = 4
|
|
239
|
+
|
|
240
|
+
# Check the BCC is balanced, if not stop the calculation and return an error
|
|
241
|
+
if abs(BCC[ColHCC][0] - BCC[ColCCC][0]) > ZERO:
|
|
242
|
+
raise Exception('Balanced Composite Curves are imbalanced...')
|
|
243
|
+
|
|
244
|
+
# Collate all H intervals
|
|
245
|
+
for i in range(1, len(BCC[0])):
|
|
246
|
+
H_val[i * 2 - 2] = BCC[ColHCC][i - 1]
|
|
247
|
+
H_val[i * 2 - 1] = BCC[ColCCC][i - 1]
|
|
248
|
+
|
|
249
|
+
H_val = H_val.sort(reverse=True)
|
|
250
|
+
|
|
251
|
+
CalcTable = [ [None for j in range(len(H_val) - 1)] for i in range(10)]
|
|
252
|
+
|
|
253
|
+
for i in range(len(H_val) - 1):
|
|
254
|
+
CalcTable[0][i] = H_val[i]
|
|
255
|
+
CalcTable[1][i] = H_val[i + 1]
|
|
256
|
+
|
|
257
|
+
r_h = 0
|
|
258
|
+
r_c = 0
|
|
259
|
+
for i in range(len(CalcTable[0])):
|
|
260
|
+
while (CalcTable[0][i] - BCC[ColHCC][r_h + 1]) <= ZERO and r_h + 2 <= len(BCC[0]):
|
|
261
|
+
r_h += 1
|
|
262
|
+
while (CalcTable[0][i] - BCC[ColCCC][r_c + 1]) <= ZERO and r_c + 2 <= len(BCC[0]):
|
|
263
|
+
r_c += 1
|
|
264
|
+
|
|
265
|
+
if (CalcTable[0][i] - BCC[ColHCC][r_h + 1] <= ZERO or CalcTable[0][i] - BCC[ColCCC][r_c + 1] <= ZERO) \
|
|
266
|
+
and (r_h + 1 == len(BCC[0]) or r_c + 1 == len(BCC[0])):
|
|
267
|
+
break
|
|
268
|
+
|
|
269
|
+
T_h1 = linear_interpolation(CalcTable[0][i], BCC[ColHCC][r_h], BCC[ColHCC][r_h + 1], BCC[ColT][r_h], BCC[ColT][r_h + 1])
|
|
270
|
+
T_h2 = linear_interpolation(CalcTable[1][i], BCC[ColHCC][r_h], BCC[ColHCC][r_h + 1], BCC[ColT][r_h], BCC[ColT][r_h + 1])
|
|
271
|
+
T_c1 = linear_interpolation(CalcTable[0][i], BCC[ColCCC][r_c], BCC[ColCCC][r_c + 1], BCC[ColT][r_c], BCC[ColT][r_c + 1])
|
|
272
|
+
T_c2 = linear_interpolation(CalcTable[1][i], BCC[ColCCC][r_c], BCC[ColCCC][r_c + 1], BCC[ColT][r_c], BCC[ColT][r_c + 1])
|
|
273
|
+
|
|
274
|
+
dh = CalcTable[0][i] - CalcTable[1][i]
|
|
275
|
+
|
|
276
|
+
T_LMTD = find_LMTD(T_h1, T_h2, T_c1, T_c2)
|
|
277
|
+
CalcTable[2][i] = T_LMTD
|
|
278
|
+
|
|
279
|
+
CP_hot = dh / (T_h1 - T_h2)
|
|
280
|
+
CP_cold = dh / (T_c1 - T_c2)
|
|
281
|
+
|
|
282
|
+
CP_min = min(CP_hot, CP_cold)
|
|
283
|
+
CP_max = max(CP_hot, CP_cold)
|
|
284
|
+
eff = dh / (CP_min * (T_h1 - T_c2))
|
|
285
|
+
CP_star = CP_min / CP_max
|
|
286
|
+
|
|
287
|
+
Arrangement = None
|
|
288
|
+
if z.config.CF_SELECTED:
|
|
289
|
+
Arrangement = HX.CF.value
|
|
290
|
+
elif z.config.PF_SELECTED:
|
|
291
|
+
Arrangement = HX.PF.value
|
|
292
|
+
else:
|
|
293
|
+
Arrangement = HX.ShellTube.value
|
|
294
|
+
|
|
295
|
+
Ntu = HX_NTU(Arrangement, eff, CP_star)
|
|
296
|
+
|
|
297
|
+
# Heat transfer resistance and coefficient
|
|
298
|
+
R_hot = BCC[ColRH][r_h + 1]
|
|
299
|
+
R_cold = BCC[ColRC][r_c + 1]
|
|
300
|
+
U_o = 1 / (R_hot + R_cold)
|
|
301
|
+
|
|
302
|
+
CalcTable[3][i] = Ntu * CP_min / U_o
|
|
303
|
+
CalcTable[4][i] = dh / (U_o * T_LMTD)
|
|
304
|
+
|
|
305
|
+
Area = Area + CalcTable[3][i]
|
|
306
|
+
|
|
307
|
+
return Area
|
|
308
|
+
|
|
309
|
+
def MinNumberHX(z, pt, BCC_star):
|
|
310
|
+
"""Estimates the minimum number of heat exchanger units for a given Pinch problem.
|
|
311
|
+
"""
|
|
312
|
+
Num_HX = 0
|
|
313
|
+
i = 0
|
|
314
|
+
while i < len(pt[0]) - 1:
|
|
315
|
+
if abs(BCC_star[4][i + 1] - BCC_star[2][i + 1]) > ZERO:
|
|
316
|
+
break
|
|
317
|
+
i += 1
|
|
318
|
+
|
|
319
|
+
i_1 = i
|
|
320
|
+
i = i + 1
|
|
321
|
+
while i < len(pt[0]):
|
|
322
|
+
i_0 = i_1
|
|
323
|
+
|
|
324
|
+
if abs(BCC_star[4][i] - BCC_star[2][i]) < ZERO or i == len(pt[0]) - 1:
|
|
325
|
+
i_1 = i
|
|
326
|
+
T_high = pt[0][i_0]
|
|
327
|
+
T_low = pt[0][i_1]
|
|
328
|
+
|
|
329
|
+
for s in z.hot_streams:
|
|
330
|
+
T_max = s.t_max_star
|
|
331
|
+
T_min = s.t_min_star
|
|
332
|
+
if (T_max > T_low + ZERO and T_max <= T_high + ZERO) or (T_min >= T_low - ZERO \
|
|
333
|
+
and T_min < T_high - ZERO) or (T_min < T_low - ZERO and T_max > T_high + ZERO):
|
|
334
|
+
Num_HX += 1
|
|
335
|
+
|
|
336
|
+
for s in z.cold_streams:
|
|
337
|
+
T_max = s.t_max_star
|
|
338
|
+
T_min = s.t_min_star
|
|
339
|
+
if (T_max > T_low + ZERO and T_max <= T_high + ZERO) or (T_min >= T_low - ZERO \
|
|
340
|
+
and T_min < T_high - ZERO) or (T_min < T_low - ZERO and T_max > T_high + ZERO):
|
|
341
|
+
Num_HX += 1
|
|
342
|
+
|
|
343
|
+
for utility_k in z.hot_utilities:
|
|
344
|
+
T_max = utility_k.t_max_star
|
|
345
|
+
T_min = utility_k.t_min_star
|
|
346
|
+
if (T_max > T_low + ZERO and T_max <= T_high + ZERO) or (T_min >= T_low - ZERO and T_min < T_high - ZERO):
|
|
347
|
+
Num_HX += 1
|
|
348
|
+
|
|
349
|
+
for utility_k in z.cold_utilities:
|
|
350
|
+
T_max = utility_k.t_max_star
|
|
351
|
+
T_min = utility_k.t_min_star
|
|
352
|
+
if (T_max > T_low + ZERO and T_max <= T_high + ZERO) or (T_min >= T_low - ZERO and T_min < T_high - ZERO):
|
|
353
|
+
Num_HX += 1
|
|
354
|
+
|
|
355
|
+
Num_HX -= 1
|
|
356
|
+
|
|
357
|
+
j = i_1
|
|
358
|
+
while j < len(pt[0]) - 1:
|
|
359
|
+
if abs(BCC_star[4][j + 1] - BCC_star[2][j + 1]) > ZERO:
|
|
360
|
+
break
|
|
361
|
+
j += 1
|
|
362
|
+
|
|
363
|
+
i = j
|
|
364
|
+
i_1 = j
|
|
365
|
+
|
|
366
|
+
i += 1
|
|
367
|
+
|
|
368
|
+
return Num_HX
|
|
369
|
+
|
|
370
|
+
# def Calc_ExGCC(z, GCC_Act):
|
|
371
|
+
# """Transposes a normal GCC (T-h) into a exergy GCC (Tx-X).
|
|
372
|
+
# """
|
|
373
|
+
# GCC_X = copy.deepcopy(GCC_Act)
|
|
374
|
+
# Min_X = 0
|
|
375
|
+
# AbovePT = True
|
|
376
|
+
# GCC_X[0][0] = compute_exergetic_temperature(GCC_Act[0][0] + z.config.DTCONT / 2, T_ref=z.config.TEMP_REF)
|
|
377
|
+
# GCC_X[1][0] = 0
|
|
378
|
+
# i_upper = len(GCC_X[0]) + 1
|
|
379
|
+
|
|
380
|
+
# # Transpose to exergetic temperature and exergy flow
|
|
381
|
+
# i = 1
|
|
382
|
+
# gcc_act_i = 1
|
|
383
|
+
# while i <= i_upper and gcc_act_i < len(GCC_Act[0]):
|
|
384
|
+
# if AbovePT:
|
|
385
|
+
# GCC_X[0][i] = compute_exergetic_temperature(GCC_Act[0][gcc_act_i] + z.config.DTCONT / 2, T_ref=z.config.TEMP_REF)
|
|
386
|
+
# GCC_X[1][i] = (GCC_Act[1][gcc_act_i - 1] - GCC_Act[1][gcc_act_i]) / (GCC_Act[0][gcc_act_i - 1] - GCC_Act[0][gcc_act_i])
|
|
387
|
+
# GCC_X[1][i] = GCC_X[1][i - 1] - GCC_X[1][i] * (GCC_X[0][i - 1] - GCC_X[0][i])
|
|
388
|
+
# if GCC_Act[1][gcc_act_i] < ZERO:
|
|
389
|
+
# Min_X = GCC_X[1][i]
|
|
390
|
+
# for row in GCC_X:
|
|
391
|
+
# row += [0, 0]
|
|
392
|
+
# i += 2
|
|
393
|
+
# gcc_act_i += 1
|
|
394
|
+
# GCC_X[0][i] = compute_exergetic_temperature(GCC_Act[0][gcc_act_i - 1] - z.config.DTCONT / 2, T_ref=z.config.TEMP_REF)
|
|
395
|
+
# GCC_X[1][i] = GCC_X[1][i - 1]
|
|
396
|
+
# AbovePT = False
|
|
397
|
+
# else:
|
|
398
|
+
# GCC_X[0][i] = compute_exergetic_temperature(GCC_Act[0][gcc_act_i - 1] - z.config.DTCONT / 2, T_ref=z.config.TEMP_REF)
|
|
399
|
+
# GCC_X[1][i] = (GCC_Act[1][gcc_act_i - 2] - GCC_Act[1][gcc_act_i - 1]) / (GCC_Act[0][gcc_act_i - 2] - GCC_Act[0][gcc_act_i - 1])
|
|
400
|
+
# GCC_X[1][i] = GCC_X[1][i - 1] - GCC_X[1][i] * (GCC_X[0][i - 1] - GCC_X[0][i])
|
|
401
|
+
# i += 1
|
|
402
|
+
# gcc_act_i += 1
|
|
403
|
+
|
|
404
|
+
# # Shift Exergy GCC appropriately
|
|
405
|
+
# for i in range(1, len(GCC_X[0])):
|
|
406
|
+
# GCC_X[1][i] = GCC_X[1][i] + abs(Min_X)
|
|
407
|
+
# if abs(GCC_X[1][i]) < ZERO:
|
|
408
|
+
# GCC_X[1][i] = 0
|
|
409
|
+
|
|
410
|
+
# return GCC_X
|
|
411
|
+
|
|
412
|
+
|
|
413
|
+
|
|
414
|
+
# def Calc_GCC_AI(z, pt_real, gcc_np):
|
|
415
|
+
# """Returns a simplified array for the assisted integration GCC.
|
|
416
|
+
# """
|
|
417
|
+
# GCC_AI = [ [ None for j in range(len(pt_real[0]))] for i in range(2)]
|
|
418
|
+
# for i in range(len(pt_real[0])):
|
|
419
|
+
# GCC_AI[0][i] = pt_real[0][i]
|
|
420
|
+
# GCC_AI[1][i] = pt_real[PT.H_NET.value][i] - gcc_np[1][i]
|
|
421
|
+
# return GCC_AI
|