def generate_rotational_dynamics(variables, f_nodes, parameters, outputs, architecture): kite_nodes = architecture.kite_nodes parent_map = architecture.parent_map j_inertia = parameters['theta0', 'geometry', 'j'] xd = variables['SI']['xd'] xddot = variables['SI']['xddot'] cstr_list = mdl_constraint.MdlConstraintList() for kite in kite_nodes: parent = parent_map[kite] moment = f_nodes['m' + str(kite) + str(parent)] rlocal = cas.reshape(xd['r' + str(kite) + str(parent)], (3, 3)) drlocal = cas.reshape(xddot['dr' + str(kite) + str(parent)], (3, 3)) omega = xd['omega' + str(kite) + str(parent)] omega_skew = vect_op.skew(omega) domega = xddot['domega' + str(kite) + str(parent)] tether_moment = outputs['tether_moments']['n{}{}'.format(kite, parent)] # moment = J dot(omega) + omega x (J omega) + [tether moment which is zero if holonomic constraints do not depend on omega] J_dot_omega = cas.mtimes(j_inertia, domega) omega_cross_J_omega = vect_op.cross(omega, cas.mtimes(j_inertia, omega)) omega_derivative = moment - (J_dot_omega + omega_cross_J_omega + tether_moment) rotational_2nd_law = omega_derivative / vect_op.norm( cas.diag(j_inertia)) rotation_dynamics_cstr = cstr_op.Constraint(expr=rotational_2nd_law, name='rotation_dynamics' + str(kite), cstr_type='eq') cstr_list.append(rotation_dynamics_cstr) # Rdot = R omega_skew -> R ( kappa/2 (I - R.T R) + omega_skew ) baumgarte = parameters['theta0', 'kappa_r'] orthonormality = baumgarte / 2. * (cas.DM_eye(3) - cas.mtimes(rlocal.T, rlocal)) ref_frame_deriv_matrix = drlocal - (cas.mtimes( rlocal, orthonormality + omega_skew)) ref_frame_derivative = cas.reshape(ref_frame_deriv_matrix, (9, 1)) ortho_cstr = cstr_op.Constraint(expr=ref_frame_derivative, name='ref_frame_deriv' + str(kite), cstr_type='eq') cstr_list.append(ortho_cstr) return cstr_list, outputs
def jacobian_dcm(expr, xd_si, variables_scaled, kite, parent): """ Differentiate expression w.r.t. kite direct cosine matrix""" dcm_si = xd_si['r{}{}'.format(kite, parent)] dcm_scaled = variables_scaled['xd', 'r{}{}'.format(kite, parent)] jac_dcm = rotation( cas.reshape(dcm_si, (3,3)), cas.reshape(cas.jacobian(expr, dcm_scaled), (3,3)) ).T return jac_dcm
def initial_guess_actuator_xl(init_options, nlp, formulation, model, V_init): level_siblings = model.architecture.get_all_level_siblings() time_final = init_options['precompute']['time_final'] omega_norm = init_options['precompute']['angular_speed'] tgrid_coll = nlp.time_grids['coll'](time_final) u_hat, v_hat, w_hat = get_local_wind_reference_frame(init_options) uzero_matr = cas.horzcat(u_hat, v_hat, w_hat) uzero_matr_cols = cas.reshape(uzero_matr, (9, 1)) n_rot_hat, y_rot_hat, z_rot_hat = tools_init.get_rotor_reference_frame(init_options) rot_matr = cas.horzcat(n_rot_hat, y_rot_hat, z_rot_hat) rot_matr_cols = cas.reshape(rot_matr, (9, 1)) dict = {} dict['rot_matr'] = rot_matr_cols dict['area'] = cas.DM(1.) dict['cmy'] = cas.DM(0.) dict['cmz'] = cas.DM(0.) dict['uzero_matr'] = uzero_matr_cols dict['g_vec_length'] = cas.DM(1.) dict['n_vec_length'] = cas.DM(1.) dict['z_vec_length'] = cas.DM(1.) dict['u_vec_length'] = cas.DM(1.) dict['varrho'] = cas.DM(1.) dict['qzero'] = cas.DM(1.) dict['gamma'] = get_gamma_angle(init_options) dict['cosgamma'] = np.cos(dict['gamma']) dict['singamma'] = np.sin(dict['gamma']) dict['chi'] = get_chi_angle(init_options, 'xl') dict['coschi'] = np.cos(dict['chi']) dict['sinchi'] = np.sin(dict['chi']) dict['tanhalfchi'] = np.tan(dict['chi'] / 2.) dict['sechalfchi'] = 1. / np.cos(dict['chi'] / 2.) dict['LL'] = cas.DM([0.375, 0., 0., 0., -1., 0., 0., -1., 0.]) dict['corr'] = 1. - init_options['xd']['a'] var_type = 'xl' for name in struct_op.subkeys(model.variables, 'xl'): name_stripped, _ = struct_op.split_name_and_node_identifier(name) if 'psi' in name_stripped: V_init = set_azimuth_variables(V_init, init_options, name, model, nlp, tgrid_coll, level_siblings, omega_norm) elif name_stripped in dict.keys(): V_init = tools_init.insert_dict(dict, var_type, name, name_stripped, V_init) return V_init
def draw_kite_vertical(ax, q, r, length, height, b_ref, c_ref, kite_color, side, num_per_meter, naca="0012"): r_dcm = np.array(cas.reshape(r, (3, 3))) r_dcm = np.array(cas.reshape(r, (3, 3))) ehat_1 = np.reshape(r_dcm[:, 0], (3, 1)) ehat_3 = np.reshape(r_dcm[:, 2], (3, 1)) new_ehat1 = ehat_1 new_ehat2 = ehat_3 new_ehat3 = np.array(vect_op.normed_cross(new_ehat1, new_ehat2)) r_new = np.array(cas.horzcat(new_ehat1, new_ehat2, new_ehat3)) horizontal_space = (3. * length / 4. - c_ref / 3.) * ehat_1 pos = q + horizontal_space + ehat_3 * height / 2. draw_lifting_surface(ax, pos, r_new, height, c_ref, c_ref / 2., c_ref / 4., kite_color, side, num_per_meter, naca)
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 get_wingtip_position(kite, model, variables, parameters, ext_int): parent_map = model.architecture.parent_map xd = model.variables_dict['xd'](variables['xd']) if ext_int == 'ext': span_sign = 1. elif ext_int == 'int': span_sign = -1. else: awelogger.logger.error('wing side not recognized for 6dof kite.') parent = parent_map[kite] name = 'q' + str(kite) + str(parent) q_unscaled = xd[name] scale = model.scaling['xd'][name] q = q_unscaled * scale kite_dcm = cas.reshape(xd['kite_dcm' + str(kite) + str(parent)], (3, 3)) ehat_span = kite_dcm[:, 1] b_ref = parameters['theta0', 'geometry', 'b_ref'] wingtip_position = q + ehat_span * span_sign * b_ref / 2. return wingtip_position
def approximate_tip_radius(model_options, variables, kite, architecture, tip, parameters): b_ref = parameters['theta0', 'geometry', 'b_ref'] half_span_proj = b_ref / 2. parent = architecture.parent_map[kite] radial_vector = get_kite_radial_vector(model_options, kite, variables, architecture, parameters) if int(model_options['kite_dof']) == 6: r_column = variables['xd']['r' + str(kite) + str(parent)] r = cas.reshape(r_column, (3, 3)) ehat2 = r[:, 1] # spanwise, from pe to ne ehat2_proj_radial = vect_op.smooth_abs( cas.mtimes(radial_vector.T, ehat2)) half_span_proj = b_ref * ehat2_proj_radial / 2. radius = get_kite_radius(model_options, kite, variables, architecture, parameters) tip_radius = radius if ('int' in tip) or (tip == 0): tip_radius = tip_radius - half_span_proj elif ('ext' in tip) or (tip == 1): tip_radius = tip_radius + half_span_proj else: raise Exception('invalid tip designated') return tip_radius
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 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 time_derivative(expr, variables, architecture): # notice that the the chain rule # df/dt = partial f/partial t # + (partial f/partial x1)(partial x1/partial t) # + (partial f/partial x2)(partial x2/partial t)... # doesn't care about the scaling of the component functions, as long as # the units of (partial xi) will cancel # it is here assumed that (partial f/partial t) = 0. vars_scaled = variables['scaled'] deriv = 0. # (partial f/partial xi) for variables with trivial derivatives deriv_vars = struct_op.subkeys(vars_scaled, 'xddot') for deriv_name in deriv_vars: deriv_type = struct_op.get_variable_type(variables['SI'], deriv_name) var_name = deriv_name[1:] var_type = struct_op.get_variable_type(variables['SI'], var_name) q_sym = vars_scaled[var_type, var_name] dq_sym = vars_scaled[deriv_type, deriv_name] deriv += cas.mtimes(cas.jacobian(expr, q_sym), dq_sym) # (partial f/partial xi) for kite rotation matrices kite_nodes = architecture.kite_nodes for kite in kite_nodes: parent = architecture.parent_map[kite] r_name = 'r{}{}'.format(kite, parent) kite_has_6dof = r_name in struct_op.subkeys(vars_scaled, 'xd') if kite_has_6dof: r_kite = vars_scaled['xd', r_name] dcm_kite = cas.reshape(r_kite, (3, 3)) omega = vars_scaled['xd', 'omega{}{}'.format(kite, parent)] dexpr_dr = cas.jacobian(expr, r_kite) dr_dt = cas.reshape( cas.mtimes(vect_op.skew(omega), cas.inv(dcm_kite.T)), (9, 1)) deriv += cas.mtimes(dexpr_dr, dr_dt) return deriv
def get_tether_length_constraint(options, vars_si, parameters, architecture): xd_si = vars_si['xd'] theta_si = vars_si['theta'] g_dict = {} number_of_nodes = architecture.number_of_nodes parent_map = architecture.parent_map kite_nodes = architecture.kite_nodes com_attachment = (options['tether']['attachment'] == 'com') stick_attachment = (options['tether']['attachment'] == 'stick') kite_has_6dof = (int(options['kite_dof']) == 6) if not (com_attachment or stick_attachment): message = 'Unknown tether attachment option: {}'.format(options['tether']['attachment']) awelogger.logger.error(message) raise Exception(message) for node in range(1, number_of_nodes): parent = parent_map[node] q_si = xd_si['q' + str(node) + str(parent)] node_is_a_kite = (node in kite_nodes) has_extended_arm = node_is_a_kite and stick_attachment if has_extended_arm and not kite_has_6dof: message = 'Stick tether attachment option not implemented for 3DOF kites' awelogger.logger.error(message) raise Exception(message) if has_extended_arm: dcm = cas.reshape(xd_si['r{}{}'.format(node, parent)], (3, 3)) current_node = q_si + cas.mtimes(dcm, parameters['theta0', 'geometry', 'r_tether']) else: current_node = q_si if node == 1: previous_node = cas.DM.zeros((3, 1)) segment_length = xd_si['l_t'] elif node in kite_nodes: grandparent = parent_map[parent] previous_node = xd_si['q' + str(parent) + str(grandparent)] segment_length = theta_si['l_s'] else: grandparent = parent_map[parent] previous_node = xd_si['q' + str(parent) + str(grandparent)] segment_length = theta_si['l_i'] # holonomic constraint seg_vector = (current_node - previous_node) length_constraint = 0.5 * (cas.mtimes(seg_vector.T, seg_vector) - segment_length ** 2.0) g_dict['c' + str(node) + str(parent)] = length_constraint return g_dict
def columnize(var): # only allows 2x2 matrices for variable [counted_rows, counted_columns] = var.shape number_elements = counted_rows * counted_columns column_var = cas.reshape(var, (number_elements, 1)) return column_var
def draw_kite_horizontal(ax, q, r, length, height, b_ref, c_ref, kite_color, side, num_per_meter, naca="0012"): r_dcm = np.array(cas.reshape(r, (3, 3))) ehat_1 = np.reshape(r_dcm[:, 0], (3,1)) ehat_3 = np.reshape(r_dcm[:, 2], (3,1)) horizontal_space = (3. * length / 4. - c_ref / 3.) * ehat_1 pos = q + horizontal_space + ehat_3 * height draw_lifting_surface(ax, pos, r_dcm, b_ref / 3., c_ref / 3., c_ref / 2., c_ref / 3., kite_color, side, num_per_meter, naca)
def isRotationMatrix(R): diff = cas.DM.eye(3) - cas.mtimes(R.T, R) diff_vert = cas.reshape(diff, (9, 1)) resi = norm(diff_vert)**0.5 threshold = 1.e-1 if resi > threshold: print('resi:' + str(resi) + ' threshold: ' + str(threshold)) return resi < threshold
def guess_values_at_time(t, init_options, model): ret = {} for name in struct_op.subkeys(model.variables, 'xd'): ret[name] = 0.0 ret['e'] = 0.0 ret['l_t'] = init_options['xd']['l_t'] ret['dl_t'] = 0.0 number_of_nodes = model.architecture.number_of_nodes parent_map = model.architecture.parent_map kite_nodes = model.architecture.kite_nodes kite_dof = model.kite_dof for node in range(1, number_of_nodes): parent = parent_map[node] if parent == 0: parent_position = np.zeros((3, 1)) else: grandparent = parent_map[parent] parent_position = ret['q' + str(parent) + str(grandparent)] if not node in kite_nodes: ret['q' + str(node) + str(parent)] = get_tether_node_position(init_options, parent_position, node, ret['l_t']) ret['dq' + str(node) + str(parent)] = np.zeros((3, 1)) else: height = init_options['precompute']['height'] radius = init_options['precompute']['radius'] ehat_normal, ehat_radial, ehat_tangential = tools.get_rotating_reference_frame(t, init_options, model, node, ret) tether_vector = ehat_radial * radius + ehat_normal * height position = parent_position + tether_vector velocity = tools.get_velocity_vector(t, init_options, model, node, ret) ret['q' + str(node) + str(parent)] = position ret['dq' + str(node) + str(parent)] = velocity dcm = tools.get_kite_dcm(t, init_options, model, node, ret) if init_options['cross_tether']: if init_options['cross_tether_attachment'] in ['com','stick']: dcm = get_cross_tether_dcm(init_options, dcm) dcm_column = cas.reshape(dcm, (9, 1)) omega_vector = tools.get_omega_vector(t, init_options, model, node, ret) if int(kite_dof) == 6: ret['omega' + str(node) + str(parent)] = omega_vector ret['r' + str(node) + str(parent)] = dcm_column return ret
def __create_reference_interpolator(self): """ Create time-varying reference generator for tracking MPC based on interpolation of optimal periodic steady state. """ # MPC time grid self.__t_grid_coll = self.__trial.nlp.time_grids['coll'](self.__N*self.__ts) self.__t_grid_coll = ct.reshape(self.__t_grid_coll.T, self.__t_grid_coll.numel(),1).full() self.__t_grid_x_coll = self.__trial.nlp.time_grids['x_coll'](self.__N*self.__ts) self.__t_grid_x_coll = ct.reshape(self.__t_grid_x_coll.T, self.__t_grid_x_coll.numel(),1).full() # interpolate steady state solution self.__ref_dict = self.__pocp_trial.visualization.plot_dict nlp_options = self.__pocp_trial.options['nlp'] V_opt = self.__pocp_trial.optimization.V_opt if self.__mpc_options['ref_interpolator'] == 'poly': self.__interpolator = self.__pocp_trial.nlp.Collocation.build_interpolator(nlp_options, V_opt) elif self.__mpc_options['ref_interpolator'] == 'spline': self.__interpolator = self.__build_spline_interpolator(nlp_options, V_opt) return None
def decolumnize(options, architecture, columnized_list): wake_nodes = options['induction']['vortex_wake_nodes'] number_kites = architecture.number_of_kites rings = wake_nodes - 1 entries = columnized_list.shape[0] filaments = 3 * (rings + 1) * number_kites arguments = int(float(entries) / float(filaments)) filament_list = cas.reshape(columnized_list, (arguments, filaments)) return filament_list
def resquare(column): entries = column.shape[0] * column.shape[1] guess_side_dim = np.sqrt(float(entries)) can_be_resquared = (np.floor(guess_side_dim) **2. == float(entries)) if can_be_resquared: side = int(guess_side_dim) return cas.reshape(column, (side, side)) else: message = 'column matrix cannot be re-squared. inappropriate number of entries: ' + str(entries) awelogger.logger.error(message) return column
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 collect_aero_validity_outputs(options, xd, ua, n, parent, outputs, parameters): if 'aero_validity' not in list(outputs.keys()): outputs['aero_validity'] = {} tightness = options['model_bounds']['aero_validity']['scaling'] num_ref = options['model_bounds']['aero_validity']['num_ref'] if 'aerodynamics' not in list(outputs.keys()): outputs['aerodynamics'] = {} alpha = cas.DM(0.) beta = cas.DM(0.) if int(options['kite_dof']) == 6: r = cas.reshape(xd['r' + str(n) + str(parent)], (3, 3)) ehat1 = r[:, 0] # chordwise, froml_e tot_e ehat2 = r[:, 1] # spanwise, fromp_e ton_e ehat3 = r[:, 2] # up alpha = get_alpha(ua, r) beta = get_beta(ua, r) alpha_min = options['aero']['alpha_min_deg']*np.pi/180.0 alpha_max = options['aero']['alpha_max_deg']*np.pi/180.0 beta_min = options['aero']['beta_min_deg']*np.pi/180.0 beta_max = options['aero']['beta_max_deg']*np.pi/180.0 alpha_ub = (cas.mtimes(ua.T, ehat3) - cas.mtimes(ua.T, ehat1) * alpha_max) * tightness / num_ref alpha_lb = (- cas.mtimes(ua.T, ehat3) + cas.mtimes(ua.T, ehat1) * alpha_min) * tightness / num_ref beta_ub = (cas.mtimes(ua.T, ehat2) - cas.mtimes(ua.T, ehat1) * beta_max) * tightness / num_ref beta_lb = (- cas.mtimes(ua.T, ehat2) + cas.mtimes(ua.T, ehat1) * beta_min) * tightness / num_ref outputs['aero_validity']['alpha_ub' + str(n)] = alpha_ub outputs['aero_validity']['alpha_lb' + str(n)] = alpha_lb outputs['aero_validity']['beta_ub' + str(n)] = beta_ub outputs['aero_validity']['beta_lb' + str(n)] = beta_lb outputs['aerodynamics']['alpha' + str(n)] = alpha outputs['aerodynamics']['beta' + str(n)] = beta outputs['aerodynamics']['alpha_deg' + str(n)] = alpha * 180. / np.pi outputs['aerodynamics']['beta_deg' + str(n)] = beta * 180. / np.pi return outputs
def collect_kite_aerodynamics_outputs(options, architecture, atmos, wind, variables, parameters, base_aerodynamic_quantities, outputs): if 'aerodynamics' not in list(outputs.keys()): outputs['aerodynamics'] = {} # unpack kite = base_aerodynamic_quantities['kite'] air_velocity = base_aerodynamic_quantities['air_velocity'] aero_coefficients = base_aerodynamic_quantities['aero_coefficients'] f_aero_earth = base_aerodynamic_quantities['f_aero_earth'] f_aero_body = base_aerodynamic_quantities['f_aero_body'] f_aero_control = base_aerodynamic_quantities['f_aero_control'] f_aero_wind = base_aerodynamic_quantities['f_aero_wind'] f_lift_earth = base_aerodynamic_quantities['f_lift_earth'] f_drag_earth = base_aerodynamic_quantities['f_drag_earth'] f_side_earth = base_aerodynamic_quantities['f_side_earth'] m_aero_body = base_aerodynamic_quantities['m_aero_body'] kite_dcm = base_aerodynamic_quantities['kite_dcm'] q = base_aerodynamic_quantities['q'] f_lift_earth_overwrite = options['aero']['overwrite']['f_lift_earth'] if f_lift_earth_overwrite is not None: f_lift_earth = f_lift_earth_overwrite for name in set(base_aerodynamic_quantities['aero_coefficients'].keys()): outputs['aerodynamics'][ name + str(kite)] = base_aerodynamic_quantities['aero_coefficients'][name] outputs['aerodynamics']['air_velocity' + str(kite)] = air_velocity airspeed = vect_op.norm(air_velocity) outputs['aerodynamics']['airspeed' + str(kite)] = airspeed outputs['aerodynamics']['u_infty' + str(kite)] = wind.get_velocity(q[2]) rho = atmos.get_density(q[2]) outputs['aerodynamics']['air_density' + str(kite)] = rho outputs['aerodynamics']['dyn_pressure' + str(kite)] = 0.5 * rho * cas.mtimes( air_velocity.T, air_velocity) ehat_chord = kite_dcm[:, 0] ehat_span = kite_dcm[:, 1] ehat_up = kite_dcm[:, 2] outputs['aerodynamics']['ehat_chord' + str(kite)] = ehat_chord outputs['aerodynamics']['ehat_span' + str(kite)] = ehat_span outputs['aerodynamics']['ehat_up' + str(kite)] = ehat_up outputs['aerodynamics']['f_aero_body' + str(kite)] = f_aero_body outputs['aerodynamics']['f_aero_control' + str(kite)] = f_aero_control outputs['aerodynamics']['f_aero_earth' + str(kite)] = f_aero_earth outputs['aerodynamics']['f_aero_wind' + str(kite)] = f_aero_wind ortho = cas.reshape(cas.mtimes(kite_dcm.T, kite_dcm) - np.eye(3), (9, 1)) ortho_resi = cas.mtimes(ortho.T, ortho) outputs['aerodynamics']['ortho_resi' + str(kite)] = ortho_resi conversion_resis = [] conversion_targets = { 'control': f_aero_control, 'earth': f_aero_earth, 'wind': f_aero_wind } for f_aero_target in conversion_targets.values(): resi = 1. - vect_op.norm(f_aero_target) / vect_op.norm(f_aero_body) conversion_resis = cas.vertcat(conversion_resis, resi) resi = vect_op.norm(f_aero_earth - f_lift_earth - f_drag_earth - f_side_earth) / vect_op.norm(f_aero_body) conversion_resis = cas.vertcat(conversion_resis, resi) outputs['aerodynamics']['check_conversion' + str(kite)] = conversion_resis outputs['aerodynamics']['f_lift_earth' + str(kite)] = f_lift_earth outputs['aerodynamics']['f_drag_earth' + str(kite)] = f_drag_earth outputs['aerodynamics']['f_side_earth' + str(kite)] = f_side_earth b_ref = parameters['theta0', 'geometry', 'b_ref'] c_ref = parameters['theta0', 'geometry', 'c_ref'] circulation_cross = vect_op.smooth_norm( f_lift_earth) / b_ref / rho / vect_op.smooth_norm( vect_op.cross(air_velocity, ehat_span)) circulation_cl = 0.5 * airspeed**2. * aero_coefficients[ 'CL'] * c_ref / vect_op.smooth_norm( vect_op.cross(air_velocity, ehat_span)) outputs['aerodynamics']['circulation_cross' + str(kite)] = circulation_cross outputs['aerodynamics']['circulation_cl' + str(kite)] = circulation_cl outputs['aerodynamics']['circulation' + str(kite)] = circulation_cross outputs['aerodynamics']['wingtip_ext' + str(kite)] = q + ehat_span * b_ref / 2. outputs['aerodynamics']['wingtip_int' + str(kite)] = q - ehat_span * b_ref / 2. outputs['aerodynamics']['fstar_aero' + str(kite)] = cas.mtimes( air_velocity.T, ehat_chord) / c_ref outputs['aerodynamics']['r' + str(kite)] = kite_dcm.reshape((9, 1)) if int(options['kite_dof']) == 6: outputs['aerodynamics']['m_aero_body' + str(kite)] = m_aero_body outputs['aerodynamics']['mach' + str(kite)] = get_mach( options, atmos, air_velocity, q) outputs['aerodynamics']['reynolds' + str(kite)] = get_reynolds( options, atmos, air_velocity, q, parameters) return outputs
def generate_holonomic_constraints(architecture, outputs, variables, parameters, options): number_of_nodes = architecture.number_of_nodes parent_map = architecture.parent_map kite_nodes = architecture.kite_nodes # extract necessary SI variables xd_si = variables['SI']['xd'] xa_si = variables['SI']['xa'] # scaled variables struct var_scaled = variables['scaled'] if 'tether_length' not in list(outputs.keys()): outputs['tether_length'] = {} # build constraints with si variables g_dict = get_tether_length_constraint(options, variables['SI'], parameters, architecture) outputs['tether_length'].update(g_dict) g = [] gdot = [] gddot = [] holonomic_constraints = 0.0 for node in range(1, number_of_nodes): parent = parent_map[node] # chain rule, based on scaled variables g_local = g_dict['c' + str(node) + str(parent)] g.append(g_local) # first-order derivative dg_local = tools.time_derivative(g_local, variables, architecture) gdot.append(dg_local) # second-order derivative ddg_local = tools.time_derivative(dg_local, variables, architecture) gddot.append(ddg_local) # outputs['tether_length']['c' + str(node) + str(parent)] = g[-1] outputs['tether_length']['dc' + str(node) + str(parent)] = dg_local outputs['tether_length']['ddc' + str(node) + str(parent)] = ddg_local holonomic_constraints += xa_si['lambda{}{}'.format(node, parent)] * g_local # add cross-tethers if options['cross_tether'] and len(kite_nodes) > 1: g_dict = get_cross_tether_length_constraint(options, variables['SI'], parameters, architecture) outputs['tether_length'].update(g_dict) for l in architecture.layer_nodes: kite_children = architecture.kites_map[l] # dual kite system (per layer) only has one tether if len(kite_children) == 2: no_tethers = 1 else: no_tethers = len(kite_children) # add cross-tether constraints for k in range(no_tethers): # set-up relevant node numbers n01 = '{}{}'.format(kite_children[k], kite_children[(k + 1) % len(kite_children)]) length_constraint = g_dict['c{}'.format(n01)] # append constraint g_local = length_constraint g.append(length_constraint) dg_local = tools.time_derivative(g_local, variables, architecture) gdot.append(dg_local) ddg_local = tools.time_derivative(dg_local, variables, architecture) gddot.append(ddg_local) # save invariants to outputs outputs['tether_length']['dc{}'.format(n01)] = dg_local outputs['tether_length']['ddc{}'.format(n01)] = ddg_local # add to holonomic constraints holonomic_constraints += xa_si['lambda{}'.format(n01)] * g_local if node in kite_nodes: if 'r' + str(node) + str(parent) in list(xd_si.keys()): r = cas.reshape(var_scaled['xd', 'r' + str(node) + str(parent)], (3, 3)) orthonormality = cas.mtimes(r.T, r) - cas.DM_eye(3) orthonormality = cas.reshape(orthonormality, (9, 1)) outputs['tether_length']['orthonormality' + str(node) + str(parent)] = orthonormality dr_dt = variables['SI']['xddot']['dr' + str(node) + str(parent)] dr_dt = cas.reshape(dr_dt, (3, 3)) omega = variables['SI']['xd']['omega' + str(node) + str(parent)] omega_skew = vect_op.skew(omega) dr = cas.mtimes(r, omega_skew) rot_kinematics = dr_dt - dr rot_kinematics = cas.reshape(rot_kinematics, (9, 1)) outputs['tether_length']['rot_kinematics10'] = rot_kinematics g_cat = cas.vertcat(*g) gdot_cat = cas.vertcat(*gdot) gddot_cat = cas.vertcat(*gddot) return holonomic_constraints, outputs, g_cat, gdot_cat, gddot_cat
def get_cross_tether_length_constraint(options, vars_si, parameters, architecture): xd_si = vars_si['xd'] theta_si = vars_si['theta'] g_dict = {} kite_nodes = architecture.kite_nodes parent_map = architecture.parent_map if options['cross_tether'] and len(kite_nodes) > 1: for l in architecture.layer_nodes: kite_children = architecture.kites_map[l] # dual kite system (per layer) only has one tether if len(kite_children) == 2: no_tethers = 1 else: no_tethers = len(kite_children) # add cross-tether constraints for k in range(no_tethers): # set-up relevant node numbers n0 = '{}{}'.format(kite_children[k], parent_map[kite_children[k]]) n1 = '{}{}'.format(kite_children[(k + 1) % len(kite_children)], parent_map[kite_children[(k + 1) % len(kite_children)]]) n01 = '{}{}'.format(kite_children[k], kite_children[(k + 1) % len(kite_children)]) # center-of-mass attachment if options['tether']['cross_tether']['attachment'] == 'com': first_node = xd_si['q{}'.format(n0)] second_node = xd_si['q{}'.format(n1)] # stick or wing-tip attachment else: # only implemented for 6DOF if int(options['kite_dof']) == 6: # rotation matrices of relevant kites dcm_first = cas.reshape(xd_si['r{}'.format(n0)], (3, 3)) dcm_second = cas.reshape(xd_si['r{}'.format(n1)], (3, 3)) # stick: same attachment point as secondary tether if options['tether']['cross_tether']['attachment'] == 'stick': r_tether = parameters['theta0', 'geometry', 'r_tether'] # wing_tip: attachment half a wing span in negative span direction elif options['tether']['cross_tether']['attachment'] == 'wing_tip': r_tether = cas.vertcat(0.0, -parameters['theta0', 'geometry', 'b_ref'] / 2.0, 0.0) # unknown option notifier else: raise ValueError('Unknown cross-tether attachment option: {}'.format( options['tether']['cross_tether']['attachment'])) # create attachment nodes first_node = xd_si['q{}'.format(n0)] + cas.mtimes(dcm_first, r_tether) second_node = xd_si['q{}'.format(n1)] + cas.mtimes(dcm_second, r_tether) # not implemented for 3DOF elif int(options['kite_dof']) == 3: raise ValueError('Stick cross-tether attachment options not implemented for 3DOF kites') # cross-tether length segment_length = theta_si['l_c{}'.format(l)] # create constraint length_constraint = 0.5 * ( cas.mtimes( (first_node - second_node).T, (first_node - second_node)) - segment_length ** 2.0) g_dict['c{}'.format(n01)] = length_constraint return g_dict
def columnize(filament_list): dims = filament_list.shape columnized_list = cas.reshape(filament_list, (dims[0] * dims[1], 1)) return columnized_list
def get_uzero_matr_var(variables, parent): rot_cols = variables['xl']['uzero_matr' + str(parent)] rot_matr = cas.reshape(rot_cols, (3, 3)) return rot_matr
def initial_node_variables_for_standard_path(t, options, model, formulation, ret={}): trajectory_type = options['type'] number_of_nodes = model.architecture.number_of_nodes parent_map = model.architecture.parent_map kite_nodes = model.architecture.kite_nodes level_siblings = get_all_level_siblings(options, model) ua_norm = options['ua_norm'] kite_dof = model.kite_dof [height_list, radius, ehat_tether, ehat_side, ehat_up] = get_orbit_cone_parameters(options, model, ret['l_t']) for node in range(1, number_of_nodes): parent = parent_map[node] if parent == 0: parent_position = np.zeros((3, 1)) else: grandparent = parent_map[parent] parent_position = ret['q' + str(parent) + str(grandparent)] if not node in kite_nodes: ret['q' + str(node) + str(parent)] = get_tether_node_position( options, parent_position, node, ret['l_t']) ret['dq' + str(node) + str(parent)] = np.zeros((3, 1)) else: if parent == 0: height = height_list[0] else: height = height_list[1] omega_norm = ua_norm / radius omega_vector = ehat_tether * omega_norm psi = get_azimuthal_angle(t, level_siblings, node, parent, omega_norm) ehat_radial = ehat_side * np.cos(psi) + ehat_up * np.sin(psi) tether_vector = ehat_radial * radius + ehat_tether * height position = parent_position + tether_vector ehat_tangential = -1. * ehat_side * np.sin(psi) + ehat_up * np.cos( psi) velocity = ua_norm * ehat_tangential ehat1 = -1. * ehat_tangential ehat3 = ehat_tether ehat2 = vect_op.normed_cross(ehat3, ehat1) dcm = cas.horzcat(ehat1, ehat2, ehat3) dcm_column = cas.reshape(dcm, (9, 1)) ret['q' + str(node) + str(parent)] = position ret['dq' + str(node) + str(parent)] = velocity if int(kite_dof) == 6: ret['omega' + str(node) + str(parent)] = omega_vector ret['r' + str(node) + str(parent)] = dcm_column return ret
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 get_LL_matrix_var(variables, parent, label): LL_var = get_LL_var(variables, parent, label) LL_matr = cas.reshape(LL_var, (3, 3)) return LL_matr
def get_outputs(options, atmos, wind, variables, outputs, parameters, architecture): parent_map = architecture.parent_map kite_nodes = architecture.kite_nodes xd = variables['xd'] u = variables['u'] elevation_angle = indicators.get_elevation_angle(xd) for n in kite_nodes: parent = parent_map[n] # get relevant variables for kite n q = xd['q' + str(n) + str(parent)] dq = xd['dq' + str(n) + str(parent)] r = cas.reshape(xd['r' + str(n) + str(parent)], (3, 3)) ehat_span = r[:, 1] ehat_chord = r[:, 0] # wind parameters rho_infty = atmos.get_density(q[2]) uw_infty = wind.get_velocity(q[2]) # apparent air velocity if options['induction_model'] == 'actuator': ua = actuator_disk_flow.get_kite_effective_velocity( options, variables, wind, n, parent, architecture) else: ua = uw_infty - dq # relative air speed norm_ua_squared = cas.mtimes(ua.T, ua) ua_norm = norm_ua_squared**0.5 # angle of attack and sideslip angle alpha = indicators.get_alpha(ua, r) beta = indicators.get_beta(ua, r) if int(options['surface_control']) == 0: delta = u['delta' + str(n) + str(parent)] omega = xd['omega' + str(n) + str(parent)] [CF, CM] = stability_derivatives.stability_derivatives( options, alpha, beta, ua, omega, delta, parameters) elif int(options['surface_control']) == 1: delta = xd['delta' + str(n) + str(parent)] omega = xd['omega' + str(n) + str(parent)] [CF, CM] = stability_derivatives.stability_derivatives( options, alpha, beta, ua, omega, delta, parameters) else: raise ValueError('unsupported surface_control chosen: %i', options['surface_control']) # body-_frameforcecomponents # notice that these are unusual because an apparent wind reference coordinate system is in use. # see below (get_coeffs_from_control_surfaces) for information CA = CF[0] CY = CF[1] CN = CF[2] Cl = CM[0] Cm = CM[1] Cn = CM[2] dynamic_pressure = 1. / 2. * rho_infty * norm_ua_squared planform_area = parameters['theta0', 'geometry', 's_ref'] ftilde_aero = cas.mtimes(r, CF) f_aero = dynamic_pressure * planform_area * ftilde_aero ehat_drag = vect_op.normalize(ua) f_drag = cas.mtimes(cas.mtimes(f_aero.T, ehat_drag), ehat_drag) ehat_lift = vect_op.normed_cross(ua, ehat_span) f_lift = cas.mtimes(cas.mtimes(f_aero.T, ehat_lift), ehat_lift) f_side = f_aero - f_drag - f_lift drag_cross_lift = indicators.convert_from_body_to_wind_axes( alpha, beta, CF) CD = drag_cross_lift[0] CS = drag_cross_lift[1] CL = drag_cross_lift[2] b_ref = parameters['theta0', 'geometry', 'b_ref'] c_ref = parameters['theta0', 'geometry', 'c_ref'] reference_lengths = cas.diag(cas.vertcat(b_ref, c_ref, b_ref)) m_aero = dynamic_pressure * planform_area * cas.mtimes( reference_lengths, CM) aero_coefficients = {} aero_coefficients['CD'] = CD aero_coefficients['CS'] = CS aero_coefficients['CL'] = CL aero_coefficients['CA'] = CA aero_coefficients['CN'] = CN aero_coefficients['CY'] = CY aero_coefficients['Cl'] = Cl aero_coefficients['Cm'] = Cm aero_coefficients['Cn'] = Cn outputs = indicators.collect_kite_aerodynamics_outputs( options, atmos, ua, ua_norm, aero_coefficients, f_aero, f_lift, f_drag, f_side, m_aero, ehat_chord, ehat_span, r, q, n, outputs, parameters) outputs = indicators.collect_environmental_outputs( atmos, wind, q, n, outputs) outputs = indicators.collect_aero_validity_outputs( options, xd, ua, n, parent, outputs, parameters) outputs = indicators.collect_local_performance_outputs( options, atmos, wind, variables, CL, CD, elevation_angle, ua, n, parent, outputs, parameters) outputs = indicators.collect_power_balance_outputs( variables, n, outputs, architecture) return outputs
def write_data_row(pcdw, plot_dict, write_csv_dict, tgrid_ip, k, rotation_representation): """ Write one row of data into the .csv file :param pcdw: dictWriter object :param plot_dict: dictionary containing trial data :param write_csv_dict: csv helper dict used to write the trial data into the .csv :param k: time step in trajectory :return: None """ # loop over variables for variable_type in ['xd', 'xa', 'xl', 'u', 'outputs']: for variable in list(plot_dict[variable_type].keys()): # check whether sub_variables exist if type(plot_dict[variable_type][variable]) == dict: for sub_variable in list( plot_dict[variable_type][variable].keys()): var = plot_dict[variable_type][variable][sub_variable] variable_length = len(var) for index in range(variable_length): write_csv_dict[variable_type + '_' + variable + '_' + sub_variable + '_' + str(index)] = str( var[index][k]) # continue if no sub_variables exist else: # convert rotations from dcm to euler if variable[0] == 'r' and rotation_representation == 'euler': dcm = [] for i in range(9): dcm = cas.vertcat( dcm, plot_dict[variable_type][variable][i][k]) var = vect_op.rotationMatrixToEulerAngles( cas.reshape(dcm, 3, 3)) for index in range(3): write_csv_dict[variable_type + '_' + variable + '_' + str(index)] = str(var[index]) elif rotation_representation not in ['euler', 'dcm']: awelogger.logger.error( 'Error: Only euler angles and direct cosine matrix supported.' ) else: var = plot_dict[variable_type][variable] variable_length = len(var) for index in range(variable_length): write_csv_dict[variable_type + '_' + variable + '_' + str(index)] = str(var[index][k]) write_csv_dict['time'] = tgrid_ip[k] parent_map = plot_dict['architecture'].parent_map if k < plot_dict['architecture'].number_of_nodes - 1: node = list(parent_map.keys())[k] write_csv_dict['nodes'] = str(node) write_csv_dict['parent'] = str(parent_map[node]) if k < len(plot_dict['architecture'].kite_nodes): write_csv_dict['kites'] = plot_dict['architecture'].kite_nodes[k] else: write_csv_dict['kites'] = None else: write_csv_dict['nodes'] = None write_csv_dict['parent'] = None write_csv_dict['kites'] = None write_csv_dict['cross_tether'] = int( plot_dict['options']['user_options']['system_model']['cross_tether']) # write out sorted row ordered_dict = collections.OrderedDict( sorted(list(write_csv_dict.items()), key=lambda t: t[0])) pcdw.writerow(ordered_dict) return None