Source code for carbatpy.utils.curve_min_distance_finder

# -*- 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")