# -*- coding: utf-8 -*-
# This file is part of pyChemEngg python package.
# PyChemEngg: A python-based framework to promote problem solving and critical
# thinking in chemical engineering.
# Copyright (c) 2021 Harvinder Singh Gill <profhsgill@gmail.com>
# 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:
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
# 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.
"""
Module for heat exchanger analysis.
"""
import math
from scipy.optimize import fsolve
__all__ = ["FCorrectionFactor", "EffNTU", "calc_overallheattransfercoefficient_fromNTU"]
[docs]class FCorrectionFactor:
r""" Contains functions to compute 'F' correction factor for LMTD
for heat exchangers.
Parameters
----------
`None_required` : 'None'
This class takes no parameters for instance creation.
Attributes
----------
`None_required` : 'None'
This class does not expose any instance attributes.
Examples
--------
First import the module **heatexchangers**.
>>> from pychemengg.heattransfer import heatexchangers as hx
>>> hx1 = hx.FCorrectionFactor
# This will assign the class 'FCorrectionFactor' to the
# variable 'hx1'.
# Methods of the class 'FCorrectionFactor' can then be called like so :-
# hx1.methodname(kwarg1=x, ... etc)
"""
[docs] def oneshell2ntubepasses(self, T_tubein=None, T_tubeout=None, T_shellin=None, T_shellout=None):
r""" To find 'F' correction factor for LMTD.
Use when shell passes = 1 and tube passes = 2, 4, 6, 8, etc
Parameters
----------
T_tubein : `int or float`
Temperature of tube side (or cold) fluid at inlet.
T_tubeout : `int or float`
Temperature of tube side (or cold) fluid at outlet.
T_shellin : `int or float`
Temperature of shell side (or hot) fluid at inlet.
T_shellout : `int or float`
Temperature of shell side (or hot) fluid at outlet.
Returns
-------
F : `int or float`
'F' correction factor for LMTD.
Raises
------
ValueError with message 'math domain error'
If negative number is being passed to the 'log' function
Notes
-----
Equation number (6) of reference [1] is used.
This is the same equation that is used to generate plots presented
in most textbooks.
Examples
--------
First import the module **heatexchangers**.
>>> from pychemengg.heattransfer import heatexchangers as hx
>>> hx1 = hx.FCorrectionFactor()
>>> hx1.oneshell2ntubepasses(T_shellin=300, T_shellout=200,
T_tubein=100, T_tubeout=200)
0.8022781617244771
References
----------
[1] R. A. Bowman, A. C. Mueller, and W. M. Nagle, "Mean Temperature
Difference in Design", Transactions of the ASME 62, 1940, pp:283-294. .
"""
try:
R, P = FCorrectionFactor.calc_R_P(self, T_tubein=T_tubein,
T_tubeout=T_tubeout,
T_shellin=T_shellin,
T_shellout=T_shellout)
ta = math.sqrt(R**2 + 1)
tb = 2/P - 1 - R
if R == 1:
tt = P / (math.log(10)*(1-P))
elif R != 1:
arg1 = (1-P) / (1-P*R)
tt = 1/(R-1) * math.log10(arg1)
tc = ta * tt
td = (tb + ta) / (tb - ta)
f1_2 = tc/math.log10(td)
return f1_2
except ValueError as err:
if str(err) == "math domain error":
print(err)
print("Most likely a negative number is being passed to the"
" 'log' function.")
print("A heat exchanger of this type cannot be designed"
" for this particular temperature combination")
[docs] def twoshell4ntubepasses(self, T_tubein=None, T_tubeout=None, T_shellin=None, T_shellout=None):
r"""To find 'F' correction factor for LMTD.
Use when shell passes = 2 and tube passes = 4, 8, 12, etc
Parameters
----------
T_tubein : `int or float`
Temperature of tube side (or cold) fluid at inlet.
T_tubeout : `int or float`
Temperature of tube side (or cold) fluid at outlet.
T_shellin : `int or float`
Temperature of shell side (or hot) fluid at inlet.
T_shellout : `int or float`
Temperature of shell side (or hot) fluid at outlet.
Returns
-------
F : `int or float`
'F' correction factor for LMTD.
Raises
------
ValueError with message 'math domain error'
If negative number is being passed to the 'log' function
Notes
-----
Equation number (8) of reference [1] is used.
This is the same equation used to generate plots presented
in most textbooks.
Examples
--------
First import the module **heatexchangers**.
>>> from pychemengg.heattransfer import heatexchangers as hx
>>> hx1 = hx.FCorrectionFactor()
>>> hx1.twoshell4ntubepasses(T_shellin=300, T_shellout=200,
T_tubein=100, T_tubeout=200)
0.9568453972970873
References
----------
[1] R. A. Bowman, A. C. Mueller, and W. M. Nagle, "Mean Temperature
Difference in Design", Transactions of the ASME 62, 1940, pp:283-294. .
"""
try:
R, P = FCorrectionFactor.calc_R_P(self, T_tubein=T_tubein,
T_tubeout=T_tubeout,
T_shellin=T_shellin,
T_shellout=T_shellout)
ta = math.sqrt(R**2 + 1)
tb = 2/P - 1 - R
tc = 2/P * math.sqrt((1-P) * (1-P*R))
if R == 1:
tt = P / (math.log(10)*(1-P))
tt = tt/2
elif R != 1:
arg1 = (1-P) / (1-P*R)
tt = 1/(R-1) * math.log10(arg1)
tt = tt/2
tf = ta * tt
td = (tb + tc + ta) / (tb + tc - ta)
f2_2 = tf/math.log10(td)
return f2_2
except ValueError as err:
if str(err) == "math domain error":
print(err)
print("Most likely a negative number is being passed to the"
" 'log' function.")
print("A heat exchanger of this type cannot be designed"
" for this particular temperature combination")
[docs] def calc_R_P(self, T_tubein=None, T_tubeout=None, T_shellin=None, T_shellout=None):
r"""To find 'R' and 'P' parameters needed to compute 'F' correction
factors. Could be useful to manually look up tables
of F correction factors in textbooks.
Parameters
----------
T_tubein : `int or float`
Temperature of tube side (or cold) fluid at inlet.
T_tubeout : `int or float`
Temperature of tube side (or cold) fluid at outlet.
T_shellin : `int or float`
Temperature of shell side (or hot) fluid at inlet.
T_shellout : `int or float`
Temperature of shell side (or hot) fluid at outlet.
Returns
-------
tuple containg R-parameter and P-parameter :
(R-parameter : `int or float`, P-parameter : `int or float`)
Notes
-----
The following formulas defined in reference [1] are used:
.. math::
R = \frac {T_{shellin} - T_{shellout}} {T_{tubeout} - T_{tubein}}
P = \frac {T_{tubeout} - T_{tubein}} {T_{shellin} - T_{tubein}}
Examples
--------
First import the module **heatexchangers**.
>>> from pychemengg.heattransfer import heatexchangers as hx
>>> hx1 = hx.FCorrectionFactor()
>>> hx1.calc_R_P(T_shellin=300, T_shellout=200,
T_tubein=100, T_tubeout=200)
(1.0, 0.5)
References
----------
[1] R. A. Bowman, A. C. Mueller, and W. M. Nagle, "Mean Temperature
Difference in Design", Transactions of the ASME 62, 1940, pp:283-294. .
"""
R = (T_shellin - T_shellout) / (T_tubeout - T_tubein)
P = (T_tubeout - T_tubein) / (T_shellin - T_tubein)
return R, P
[docs] def singlepass_crossflow_bothunmixed(self, T_tubein=None, T_tubeout=None,
T_shellin=None, T_shellout=None):
r"""To find 'F' correction factor for LMTD.
Use for single pass cross flow with both fluids unmixed.
Parameters
----------
T_tubein : `int or float`
Temperature of tube side (or cold) fluid at inlet.
T_tubeout : `int or float`
Temperature of tube side (or cold) fluid at outlet.
T_shellin : `int or float`
Temperature of shell side (or hot) fluid at inlet.
T_shellout : `int or float`
Temperature of shell side (or hot) fluid at outlet.
Returns
-------
F : `int or float`
'F' correction factor for LMTD.
Notes
-----
Equation number (10) of reference [1] is used.
This is the same equation that is used to generate plots presented
in most textbooks.
Examples
--------
First import the module **heatexchangers**.
>>> from pychemengg.heattransfer import heatexchangers as hx
>>> hx1 = hx.FCorrectionFactor()
>>> hx1.singlepass_crossflow_bothunmixed(T_shellin=300, T_shellout=200,
T_tubein=100, T_tubeout=200)
0.8945911509910063
References
----------
[1] R. A. Bowman, A. C. Mueller, and W. M. Nagle, "Mean Temperature
Difference in Design", Transactions of the ASME 62, 1940, pp:283-294. .
"""
try:
T1 = T_shellin
T2 = T_shellout
t1 = T_tubein
t2 = T_tubeout
p = (T1-T2)/(T1-t1)
q = (t2-t1)/(T1-t1)
if p!=q:
r0 = (p-q)/math.log((1-q)/(1-p)) # r0 stand for countercurrent configuration
if p==q: # i.e. R = 1
r0 = (T1-t2)/(T1-t1) # r0 stand for countercurrent configuration
# when R=1 , deltaTm = LMTD = T1-t2
# so this term is in numerator
def find_r(r): # this is equation 10 from Bowman, 1940
n = 40
summation = 0
fact = math.factorial
for u in range(n):
for v in range(n):
term = (-1)**(u+v) * fact(u+v)/fact(u)/fact(v)/fact(u+1)/fact(v+1)
term = term * math.pow(p/r, u) * math.pow(q/r, v)
summation = summation + term
return r-summation
ans = fsolve(find_r, 0.5)
F = ans[0]/r0
# print("p= ",p, " q= ", q, " F= ", F)
return F
except ValueError as err:
if str(err) == "math domain error":
print(err)
print("Most likely a negative number is being passed to the"
" 'log' function.")
print("A heat exchanger of this type cannot be designed"
" for this particular temperature combination")
[docs] def singlepass_crossflow_oneunmixed(self, T_tubein=None, T_tubeout=None,
T_shellin=None, T_shellout=None):
r"""To find 'F' correction factor for LMTD.
Use for single pass cross flow with one fluid unmixed.
Parameters
----------
T_tubein : `int or float`
Temperature of tube side (or cold) fluid at inlet.
T_tubeout : `int or float`
Temperature of tube side (or cold) fluid at outlet.
T_shellin : `int or float`
Temperature of shell side (or hot) fluid at inlet.
T_shellout : `int or float`
Temperature of shell side (or hot) fluid at outlet.
Returns
-------
F : `int or float`
'F' correction factor for LMTD.
Notes
-----
Equation number (11) of reference [1] is used.
This is the same equation that is used to generate plots presented
in most textbooks.
Examples
--------
First import the module **heatexchangers**.
>>> from pychemengg.heattransfer import heatexchangers as hx
>>> hx1 = hx.FCorrectionFactor()
>>> hx1.singlepass_crossflow_oneunmixed(T_shellin=300, T_shellout=200,
T_tubein=100, T_tubeout=200)
0.8464626304853572
References
----------
[1] R. A. Bowman, A. C. Mueller, and W. M. Nagle, "Mean Temperature
Difference in Design", Transactions of the ASME 62, 1940, pp:283-294. .
"""
# this is equation 11 from Bowman, 1940
try:
T1 = T_shellin
T2 = T_shellout
t1 = T_tubein
t2 = T_tubeout
P = (T1-T2)/(T1-t1)
Q = (t2-t1)/(T1-t1)
if P!=Q:
r0 = (P-Q)/math.log((1-Q)/(1-P))
if P==Q:
r0 = (T1-t2)/(T1-t1)
b = 1-(Q/P*math.log(1/(1-P)))
R = Q/math.log(1/(b))
F = R/r0
return F
except ValueError as err:
if str(err) == "math domain error":
print(err)
print("Most likely a negative number is being passed to the"
" 'log' function.")
print("A heat exchanger of this type cannot be designed"
" for this particular temperature combination")
[docs] def singlepass_crossflow_bothmixed(self, T_tubein=None, T_tubeout=None,
T_shellin=None, T_shellout=None):
r"""To find 'F' correction factor for LMTD.
Use for single pass cross flow with both fluids mixed.
Parameters
----------
T_tubein : `int or float`
Temperature of tube side (or cold) fluid at inlet.
T_tubeout : `int or float`
Temperature of tube side (or cold) fluid at outlet.
T_shellin : `int or float`
Temperature of shell side (or hot) fluid at inlet.
T_shellout : `int or float`
Temperature of shell side (or hot) fluid at outlet.
Returns
-------
F : `int or float`
'F' correction factor for LMTD.
Notes
-----
Equation number (12) of reference [1] is used.
This is the same equation that is used to generate plots presented
in most textbooks.
Examples
--------
First import the module **heatexchangers**.
>>> from pychemengg.heattransfer import heatexchangers as hx
>>> hx1 = hx.FCorrectionFactor()
>>> hx1.singlepass_crossflow_bothmixed(T_shellin=300, T_shellout=200,
T_tubein=100, T_tubeout=200)
0.7959050946318332
References
----------
[1] R. A. Bowman, A. C. Mueller, and W. M. Nagle, "Mean Temperature
Difference in Design", Transactions of the ASME 62, 1940, pp:283-294. .
"""
# this is equation 12 from Bowman, 1940
try:
T1 = T_shellin
T2 = T_shellout
t1 = T_tubein
t2 = T_tubeout
P = (T1-T2)/(T1-t1)
Q = (t2-t1)/(T1-t1)
if P!=Q:
r0 = (P-Q)/math.log((1-Q)/(1-P))
if P==Q:
r0 = (T1-t2)/(T1-t1)
def findR(R): # this is equation 10 from Bowman, 1940
ta = (P/R)/(1-math.exp(-P/R))
tb = (Q/R)/(1-math.exp(-Q/R))
return R*(ta + tb - 1) - 1
ans = fsolve(findR, 0.5)
F = ans[0]/r0
return F
except ValueError as err:
if str(err) == "math domain error":
print(err)
print("Most likely a negative number is being passed to the"
" 'log' function.")
print("A heat exchanger of this type cannot be designed"
" for this particular temperature combination")
############################
# Following functions need more work before adding to API
############################
def _find_P_equivalent (self, x, R=None, P=None, shellpass_count=None):
"""
VERIFY AGAIN BEFORE ADDING TO PUBLIC API
"""
n = shellpass_count
if R != 1:
term1 = ((1-x*R) / (1-x))**n
Pnew = (1-term1) / (R-term1)
return Pnew-P
if R == 1:
Pnew = (x * n) / (x*n - x + 1)
return Pnew-P
def _nshell2ntube(self, T_tubein=None, T_tubeout=None, T_shellin=None,
T_shellout=None, shellpass_count=None):
""" Use when shell passes >= 3
and tube passes = even multiples of shell pass
example : 3 shell - 6, 12, 18 ... tube passes
4 shell - 8, 12, 16 ... tube passes
... and so on
VERIFY AGAIN BEFORE ADDING TO PUBLIC API
"""
try:
n = shellpass_count
R, P = FCorrectionFactor.calc_R_P(self, T_tubein=T_tubein,
T_tubeout=T_tubeout,
T_shellin=T_shellin,
T_shellout=T_shellout)
# First we convert P for the 'n' shell '2N' tube exchanger
# into 'P' that is equivalent to '1' shell '2' tube exchanger
psolved = fsolve(lambda x: FCorrectionFactor._find_P_equivalent(x, R=R, P=P, shellpass_count=n), 0.5)
pequivalent = psolved[0]
print("equivalent P =", pequivalent)
# Now we take this P-equivalent and use 1 shell - 2 tube formula to find 'F'
# To do this we find equivalent T_tubein and T_tubeout values that will correspond
# to the exchanger with equivalent P
# For this we use R, pequivalent, T_shellin and T_shellout and find equivalent T_tubein and equivalent T_tubeout
equivalenttemps = FCorrectionFactor._get_t1_t2_for_given_P_R (T1=T_shellin, T2=T_shellout, P=pequivalent, R=R)
t1equivalent = equivalenttemps[2]
t2equivalent = equivalenttemps[3]
# Now cal 1-2 exachanger with these equivalent temps
fn_2n = FCorrectionFactor.oneshell2ntubepasses(T_tubein=t1equivalent,
T_tubeout=t2equivalent,
T_shellin=T_shellin,
T_shellout=T_shellout)
return fn_2n
except:
return "Impossible"
def _oneshell3tube(self, T_tubein=None, T_tubeout=None, T_shellin=None, T_shellout=None):
"""Based on equation by Fischer
"Mean Temperature Difference Correction in Multipass Exchangers
Industrial and Engineering Chemistry, 1938
vol 30, No 4, page 377-383
VERIFY AGAIN BEFORE ADDING TO PUBLIC API
"""
try:
R, P = FCorrectionFactor.calc_R_P(self, T_tubein=T_tubein,
T_tubeout=T_tubeout,
T_shellin=T_shellin,
T_shellout=T_shellout)
R1 = 1/R
T1 = T_shellin
T2 = T_shellout
t1 = T_tubein
t2 = T_tubeout
if R1 == 1:
LMTD_countercurrent = abs(T1-t2)
if R1 !=1:
LMTD_countercurrent = ((T1-t2)-(T2-t1)) / math.log((T1-t2)/(T2-t1))
def solvefor_thetam(thetaM):
# thetaM is the true mean temperature from which 'F' can be computed
# F = thetaM / LMTD_countercurrent
if R1 != 1:
lambda_1 = math.sqrt(9 - 4*R1*(1-R1))
m1L =1/2 * (3+lambda_1) * (T1-T2) / (3*thetaM)
m2L = 1/2 * (3-lambda_1) * (T1-T2) / (3*thetaM)
denom_1 = math.exp(m1L) - math.exp(m2L)
A = ((T1-t2)- math.exp(m2L)*(T2-t1)) / denom_1/(1-R1)
B = (math.exp(m1L)*(T2-t1) - (T1-t2)) / denom_1/(1-R1)
LHS = A*m1L + B*m2L
RHS_1 = (T1-T2)/thetaM * (T2 - t1 + math.exp((t2-t1)/3/thetaM) * (T1-t2))
RHS_2 = math.exp((t2-t1)/3/thetaM) * (A*m1L*math.exp(m1L) + B*m2L*math.exp(m2L))
RHS = RHS_1 - RHS_2
return LHS-RHS
if R1 == 1:
# This case was derived by Prof. Harvinder Singh Gill
# Equation 12 in the paper by Fischer was adapted by putting R1=1
# and the entire solution was reworked for this case
# Fischer provided data for R1=1 in the form of a Table (Table - I)
# However, Fischer did not show the work nor the equation that
# he had used to generate Table I for the case R = 1
bLa = 1/9 * (t2-t1) * (t2-T1) / thetaM
eal = math.exp((T1-T2)/thetaM)
B = (T1-T2+bLa) / (eal-1)
A = T2-B
termA = math.exp((t2-t1)/3/thetaM) * (3*t2 - 3*T1 - (t2-T1)/3 + 3*B*eal)
LHS = (T1-T2) * (3*T2 - 3*t1 - termA)
RHS = 3*(T1-T2)*B - 1/3*(t2-t1)*(t2-T1)
return LHS-RHS
ans = fsolve(solvefor_thetam, LMTD_countercurrent)
F = ans[0]/LMTD_countercurrent
return F
except:
return "Impossible"
def _nshell3ntube(self, T_tubein=None, T_tubeout=None, T_shellin=None,
T_shellout=None, shellpass_count=None):
""" Use when shell passes >= 2
and tube passes = multiples of '3' of shell pass
example : 2 shell-6 tube,
3 shell-9 tube,
4 shell-12 tube passes
... and so on
VERIFY AGAIN BEFORE ADDING TO PUBLIC API
"""
try:
n = shellpass_count
R, P = FCorrectionFactor.calc_R_P(self, T_tubein=T_tubein, T_tubeout=T_tubeout, T_shellin=T_shellin, T_shellout=T_shellout)
R1 = 1/R
T1 = T_shellin
T2 = T_shellout
t1 = T_tubein
t2 = T_tubeout
# First we convert P for the 'n' shell '3N' tube exchanger
# into 'P' that is equivalent to '1' shell '3' tube exchanger
psolved = fsolve(FCorrectionFactor._find_P_equivalent, 0.5, R, P, n)
pequivalent = psolved[0]
print("equivalent P =", pequivalent)
# Now we take this P-equivalent and use 1 shell - 3 tube formula to find 'F'
# To do this we find equivalent T_tubein and T_tubeout values that will correspond
# to the exchanger with equivalent P
# For this we use R, pequivalent, T_shellin and T_shellout and find equivalent T_tubein and equivalent T_tubeout
equivalenttemps = FCorrectionFactor._get_t1_t2_for_given_P_R (T1=T_shellin, T2=T_shellout, P=pequivalent, R=R)
t1equivalent = equivalenttemps[2]
t2equivalent = equivalenttemps[3]
# Now cal 1-3 exachanger with these equivalent temps
fn_2n = FCorrectionFactor._oneshell3tube(T_tubein=t1equivalent, T_tubeout=t2equivalent , T_shellin=T_shellin, T_shellout=T_shellout)
return fn_2n
except:
return "Impossible"
def _get_t1_t2_for_given_P_R (self, T1=1, T2=1, P=1, R=1):
t1 = T1-(T1-T2)/P/R
t2 = P*(T1-t1)+t1
Rcalc = (T1-T2)/(t2-t1)
Pcalc = (t2-t1)/(T1-t1)
return T1,T2,t1,t2,Rcalc,Pcalc
def _twopass_crossflow_shellmixed_tubeunmixed(self, T_tubein=None, T_tubeout=None,
T_shellin=None, T_shellout=None):
# this is equation 13 from Bowman, 1940
T1 = T_shellin
T2 = T_shellout
t1 = T_tubein
t2 = T_tubeout
P = (T1-T2)/(T1-t1)
Q = (t2-t1)/(T1-t1)
if P!=Q:
r0 = (P-Q)/math.log((1-Q)/(1-P))
if P==Q:
r0 = (T1-t2)/(T1-t1)
ta = math.sqrt((1-Q)/(1-P))
qOverP = Q/P
tb = 1 - qOverP * math.log((ta-qOverP)/(1-qOverP))
R = Q/(2* math.log((1)/(tb)))
F = R/r0
return F
[docs]class EffNTU(object):
r""" Contains functions to compute 'effectiveness'
and NTU for heat exchangers.
Parameters
----------
Cmin : `int or float`
Minimum heat capacity rate of fluids in heat exchanger.
:math:`C_{min} = \dot{m} c_p` = smaller of the two heat
capacity rates for the two fluids in the heat exchanger.
:math:`\dot{m}` = mass flow rate of the fluid
:math:`c_p` = specific heat of the fluid
Cmax : `int or float`
Maximum heat capacity rate of fluids in heat exchanger.
:math:`C_{max} = \dot{m} c_p` = Larger of the two heat l
capacity rates for the two fluids in the heat exchanger
:math:`\dot{m}` = mass flow rate of the fluid
:math:`c_p` = specific heat of the fluid
Cratio : `int or float`
Cmin/Cmax
effectiveness : `int or float or str`
Efficiency of the heat exchanger.
Input 'int or float' value if effectiveness values is known, or
input "?" if it has to be computed. Default = "?".
NTU : `int or float or str`
Number of transfer units for the heat exchanger.
Input 'int or float' value if NTU values is known, or
input "?" if it has to be computed. Default = "?".
Notes
-----
1. At least one of effectiveness or NTU must be entered
as 'int or float' and the other will get computed.
2. Textbooks contain explicit formulas to compute
i) effectiveness and ii) NTU for different exchanger types.
3. In this 'class' definition, the following exchanger types
can be computed with respect to 'effectiveness' or 'NTU''
- Double pipe - parallel flow
- Double pipe - counter flow
- Shell and tube
* one shell pass and 2, 4, 6 etc tube passes
* n shell passes and 2n, 4n, etc tube passes
- Single pass counter flow:
* both fluids unmixed
* Cmax fluid mixed, and Cmin unmixed
* Cmin fluid mixed, and Cmax unmixed
- All exhangers when Cratio = Cmin/Cmax = 0
4. Logic:
Explicit formula for computing 'effectiveness' for each of the above
exchanger types is implemented in a separate method.
When that method is called, it internally calls another
method ("_get_eff_or_ntu"). This method checks which of
"effectiveness" or "NTU" is missing. If "effectiveness" is
missing, and "NTU" is provided, the explict equation is used
to compute effectiveness from "NTU". If "NTU" is missing, the same
explict equation for effectiveness becomes an implicit equation for "NTU",
and it is solved using 'fsolve' of scipy to find "NTU". A
seperate explict equation for calculating "NTU" from "effectiveness"
is not implemented. The advantage of this approach is that for the
case of "Single pass counter flow with both fluid unmixed", there
is no explict relationship for "NTU" reported in textbooks, but
there is one for computing "effectiveness". So in this manner by using
'fsolve', the "NTU"" for the case of "Single pass counter flow
with both fluid unmixed" can also be computed.
Attributes
----------
See "Parameters". All parameters are attributes.
Examples
--------
First import the module **heatexchangers**.
>>> from pychemengg.heattransfer import heatexchangers as hx
>>> hx1 = hx.EffNTU(Cmin=0.639, Cmax=0.836, NTU=0.854)
# This will create an instance of EffNTU and assign it to "hx1".
# Methods of the class 'EffNTU' can then be called.
# In this particular example, NTU is assigned = 0.854.
# By default effectiveness = "?".
# When a heat exchanger type is next called, it will automatically
# compute the effectiveness for that exchanger type
# For example
>>> hx1.shelltube(shellpass_count=1, tubepass_count=8)
0.4621565341983249
# This is the effectiveness.
"""
[docs] def __init__(self, Cmin=None, Cmax=None, NTU="?", effectiveness="?"):
# C: heat capacity rate = specificheat * massrate
self.Cmin = Cmin
self.Cmax = Cmax
self.Cratio = Cmin/Cmax
self.effectiveness = effectiveness
self.NTU = NTU
def _get_eff_or_ntu(self, func):
r""" Determines which of "effectiveness" or "NTU" is provided and
computes the unknown quantity.
Parameters
----------
func : `function object`
function of the form: effectiveness = func(NTU)
Returns
-------
effectiveness or NTU of heat exchanger : both as `int or float`
Notes
-----
1. Function - 'func' is passed to this method.
2. A check is made whether "effectiveness" or "NTU" is unknown.
3. If "effectiveness" is unknown, the function object, "func"
is used to compute "effectiveness" from known "NTU" because
func is explicit for "effectiveness" in the form :
effectiveness = func(NTU).
4. If "NTU" is unknown, the same function object, "func" is
used to solve for "NTU" with known effectiveness by setting
the equation: func(x) - effectiveness = 0.
"""
if self.effectiveness == "?" and self.NTU != "?":
# Case: effectiveness is unknown but NTU is known
self.effectiveness = func(self.NTU) # computes effectiveness from func
return self.effectiveness
if self.NTU == "?" and self.effectiveness != "?":
# Case: NTU is unknown but effectiveness is known
guess = 1
NTUValue = fsolve(lambda x: func(x) - self.effectiveness, guess)
# solve for NTU using fsolve solver of scipy
self.NTU = NTUValue[0]
return self.NTU
[docs] def doublepipe_parallelflow(self):
r""" Computes "effectiveness" or "NTU" for double pipe parallel flow
heat exchanger.
"""
def effectiveness(NTU):
num = (1 - math.exp( - NTU*(1 + self.Cratio)))
denom = 1 + self.Cratio
effvalue = num/denom
return effvalue
return self._get_eff_or_ntu(effectiveness)
[docs] def doublepipe_counterflow(self):
r""" Computes "effectiveness" or "NTU" for double pipe counter flow
heat exchanger.
"""
def effectiveness(NTU):
if self.Cratio < 1:
num = (1 - math.exp( - NTU*(1 - self.Cratio)))
denom = (1 - self.Cratio*math.exp( - NTU*(1 - self.Cratio)))
effvalue = num/denom
return effvalue
elif self.Cratio ==1:
effvalue = NTU/(1+self.NTU)
return effvalue
return self._get_eff_or_ntu(effectiveness)
[docs] def shelltube(self, shellpass_count = None, tubepass_count = None):
r""" Computes "effectiveness" or "NTU" for shell and tube
heat exchanger.
"""
def effectiveness(NTU):
if tubepass_count%2 == 0:
self.shellpass_count = shellpass_count
self.tubepass_count = tubepass_count
term1 = math.sqrt(1 + self.Cratio**2)
term2 = math.exp(- NTU * term1)
b = 2 * (1 + self.Cratio + term1 * (1+term2) / (1-term2))**(-1)
if self.shellpass_count == 1:
effvalue = b
return effvalue
if self.shellpass_count > 1:
n = self.shellpass_count
if (self.tubepass_count%self.shellpass_count)%2 == 0:
c = (1 - b*self.Cratio)/(1 - b)
d = (c**n - 1)/(c**n - self.Cratio)
effvalue = d
return effvalue
else:
print("Tube pass must be a multiple of shell pass")
print("Tube passes must be in multiples of '2'")
return None
else:
print("Tube passes must be in multiples of '2'")
return None
return self._get_eff_or_ntu(effectiveness)
[docs] def crossflow_bothfluids_unmixed(self):
r""" Computes "effectiveness" or "NTU" for cross flow heat exchanger
with both fluid unmixed.
"""
def effectiveness(NTU):
effvalue = 1-math.exp((1/self.Cratio)
* NTU**0.22
* (math.exp(- self.Cratio * NTU**0.78)-1))
return effvalue
return self._get_eff_or_ntu(effectiveness)
[docs] def crossflow_Cmin_unmixed(self):
r""" Computes "effectiveness" or "NTU" for cross flow heat exchanger
with both fluid with Cmin unmixed.
"""
def effectiveness(NTU):
effvalue = (1/self.Cratio) * ( 1-math.exp(- self.Cratio*(1-math.exp(-NTU))))
return effvalue
return self._get_eff_or_ntu(effectiveness)
[docs] def crossflow_Cmax_unmixed(self):
r""" Computes "effectiveness" or "NTU" for cross flow heat exchanger
with both fluid with Cmax unmixed.
"""
def effectiveness(NTU):
effvalue = 1-math.exp(- 1/self.Cratio*(1-math.exp(-self.Cratio*NTU)))
return effvalue
return self._get_eff_or_ntu(effectiveness)
[docs] def Cratio_is_zero(self):
r""" Computes "effectiveness" or "NTU" for any exchanger with
Cmin/Cmax = 0
"""
def effectiveness(NTU):
effvalue = 1-math.exp(-NTU)
return effvalue
return self._get_eff_or_ntu(effectiveness)
[docs]def calc_overallheattransfercoefficient_fromNTU(NTU=None, area=None, Cmin=None):
r"""To compute overall heat transfer coefficient (U) from NTU
Parameters
----------
NTU : `int or float`
Number of transfer units of heat exchanger.
area : `int or float`
Surface area of heat transfer for exchanger.
Cmin: `int or float`
Minimum heat capacity rate.
Returns
-------
Overall heat transfer coefficient (U) : `int or float`
Notes
-----
The following formula is used.
.. math::
U = \frac {NTU \hspace{2pt} C_{min}} {A_{surfacearea}}
*where:*
NTU = number of transfer units for the heat exchanger
:math:`C_{min} = \dot{m} c_p` = smaller of the two
capacity rates for the two fluids in the heat exchanger
:math:`\dot{m}` = mass flow rate of the fluid
:math:`c_p` = specific heat of the fluid
:math:`A_{surfacearea}` : Heat transfer surface area of the
exchanger
U = overall heat transfer coefficient
See Also
--------
pychemengg.heattransfer.heatcommonmethods.calc_overallheattransfercoefficient
Examples
--------
First import the module **heatexchangers**.
>>> from pychemengg.heattransfer import heatexchangers as hx
>>> hx.calc_overallheattransfercoefficient_fromNTU(NTU=0.651, Cmin=5.02e3, area=5.11)
639.5342465753424
References
----------
[1] Yunus A. Cengel and Afshin J. Ghajar, "Heat And Mass Transfer
Fundamentals and Applications", 6th Edition. New York, McGraw Hill
Education, 2020.
"""
return NTU * Cmin / area