Source code for optimeed.core.additional_tools

import math
import scipy.spatial.qhull as qhull
import numpy as np


[docs]class fast_LUT_interpolation: """Class designed for fast interpolation in look-up table when successive searchs are called often. Otherwise use griddata""" def __init__(self, independent_variables, dependent_variables): """ :param independent_variables: np.array of shape (N,d) containing the independent variables of the dataset to interpolate. Typically X for 1D LU, or XY for 2D LUT :param dependent_variables: np.array of shape (1,d) containing the dependet variables of the dataset to interpolate. Typically Y for 1D LU, or Z for 2D LUT """ self.param_ind = independent_variables self.param_dep = dependent_variables self.tri = self.interp_tri(independent_variables) self.dim = independent_variables.shape[1] @staticmethod
[docs] def interp_tri(xyz): tri = qhull.Delaunay(xyz) return tri
[docs] def interpolate(self, point, fill_value=np.nan): """ Perform the interpolation :param point: coordinates to interpolate (tuple or list of tuples for multipoints) :param fill_value: value to put if extrapolated. :return: coordinates """ d = self.dim tri = self.tri values = self.param_dep simplex = tri.find_simplex(point) vertices = np.take(tri.simplices, simplex, axis=0) temp = np.take(tri.transform, simplex, axis=0) delta = point - temp[:, d] bary = np.einsum('njk,nk->nj', temp[:, :d, :], delta) wts = np.hstack((bary, 1.0 - bary.sum(axis=1, keepdims=True))) ret = np.einsum('nj,nj->n', np.take(values, vertices), wts) ret[np.any(wts < 0, axis=1)] = fill_value return ret
[docs]def interpolate_table(x0, x_values, y_values): """From sorted table (x,y) find y0 corresponding to x0 (linear interpolation)""" index = np.searchsorted(x_values, x0) def _interpolate(x0, x_values, y_values, index): if index == len(x_values): return y_values[-1] if index == 0: return y_values[0] x_before, y_before = x_values[index-1], y_values[index-1] x_after, y_after = x_values[index], y_values[index] for x, y in [(x_before, y_before), (x_after, y_after)]: if x == x0: return y ratio = (x0 - x_before)/(x_after-x_before) return y_before + ratio*(y_after - y_before) if isinstance(index, np.ndarray): results = [0.0]*len(index) for k, ind in enumerate(index): results[k] = _interpolate(x0[k], x_values, y_values, ind) return results else: return _interpolate(x0, x_values, y_values, index)
[docs]def derivate(t, y): derivated = [0.0]*len(y) for i in range(1, len(y)-1): derivated[i] = (y[i+1] - y[i-1])/(t[i+1] - t[i-1]) derivated[-1] = (y[1] - y[-2])/((t[-1]-t[-2]) + (t[1]-t[0])) derivated[0] = derivated[-1] return derivated
[docs]def linspace(start, stop, npoints): return np.linspace(start, stop, npoints).tolist()
[docs]def reconstitute_signal(amplitudes, phases, numberOfPeriods=1, x_points=None, n_points=50): """Reconstitute the signal from fft. Number of periods of the signal must be specified if different of 1""" if x_points is None: x_points = np.linspace(0, 2*np.pi*numberOfPeriods, num=int(n_points)) n_points = len(x_points) y = np.zeros(n_points) for key in amplitudes: y += amplitudes[key] * np.cos(key/numberOfPeriods * x_points + phases[key]) return x_points, y
# def my_ifft(amplitude, angle, hasBeenTruncated=True): # """Inverse fft of signal. # If hasBeenTruncated (initial fft signal was full period (including first point of next period): # Axis vector must be multiplied by (n_points-1)/(n_points-2)""" # amplitudes = list(amplitude.values()) # angles = list(angle.values()) # n_points = (len(angles))*2 # normalization = 2/n_points # if hasBeenTruncated: # normalization *= (n_points+1)/(n_points-1) # coeffs = np.array([amplitudes[i] * np.exp(1j*angles[i]) for i in range(len(angles))]) # return np.fft.irfft(coeffs)/normalization
[docs]def my_fft(y): """Real FFT of signal Bx, with real amplitude of harmonics. Input signal must be within a period.""" if np.isclose(y[0], y[-1]): y = y[:-1] n_points = len(y) normalization = 2/n_points res = np.fft.rfft(y)*normalization res[0] /= 2 amplitudes = np.abs(res) phases = np.angle(res) max_amplitude = max(amplitudes[1:]) amplitude = dict() phase = dict() for harmonic in range(len(amplitudes)): if harmonic > 0 and amplitudes[harmonic] > 0.01*max_amplitude: amplitude[harmonic] = amplitudes[harmonic] phase[harmonic] = phases[harmonic] # harmonics = list(range(len(res))) # amplitude = dict(zip(harmonics, np.abs(res))) # phase = dict(zip(harmonics, np.angle(res))) return amplitude, phase
[docs]def cart2pol(x, y): rho = np.sqrt(x ** 2 + y ** 2) phi = np.arctan2(y, x) return rho, phi
[docs]def pol2cart(rho, phi): x = rho * np.cos(phi) y = rho * np.sin(phi) return x, y
[docs]def partition(array, begin, end): pivot = begin for i in range(begin+1, end+1): if array[i] <= array[begin]: pivot += 1 array[i], array[pivot] = array[pivot], array[i] array[pivot], array[begin] = array[begin], array[pivot] return pivot
[docs]def quicksort(array): end = len(array) - 1 def _quicksort(_array, _begin, _end): if _begin >= _end: return pivot = partition(_array, _begin, _end) _quicksort(_array, _begin, pivot-1) _quicksort(_array, pivot+1, _end) _quicksort(array, 0, end) return array
[docs]def dist(p, q): """Return the Euclidean distance between points p and q. :param p: [x, y] :param q: [x, y] :return: distance (float)""" return math.hypot(p[0] - q[0], p[1] - q[1])
[docs]def sparse_subset(points, r): """Returns a maximal list of elements of points such that no pairs of points in the result have distance less than r. :param points: list of tuples (x,y) :param r: distance :return: corresponding subset (list), indices of the subset (list)""" result = [] indices = [] for i, p in enumerate(points): if all(dist(p, q) >= r for q in result): result.append(p) indices.append(i) return result, indices
[docs]def integrate(x, y): """ Performs Integral(x[0] to x[-1]) of y dx :param x: x axis coordinates (list) :param y: y axis coordinates (list) :return: integral value """ integral = 0 for i in range(1, len(x)): dx = x[i]-x[i-1] integral += dx*(y[i] + y[i-1])/2 return integral
[docs]def my_fourier(x, y, n, L): """ Fourier analys :param x: x axis coordinates :param y: y axis coordinates :param n: number of considered harmonic :param L: half-period length :return: a and b coefficients (y = a*cos(x) + b*sin(y)) """ y_to_integrate_a = [None]*len(y) y_to_integrate_b = [None]*len(y) for i in range(len(y)): y_to_integrate_a[i] = y[i] * np.cos(n*np.pi*x[i] / L) y_to_integrate_b[i] = y[i] * np.sin(n*np.pi*x[i] / L) a_n = 1/L * integrate(x, y_to_integrate_a) b_n = 1/L * integrate(x, y_to_integrate_b) return a_n, b_n
[docs]def get_ellipse_axes(a, b, dphi): """Trouve les longueurs des axes majeurs et mineurs de l'ellipse, ainsi que l'orientation de l'ellipse. ellipse: x(t) = A*cos(t), y(t) = B*cos(t+dphi) Etapes: longueur demi ellipse CENTRéE = sqrt(a^2 cos^2(x) + b^2 cos^2(t+phi) Minimisation de cette formule => obtention formule tg(2x) = alpha/beta""" assert a >= 0, 'a must be positive' assert b >= 0, 'b must be positive' if np.isclose(dphi, 0): # Line if a == 0: phase = np.pi/2 else: phase = np.arctan(b/a) return np.sqrt(a**2 + b**2), 0.0, phase elif np.isclose(dphi, np.pi/2): # Properly oriented ellipse if a > b: return a, b, 0.0 return b, a, np.pi/2 sdphi = np.sin(dphi) cdphi = np.cos(dphi) alpha = (a**2)/2 + (b**2)/2*(cdphi**2 - sdphi**2) beta = -b**2*(sdphi*cdphi) t_optim_1, t_optim_2 = 0.5*(np.arctan(beta/alpha) + np.pi), 0.5*(np.arctan(beta/alpha) + 2*np.pi) max_length = np.sqrt((a * np.cos(t_optim_1))**2 + (b * np.cos(t_optim_1 + dphi))**2) min_length = np.sqrt((a * np.cos(t_optim_2))**2 + (b * np.cos(t_optim_2 + dphi))**2) # print(np.cos(t_optim_1 + dphi)/np.cos(t_optim_1), np.cos(t_optim_2 + dphi)/np.cos(t_optim_2)) if max_length > min_length: phase = np.arctan(b/a * np.cos(t_optim_1 + dphi)/np.cos(t_optim_1)) else: phase = np.arctan(b/a * np.cos(t_optim_2 + dphi)/np.cos(t_optim_2)) max_length, min_length = min_length, max_length return max_length, min_length, phase