# -*- coding: utf-8 -*-
"""
find the straight line which just touches a curve, while having the minimum
area between the curves.
2024-02-11
part of carbatpy
@author: atakan
"""
import numpy as np
import matplotlib.pyplot as plt
from scipy.optimize import minimize
[docs]def straight_diff(param, values, below=True):
"""
Calculating the integral between a straight line and points from a curve
the line is defined by the param values [slope,intercept], values[0,:] are
the x values, values[1,:] are the y-values. below decides, whether the
straight line should be below or not. This function is used for
minimization.
If the curves cross each other or if the line is on the wrong side,
a large value is returned.
Parameters
----------
param : list of float
[slope,intercept].
values : numpy.array [2,n_points]
values[0,:] are the x values, values[1,:] are the y-values.
below : boolean, optional
is the straight line below. The default is True.
Returns
-------
float
integral of the difference between the curves.
"""
difference = values[1, :] - (param[0] * values[0, :] + param[1])
positive = np.any(difference > 0)
negative = np.any(difference < 0)
crossing = (positive > 0 and negative > 0)
wrong_side = (positive > 0 and not below) or (negative > 0 and below)
penalize = np.abs(100 * (values[1, :].max() - values[1, :].min())
* (values[0, -1] - values[0, 0]))
if crossing or wrong_side:
return penalize
if below and positive > 0:
integral = np.trapz(difference, values[0])
else:
integral = np.trapz(-difference, values[0])
return integral
[docs]def find_min_approach(values_in, below=True, delta=0):
"""
Finds a straight line which approaches a curve, without crossing.
Mainly for finding the minimum approach of a fluid with phase change
to a fluid with a constant heat capacity along heat transfer. One can
select, whether the straight line should be *below* the curve or above.
Parameters
----------
values : numpy array [2,n]
[0,n] are the x-values, [1,n] are the y-values.
below : boolean, optional
should the straight line be below? The default is True.
delta : float, optional
wanted least distance between curves (absolute value.)
Returns
-------
output : dictionary
keys are "success", "integral": the integral between curve and line,
"line_par": slope and intercept of the line (np.array),"below":as above,
"message": result.message from minimize}.
"""
values = np.copy(values_in)
factor = 1
if below == True:
factor = -1
values[1] = values[1] + factor * delta
para0 = np.polyfit(values[0], values[1], 1)
min_val = values[1].min()
max_val = values[1].max()
diff_val = max_val - min_val
if below:
para0[1] -= diff_val
else:
para0[1] += diff_val
result = minimize(straight_diff, para0, args=(values, below),
method='Nelder-Mead', tol=1e-4,
options={'maxiter': 1e3})
difference = (values[1] - np.polyval(result.x, values[0])).mean()
output = {"success": result.success,
"integral": result.fun,
"line_par": result.x,
"below": below,
"message": result.message,
"mean_difference": difference - factor * delta}
return output
[docs]def diff_mean(high, low, dist_min=0., optimize=False):
"""
calculate the mean distance between two equally-spaced arrays
In thermodynamics/heat exchangers for minimum approach temperature. The size
of both arrays must be the same. If the curves cross, success will be False!
Parameters
----------
high : numpy array (length m)
the high values.
low : numpy array(length m)
the low values.
dist_min : float, optional
minimum required distnce, must be positive. The default is 0..
optimize : boolean, optional
if used for optimization. The default is False.
Returns
-------
dictionary or float
for optimization the mean difference is returned, else a dictionary
with success, mean-difference, all differences, dist_min.
"""
success = True
if len(high) != len(low):
print("wrong array lengths")
return 1e9
difference = high - low
mean_diff = difference.mean()
problem = np.any(difference < dist_min)
if problem > 0:
success = False
if optimize:
if success:
return mean_diff
else:
return (high.max() - low.min()) * 5
else:
return {"success": success,
"mean_difference": mean_diff,
"dist_min": dist_min,
"all_differences": difference}
if __name__ == "__main__":
# generating test points
[docs] x_val = np.linspace(1, 12)
y_val = -5 * np.sin(x_val) + 5 * x_val + 4 - 100 / x_val
values_act = np.array([x_val, y_val]) # input for find_min_approach
lower = np.polyval([10, -120], values_act[0])
delta = 5
###############################
# for arbitrary arays, comparison
mean_difference = diff_mean(values_act[1], lower, 14)
print(mean_difference)
###############################
# finding an optimal straigh line
solution_below = find_min_approach(values_act, True, delta)
solution_above = find_min_approach(values_act, False, delta)
# Plotting ####################
figure, axis = plt.subplots(1, 1)
axis.plot(values_act[0], values_act[1], "ko")
axis.plot(values_act[0], lower,"g:")
axis.plot(values_act[0], np.polyval(
solution_below["line_par"], values_act[0]), "b")
axis.plot(values_act[0], np.polyval(
solution_above["line_par"], values_act[0]), "r")