def calculate_transients(self): self.optimal_pitching_profile_rad = self.opti.value( self.angles_of_attack) self.optimal_pitching_profile_deg = np.rad2deg( self.optimal_pitching_profile_rad) self.optimal_lift_history = self.opti.value(self.lift_coefficients) self.pitching_lift = np.zeros(self.timesteps - 1) # Calculate unsteady lift due to pitching wagner = wagners_function(self.reduced_time) ds = self.reduced_time[1:] - self.reduced_time[:-1] da_ds = (self.optimal_pitching_profile_rad[1:] - self.optimal_pitching_profile_rad[:-1]) / ds init_term = self.optimal_pitching_profile_rad[0] * wagner[:-1] for i in range(self.timesteps - 1): integral_term = np.sum(da_ds[j] * wagner[i - j] * ds[j] for j in range(i)) self.pitching_lift[i] = 2 * np.pi * (integral_term + init_term[i]) self.gust_lift = np.zeros(self.timesteps - 1) # Calculate unsteady lift due to transverse gust kussner = kussners_function(self.reduced_time) dw_ds = (self.gust_profile[1:] - self.gust_profile[:-1]) / ds init_term = self.gust_profile[0] * kussner for i in range(self.timesteps - 1): integral_term = 0 for j in range(i): integral_term += dw_ds[j] * kussner[i - j] * ds[j] self.gust_lift[i] += 2 * np.pi / self.velocity * (init_term[i] + integral_term) # Calculate unsteady lift due to added mass self.added_mass_lift = np.pi / 2 * np.cos( self.optimal_pitching_profile_rad[:-1])**2 * da_ds
def _setup_unknowns(self): for airfoil in self.airfoils: airfoil.gamma = self.opti.variable( init_guess=0, scale=self.op_point.velocity, n_vars=airfoil.n_points() ) airfoil.sigma = np.zeros(airfoil.n_points())
def guess(eta): opti = asb.Opti() coeffs = opti.variable(init_guess=np.zeros(degree + 1)) y_model = model(coeffs) error = loss(y_model, y_data) opti.minimize(error + eta * (degree + 1)) sol = opti.solve(verbose=False) return sol.value(error) + eta * (degree + 1)
def obj(n): ''' minimizes objective (loss+regulaization) given n and eta n contains values (0 or 1) for each variable ''' opti = asb.Opti() coeffs = opti.variable(init_guess=np.zeros(degree + 1)) y_model = model(coeffs * n) error = loss(y_model, y_data) opti.minimize(error + eta * np.sum(n)) sol = opti.solve(verbose=False) return sol.value(error) + eta * np.sum(n), sol.value(coeffs)
def lower_bound(n): ''' finds a lower bound for instance n ''' opti = asb.Opti() coeffs = opti.variable(init_guess=np.zeros(degree + 1)) n_new = np.where(n == None, 1, n) n_new = np.array(n_new) y_model = model(coeffs * n_new) error = loss(y_model, y_data) opti.minimize(error + eta * np.sum(np.where(n == None, 0, n))) sol = opti.solve(verbose=False) return sol.value(error) + eta * np.sum(np.where(n == None, 0, n))
from data import x, y_data import aerosandbox as asb import aerosandbox.numpy as np degree = 10 opti = asb.Opti() coeffs = opti.variable(init_guess=np.zeros(degree + 1)) vandermonde = np.ones((len(x), degree + 1)) for j in range(1, degree + 1): vandermonde[:, j] = vandermonde[:, j - 1] * x y_model = vandermonde @ coeffs error = np.sum((y_model - y_data)**2) abs_coeffs = opti.variable(init_guess=np.zeros(degree + 1)) opti.subject_to([abs_coeffs > coeffs, abs_coeffs > -coeffs]) opti.minimize(error + 1e-4 * np.sum(abs_coeffs)) sol = opti.solve(verbose=False) if __name__ == '__main__': import matplotlib.pyplot as plt import seaborn as sns sns.set(palette=sns.color_palette("husl"))
def finite_difference_coefficients( x: np.ndarray, x0: float = 0, derivative_degree: int = 1, ) -> np.ndarray: """ Computes the weights (coefficients) in compact finite differece formulas for any order of derivative and to any order of accuracy on one-dimensional grids with arbitrary spacing. (Wording above is taken from the paper below, as are docstrings for parameters.) Modified from an implementation of: Fornberg, Bengt, "Generation of Finite Difference Formulas on Arbitrarily Spaced Grids". Oct. 1988. Mathematics of Computation, Volume 51, Number 184, pages 699-706. PDF: https://www.ams.org/journals/mcom/1988-51-184/S0025-5718-1988-0935077-0/S0025-5718-1988-0935077-0.pdf More detail: https://en.wikipedia.org/wiki/Finite_difference_coefficient Args: derivative_degree: The degree of the derivative that you are interested in obtaining. (denoted "M" in the paper) x: The grid points (not necessarily uniform or in order) that you want to obtain weights for. You must provide at least as many grid points as the degree of the derivative that you're interested in, plus 1. The order of accuracy of your derivative depends in part on the number of grid points that you provide. Specifically: order_of_accuracy = n_grid_points - derivative_degree (This is in general; can be higher in special cases.) For example, if you're evaluating a second derivative and you provide three grid points, you'll have a first-order-accurate answer. (x is denoted "alpha" in the paper) x0: The location that you are interested in obtaining a derivative at. This need not be on a grid point. Complexity is O(derivative_degree * len(x) ^ 2) Returns: A 1D ndarray corresponding to the coefficients that should be placed on each grid point. In other words, the approximate derivative at `x0` is the dot product of `coefficients` and the function values at each of the grid points `x`. """ ### Check inputs if derivative_degree < 1: return ValueError( "The parameter derivative_degree must be an integer >= 1.") expected_order_of_accuracy = np.length(x) - derivative_degree if expected_order_of_accuracy < 1: return ValueError( "You need to provide at least (derivative_degree+1) grid points in the x vector." ) ### Implement algorithm; notation from paper in docstring. N = np.length(x) - 1 delta = np.zeros(shape=(derivative_degree + 1, N + 1, N + 1), dtype="O") delta[0, 0, 0] = 1 c1 = 1 for n in range( 1, N + 1 ): # TODO make this algorithm more efficient; we only need to store a fraction of this data. c2 = 1 for v in range(n): c3 = x[n] - x[v] c2 = c2 * c3 # if n <= M: # Omitted because d is initialized to zero. # d[n, n - 1, v] = 0 for m in range(min(n, derivative_degree) + 1): delta[m, n, v] = ((x[n] - x0) * delta[m, n - 1, v] - m * delta[m - 1, n - 1, v]) / c3 for m in range(min(n, derivative_degree) + 1): delta[m, n, n] = (c1 / c2 * (m * delta[m - 1, n - 1, n - 1] - (x[n - 1] - x0) * delta[m, n - 1, n - 1])) c1 = c2 coefficients_object_array = delta[derivative_degree, -1, :] coefficients = np.array([ *coefficients_object_array ]) # Reconstructs using aerosandbox.numpy to intelligently type return coefficients