def test_horizontal(): name = 'horizontal' xhat = vect_op.xhat_np() yhat = vect_op.yhat_np() zhat = vect_op.zhat_np() ehat_x = vect_op.xhat_np() ehat_y = vect_op.yhat_np() ehat_z = vect_op.zhat_np() q_upper = 5. * xhat q_lower = 2. * xhat dir = 'x' transformed = from_earth_to_body(xhat, q_upper, q_lower) reference = ehat_z diff = transformed - reference resi = cas.mtimes(diff.T, diff) epsilon = 1.e-12 if resi > epsilon: awelogger.logger.error('tether frame transformation test (' + name + ' ' + dir + ') gives error of size: ' + str(resi)) dir = 'y' transformed = from_earth_to_body(yhat, q_upper, q_lower) reference = ehat_y diff = transformed - reference resi = cas.mtimes(diff.T, diff) epsilon = 1.e-12 if resi > epsilon: awelogger.logger.error('tether frame transformation test (' + name + ' ' + dir + ') gives error of size: ' + str(resi)) dir = 'z' transformed = from_earth_to_body(zhat, q_upper, q_lower) reference = -1. * ehat_x diff = transformed - reference resi = cas.mtimes(diff.T, diff) epsilon = 1.e-12 if resi > epsilon: awelogger.logger.error('tether frame transformation test (' + name + ' ' + dir + ') gives error of size: ' + str(resi)) return None
def test_transform_from_body(): xhat = vect_op.xhat_np() yhat = vect_op.yhat_np() zhat = vect_op.zhat_np() qx_test = np.random.random() * 1000. qy_test = np.random.random() * 1000. qz_test = np.random.random() * 1000. q_upper = qx_test * xhat + qy_test * yhat + qz_test * zhat q_lower = q_upper / 2. x_test = np.random.random() * 100. y_test = np.random.random() * 100. z_test = np.random.random() * 100. test_vec = x_test * xhat + y_test * yhat + z_test * zhat test_mag = vect_op.norm(test_vec) trans_vec = from_body_to_earth(test_vec, q_upper, q_lower) trans_mag = vect_op.norm(trans_vec) norm_error = vect_op.norm(trans_mag - test_mag) reformed_vec = from_earth_to_body(trans_vec, q_upper, q_lower) vector_diff = reformed_vec - test_vec vector_error = cas.mtimes(vector_diff.T, vector_diff) tot_error = norm_error + vector_error epsilon = 1.e-12 if tot_error > epsilon: awelogger.logger.error( 'tether frame transformation test gives error of size: ' + str(tot_error)) return None
def draw_kite_fuselage(ax, q, r, length, kite_color, side, num_per_meter, naca="0006"): r_dcm = np.array(cas.reshape(r, (3, 3))) total_width = np.float(naca[2:]) / 100. * length num_spanwise = np.ceil(total_width * num_per_meter / 2.) ypos = np.arange(-1. * num_spanwise, num_spanwise + 1.) / num_spanwise / 2. for y in ypos: yloc = cas.mtimes(r_dcm, vect_op.yhat_np()) * y * total_width basic_shell = get_naca_shell(length, naca) * (1 - (2. * y)**2.) horizontal_shell = [] for idx in range(basic_shell[:, 0].shape[0]): new_point = q + yloc + np.array(cas.mtimes(r_dcm, basic_shell[idx, :].T)) horizontal_shell = cas.vertcat(horizontal_shell, new_point.T) horizontal_shell = np.array(horizontal_shell) make_side_plot(ax, horizontal_shell, side, kite_color)
def get_aero_test_values(t, options): h_0 = options['aero_test']['h_0'] phi_0 = options['aero_test']['phi_0'] omega = options['aero_test']['omega'] h = h_0 * np.sin(omega * t) dh = h_0 * np.cos(omega * t) * omega dh = -1. * h_0 * np.sin(omega * t) * omega**2. phi = phi_0 * np.cos(omega * t) dphi = -1. * phi_0 * np.sin(omega * t) * omega ddphi = -1. * phi_0 * np.cos(omega * t) * omega**2. l_t = h + 3. dl_t = dh q10 = l_t * vect_op.zhat_np() dq10 = dl_t * vect_op.zhat_np() ehat1 = np.cos(phi) * vect_op.xhat_np() - np.sin(phi) * vect_op.zhat_np() ehat2 = vect_op.yhat_np() ehat3 = np.cos(phi) * vect_op.zhat_np() + np.sin(phi) * vect_op.xhat_np() r_dcm = cas.horzcat(ehat1, ehat2, ehat3) r_dcm = cas.reshape(r_dcm, (9, 1)) omega = dphi * ehat2 return l_t, dl_t, q10, dq10, r_dcm, omega
def draw_lifting_surface(ax, q, r, b_ref, c_tipn, c_root, c_tipp, kite_color, side, body_cross_sections_per_meter, naca="0012"): r_dcm = np.array(cas.reshape(r, (3, 3))) num_spanwise = np.ceil(b_ref * body_cross_sections_per_meter / 2.) ypos = np.arange(-1. * num_spanwise, num_spanwise + 1.) / num_spanwise / 2. leading_edges = [] trailing_edges = [] for y in ypos: yloc = cas.mtimes(r_dcm, vect_op.yhat_np()) * y * b_ref s = np.abs(y) / 0.5 # 1 at tips and 0 at root if y < 0: c_local = c_root * (1. - s) + c_tipn * s else: c_local = c_root * (1. - s) + c_tipp * s basic_shell = get_naca_shell(c_local, naca) basic_leading_ege = basic_shell[np.argmin(basic_shell[:, 0]), :] basic_trailing_ege = basic_shell[np.argmax(basic_shell[:, 0]), :] new_leading_edge = q + yloc + np.array( cas.mtimes(r_dcm, basic_leading_ege.T)) new_trailing_edge = q + yloc + np.array( cas.mtimes(r_dcm, basic_trailing_ege.T)) leading_edges = cas.vertcat(leading_edges, new_leading_edge.T) trailing_edges = cas.vertcat(trailing_edges, new_trailing_edge.T) horizontal_shell = [] for idx in range(basic_shell[:, 0].shape[0]): new_point = q + yloc + np.array( cas.mtimes(r_dcm, basic_shell[idx, :].T)) horizontal_shell = cas.vertcat(horizontal_shell, new_point.T) horizontal_shell = np.array(horizontal_shell) make_side_plot(ax, horizontal_shell, side, kite_color) make_side_plot(ax, leading_edges, side, kite_color) make_side_plot(ax, trailing_edges, side, kite_color) return None
def test_level_body_wind(epsilon): name = 'level wind' kite_dcm = cas.DM.eye(3) # CA = CD, CY = CS, CN = CL alpha = 0. beta = 0. u_test = get_test_wind(alpha, beta, kite_dcm) dir = 'x body->wind' test = vect_op.xhat_np() reference = vect_op.xhat_np() transformed = from_body_to_wind(u_test, kite_dcm, test) diff = transformed - reference resi = cas.mtimes(diff.T, diff) if resi > epsilon: awelogger.logger.error( 'kite frame transformation test (' + name + ' ' + dir + ') gives error of size: ' + str(resi)) dir = 'y body->wind' test = vect_op.yhat_np() reference = vect_op.yhat_np() transformed = from_body_to_wind(u_test, kite_dcm, test) diff = transformed - reference resi = cas.mtimes(diff.T, diff) if resi > epsilon: awelogger.logger.error( 'kite frame transformation test (' + name + ' ' + dir + ') gives error of size: ' + str(resi)) dir = 'z body->wind' test = vect_op.zhat_np() reference = vect_op.zhat_np() transformed = from_body_to_wind(u_test, kite_dcm, test) diff = transformed - reference resi = cas.mtimes(diff.T, diff) if resi > epsilon: awelogger.logger.error( 'kite frame transformation test (' + name + ' ' + dir + ') gives error of size: ' + str(resi)) return None
def get_rotor_reference_frame(init_options): n_rot_hat = get_ehat_tether(init_options) n_hat_is_x_hat = vect_op.abs( vect_op.norm(n_rot_hat - vect_op.xhat_np())) < 1.e-4 if n_hat_is_x_hat: y_rot_hat = vect_op.yhat_np() z_rot_hat = vect_op.zhat_np() else: u_hat = vect_op.xhat_np() z_rot_hat = vect_op.normed_cross(u_hat, n_rot_hat) y_rot_hat = vect_op.normed_cross(z_rot_hat, n_rot_hat) return n_rot_hat, y_rot_hat, z_rot_hat
def draw_kite_fuselage(ax, q, r, length, kite_color, side, body_cross_sections_per_meter, naca="0006"): r_dcm = np.array(cas.reshape(r, (3, 3))) total_width = np.float(naca[2:]) / 100. * length num_spanwise = np.ceil(total_width * body_cross_sections_per_meter / 2.) ypos = np.arange(-1. * num_spanwise, num_spanwise + 1.) / num_spanwise / 2. for y in ypos: yloc = cas.mtimes(r_dcm, vect_op.yhat_np()) * y * total_width zloc = cas.mtimes(r_dcm, vect_op.zhat_np()) * y * total_width basic_shell = get_naca_shell(length, naca) * (1 - (2. * y)**2.) span_direction_shell = [] up_direction_shell = [] for idx in range(basic_shell[:, 0].shape[0]): new_point_spanwise = q + yloc + np.array( cas.mtimes(r_dcm, basic_shell[idx, :].T)) span_direction_shell = cas.vertcat(span_direction_shell, new_point_spanwise.T) new_point_upwise = q + zloc + np.array( cas.mtimes(r_dcm, basic_shell[idx, :].T)) up_direction_shell = cas.vertcat(up_direction_shell, new_point_upwise.T) span_direction_shell = np.array(span_direction_shell) make_side_plot(ax, span_direction_shell, side, kite_color) up_direction_shell = np.array(up_direction_shell) make_side_plot(ax, up_direction_shell, side, kite_color) return None
def get_windings(nlp_options, model, V, outputs={}): if 'winding' not in list(outputs.keys()): outputs['winding'] = {} nk = nlp_options['n_k'] parent_map = model.architecture.parent_map kite_nodes = model.architecture.kite_nodes ehat_tether_x = 0. ehat_tether_y = 0. ehat_tether_z = 0. total_steps = float(nk) # TODO: in case of collocation, include collocation node info weighted with quad_weights # TODO: weight with time constant in case of phase fixing! for kdx in range(nk): local_ehat = vect_op.normalize(V['xd', kdx, 'q10']) ehat_tether_x += local_ehat[0] / total_steps ehat_tether_y += local_ehat[1] / total_steps ehat_tether_z += local_ehat[2] / total_steps ehat_tether = vect_op.normalize( cas.vertcat(ehat_tether_x, ehat_tether_y, ehat_tether_z)) outputs['winding']['ehat_tether'] = ehat_tether ehat_side_a = vect_op.normed_cross(vect_op.yhat_np(), ehat_tether) # right handed coordinate system -> x/re: _a, y/im: _b, z/out: _tether ehat_side_b = vect_op.normed_cross(ehat_tether, ehat_side_a) # now project the path onto this plane for n in kite_nodes: parent = parent_map[n] theta_start = 0. theta_end = 0. # find the origin of the plane origin = np.zeros((3, 1)) for kdx in range(nk): q = V['xd', kdx, 'q' + str(n) + str(parent)] q_in_plane = q - vect_op.dot(q, ehat_tether) * ehat_tether origin = origin + q_in_plane / total_steps # recenter the plane about origin for kdx in range(nk): q = V['xd', kdx, 'q' + str(n) + str(parent)] q_in_plane = q - vect_op.dot(q, ehat_tether) * ehat_tether q_recentered = q_in_plane - origin q_next = V['xd', kdx + 1, 'q' + str(n) + str(parent)] q_next_in_plane = q_next - vect_op.dot(q_next, ehat_tether) * ehat_tether q_next_recentered = q_next_in_plane - origin delta_q = q_next_recentered - q_recentered x = vect_op.dot(q_recentered, ehat_side_a) y = vect_op.dot(q_recentered, ehat_side_b) r_squared = x**2. + y**2. dx = vect_op.dot(delta_q, ehat_side_a) dy = vect_op.dot(delta_q, ehat_side_b) # dx = vect_op.dot(dq_in_plane, ehat_side_a) # dy = vect_op.dot(dq_in_plane, ehat_side_b) dtheta = (x * dy - y * dx) / (r_squared + 1.0e-4) theta_end += dtheta winding = (theta_end - theta_start) / 2. / np.pi outputs['winding']['winding' + str(n)] = winding return outputs
def test_vertical_body_earth(epsilon): name = 'vertical' xhat = vect_op.xhat_np() yhat = vect_op.yhat_np() zhat = vect_op.zhat_np() ehat1_k = vect_op.xhat_np() ehat2_k = vect_op.yhat_np() ehat3_k = vect_op.zhat_np() ehat_chord = -1. * zhat ehat_span = -1. * xhat ehat_up = yhat kite_dcm = cas.horzcat(ehat_chord, ehat_span, ehat_up) dir = 'chord body->earth' transformed = from_body_to_earth(kite_dcm, ehat1_k) reference = ehat_chord diff = transformed - reference resi = cas.mtimes(diff.T, diff) if resi > epsilon: awelogger.logger.error( 'kite frame transformation test (' + name + ' ' + dir + ') gives error of size: ' + str(resi)) dir = 'span body->earth' transformed = from_body_to_earth(kite_dcm, ehat2_k) reference = ehat_span diff = transformed - reference resi = cas.mtimes(diff.T, diff) if resi > epsilon: awelogger.logger.error( 'kite frame transformation test (' + name + ' ' + dir + ') gives error of size: ' + str(resi)) dir = 'up body->earth' transformed = from_body_to_earth(kite_dcm, ehat3_k) reference = ehat_up diff = transformed - reference resi = cas.mtimes(diff.T, diff) if resi > epsilon: awelogger.logger.error( 'kite frame transformation test (' + name + ' ' + dir + ') gives error of size: ' + str(resi)) dir = 'chord earth->body' transformed = from_earth_to_body(kite_dcm, ehat_chord) reference = ehat1_k diff = transformed - reference resi = cas.mtimes(diff.T, diff) if resi > epsilon: awelogger.logger.error( 'kite frame transformation test (' + name + ' ' + dir + ') gives error of size: ' + str(resi)) dir = 'span earth->body' transformed = from_earth_to_body(kite_dcm, ehat_span) reference = ehat2_k diff = transformed - reference resi = cas.mtimes(diff.T, diff) if resi > epsilon: awelogger.logger.error( 'kite frame transformation test (' + name + ' ' + dir + ') gives error of size: ' + str(resi)) dir = 'up earth->body' transformed = from_earth_to_body(kite_dcm, ehat_up) reference = ehat3_k diff = transformed - reference resi = cas.mtimes(diff.T, diff) if resi > epsilon: awelogger.logger.error( 'kite frame transformation test (' + name + ' ' + dir + ') gives error of size: ' + str(resi)) return None
def compute_vortex_verification_points(plot_dict, cosmetics, idx_at_eval, kdx): kite_plane_induction_params = get_kite_plane_induction_params( plot_dict, idx_at_eval) architecture = plot_dict['architecture'] filament_list = reconstruct_filament_list(plot_dict, idx_at_eval) number_of_kites = architecture.number_of_kites radius = kite_plane_induction_params['average_radius'] center = kite_plane_induction_params['center'] u_infty = kite_plane_induction_params['u_infty'] u_zero = u_infty verification_points = plot_dict['options']['model']['aero']['vortex'][ 'verification_points'] half_points = int(verification_points / 2.) + 1 psi0_base = plot_dict['options']['solver']['initialization']['psi0_rad'] mu_grid_min = kite_plane_induction_params['mu_start_by_path'] mu_grid_max = kite_plane_induction_params['mu_end_by_path'] psi_grid_min = psi0_base - np.pi / float(number_of_kites) + float( kdx) * 2. * np.pi / float(number_of_kites) psi_grid_max = psi0_base + np.pi / float(number_of_kites) + float( kdx) * 2. * np.pi / float(number_of_kites) # define mu with respect to kite mid-span radius mu_grid_points = np.linspace(mu_grid_min, mu_grid_max, verification_points, endpoint=True) length_mu = mu_grid_points.shape[0] beta = np.linspace(0., np.pi / 2, half_points) cos_front = 0.5 * (1. - np.cos(beta)) cos_back = -1. * cos_front[::-1] psi_grid_unscaled = cas.vertcat(cos_back, cos_front) + 0.5 psi_grid_points_cas = psi_grid_unscaled * (psi_grid_max - psi_grid_min) + psi_grid_min psi_grid_points_np = np.array(psi_grid_points_cas) psi_grid_points_recenter = np.deg2rad(np.rad2deg(psi_grid_points_np)) psi_grid_points = np.unique(psi_grid_points_recenter) length_psi = psi_grid_points.shape[0] # reserve mesh space y_matr = np.ones((length_psi, length_mu)) z_matr = np.ones((length_psi, length_mu)) a_matr = np.ones((length_psi, length_mu)) for mdx in range(length_mu): mu_val = mu_grid_points[mdx] for pdx in range(length_psi): psi_val = psi_grid_points[pdx] r_val = mu_val * radius ehat_radial = vect_op.zhat_np() * cas.cos( psi_val) - vect_op.yhat_np() * cas.sin(psi_val) added = r_val * ehat_radial x_obs = center + added unscaled = mu_val * ehat_radial a_ind = float( vortex_flow.get_induction_factor_at_observer( plot_dict['options']['model'], filament_list, x_obs, u_zero, n_hat=vect_op.xhat())) unscaled_y_val = float(cas.mtimes(unscaled.T, vect_op.yhat_np())) unscaled_z_val = float(cas.mtimes(unscaled.T, vect_op.zhat_np())) y_matr[pdx, mdx] = unscaled_y_val z_matr[pdx, mdx] = unscaled_z_val a_matr[pdx, mdx] = a_ind y_list = np.array(cas.reshape(y_matr, (length_psi * length_mu, 1))) z_list = np.array(cas.reshape(z_matr, (length_psi * length_mu, 1))) return y_matr, z_matr, a_matr, y_list, z_list
def test(gamma_scale=1.): architecture = archi.Architecture({1: 0}) options = {} options['induction'] = {} options['induction']['vortex_gamma_scale'] = gamma_scale options['induction']['vortex_wake_nodes'] = 2 options['induction']['vortex_far_convection_time'] = 1. options['induction']['vortex_u_ref'] = 1. options['induction']['vortex_position_scale'] = 1. kite = architecture.kite_nodes[0] xd_struct = cas.struct([ cas.entry("wx_" + str(kite) + "_ext_0", shape=(3, 1)), cas.entry("wx_" + str(kite) + "_ext_1", shape=(3, 1)), cas.entry("wx_" + str(kite) + "_int_0", shape=(3, 1)), cas.entry("wx_" + str(kite) + "_int_1", shape=(3, 1)) ]) xl_struct = cas.struct([cas.entry("wg_" + str(kite) + "_0")]) var_struct = cas.struct_symSX( [cas.entry('xd', struct=xd_struct), cas.entry('xl', struct=xl_struct)]) variables = var_struct(0.) variables['xd', 'wx_' + str(kite) + '_ext_0'] = 0.5 * vect_op.yhat_np() variables['xd', 'wx_' + str(kite) + '_int_0'] = -0.5 * vect_op.yhat_np() variables['xd', 'wx_' + str(kite) + '_ext_1'] = variables['xd', 'wx_' + str(kite) + '_ext_0'] + vect_op.xhat_np() variables['xd', 'wx_' + str(kite) + '_int_1'] = variables['xd', 'wx_' + str(kite) + '_int_0'] + vect_op.xhat_np() variables['xl', 'wg_' + str(kite) + '_0'] = 1. / gamma_scale test_list = get_list(options, variables, architecture) filaments = test_list.shape[1] filament_count_test = filaments - 6 if not (filament_count_test == 0): message = 'filament list does not work as expected. difference in number of filaments in test_list = ' + str( filament_count_test) awelogger.logger.error(message) raise Exception(message) LE_expected = cas.DM(np.array([0., -0.5, 0., 0., 0.5, 0., 1.])) PE_expected = cas.DM(np.array([0., 0.5, 0., 1., 0.5, 0., 1.])) TE_expected = cas.DM(np.array([1., -0.5, 0., 0., -0.5, 0., 1.])) expected_filaments = { 'leading edge': LE_expected, 'positive edge': PE_expected, 'trailing edge': TE_expected } for type in expected_filaments.keys(): expected_filament = expected_filaments[type] expected_in_list = expected_filament_in_list(test_list, expected_filament) if not expected_in_list: message = 'filament list does not work as expected. ' + type + \ ' test filament not in test_list.' awelogger.logger.error(message) with np.printoptions(precision=3, suppress=True): print('test_list:') print(np.array(test_list)) raise Exception(message) NE_not_expected = cas.DM(np.array([1., -0.5, 0., 1., 0.5, 0., -1.])) not_expected_filaments = {'negative edge': NE_not_expected} for type in not_expected_filaments.keys(): not_expected_filament = not_expected_filaments[type] is_reasonable = not (expected_filament_in_list(test_list, not_expected_filament)) if not is_reasonable: message = 'filament list does not work as expected. ' + type + \ ' test filament in test_list.' awelogger.logger.error(message) with np.printoptions(precision=3, suppress=True): print('test_list:') print(np.array(test_list)) raise Exception(message) return test_list