def get_planar_dmc(vec_u_eff, variables, kite, architecture): parent = architecture.parent_map[kite] # get relevant variables for kite n q = variables['xd']['q' + str(kite) + str(parent)] # in kite body: if parent > 0: grandparent = architecture.parent_map[parent] q_parent = variables['xd']['q' + str(parent) + str(grandparent)] else: q_parent = np.array([0., 0., 0.]) vec_t = q - q_parent # should be roughly "up-wards", ie, act like vec_w vec_v = vect_op.cross(vec_t, vec_u_eff) vec_w = vect_op.cross(vec_u_eff, vec_v) uhat = vect_op.smooth_normalize(vec_u_eff) vhat = vect_op.smooth_normalize(vec_v) what = vect_op.smooth_normalize(vec_w) planar_dcm = cas.horzcat(uhat, vhat, what) return planar_dcm
def filament(seg_data, epsilon=1.e-2): try: point_obs = seg_data[:3] point_1 = seg_data[3:6] point_2 = seg_data[6:9] Gamma = seg_data[9] vec_1 = point_obs - point_1 vec_2 = point_obs - point_2 vec_0 = point_2 - point_1 r1 = vect_op.smooth_norm(vec_1) r2 = vect_op.smooth_norm(vec_2) r0 = vect_op.smooth_norm(vec_0) factor = Gamma / (4. * np.pi) num = (r1 + r2) den_ori = (r1 * r2) * (r1 * r2 + cas.mtimes(vec_1.T, vec_2)) den_reg = (epsilon * r0)**2. den = den_ori + den_reg dir = vect_op.cross(vec_1, vec_2) scale = factor * num / den sol = dir * scale except: message = 'something went wrong while computing the filament biot-savart induction. proceed with zero induced velocity from this filament.' awelogger.logger.error(message) raise Exception(message) return sol
def get_plane_fit_n_vec(parent, variables, parameters, architecture): children = sorted(architecture.kites_map[parent]) kite0 = children[0] kite1 = children[1] kite2 = children[2] # there is a potential failure here if the order of the kite nodes is not increaseing from kite0 -> kite1 -> kite2, # where the direction of the cross-product flips, based on initialization where the lower number kites have a # smaller azimuthal angle. presumably, this should be resulved by the sorting above, but, just in case! if (kite0 > kite1) or (kite1 > kite2): awelogger.logger.warning( 'based on assignment order of kites, normal vector (by cross product) may point in reverse.' ) qkite0 = variables['xd']['q' + str(kite0) + str(parent)] qkite1 = variables['xd']['q' + str(kite1) + str(parent)] qkite2 = variables['xd']['q' + str(kite2) + str(parent)] arm1 = qkite1 - qkite0 arm2 = qkite2 - qkite0 n_vec = vect_op.cross(arm1, arm2) b_ref = parameters['theta0', 'geometry', 'b_ref'] varrho_temp = 8. scale = b_ref**2. * varrho_temp**2. n_vec = n_vec / scale return n_vec
def get_total_drag(model_options, diam, q_upper, q_lower, dq_upper, dq_lower, atmos, wind, cd_tether_fun): elem = model_options['tether']['aero_elements'] q_average = (q_upper + q_lower) / 2. total_force = np.zeros((3, 1)) total_moment = np.zeros((3, 1)) for idx in range(elem): loc_s_upper = float(idx + 1) / float(elem) loc_s_lower = float(idx) / float(elem) q_loc_upper = q_lower + loc_s_upper * (q_upper - q_lower) q_loc_lower = q_lower + loc_s_lower * (q_upper - q_lower) q_loc_average = (q_loc_lower + q_loc_upper)/2. moment_arm = q_average - q_loc_average dq_loc_upper = dq_lower + loc_s_upper * (dq_upper - dq_lower) dq_loc_lower = dq_lower + loc_s_lower * (dq_upper - dq_lower) loc_force = get_segment_force(diam, q_loc_upper, q_loc_lower, dq_loc_upper, dq_loc_lower, atmos, wind, cd_tether_fun) loc_moment = vect_op.cross(moment_arm, loc_force) total_force = total_force + loc_force total_moment = total_moment + loc_moment return [total_force, total_moment]
def approx_kite_tang_vector(variables, architecture, kite): parent = architecture.parent_map[kite] z_vec = tether_vector(variables, architecture, parent) r_vec = approx_kite_radius_vector(variables, architecture, kite) t_vec_dimensioned = vect_op.cross(z_vec, r_vec) return t_vec_dimensioned
def approx_kite_normal_vector(variables, architecture, kite): # z cross r = t # r x t = n r_vec = approx_kite_radius_vector(variables, architecture, kite) t_vec = approx_kite_tang_vector(variables, architecture, kite) n_vec = vect_op.cross(r_vec, t_vec) return n_vec
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 get_binormal_n_vec(parent, variables, parameters, architecture): children = architecture.kites_map[parent] n_vec = np.zeros((3, 1)) for kite in children: dqk = variables['xddot']['dq' + str(kite) + str(parent)] ddqk = variables['xddot']['ddq' + str(kite) + str(parent)] binormal_dim = vect_op.cross(dqk, ddqk) n_vec = n_vec + binormal_dim return n_vec
def get_actuator_moment(model_options, variables, outputs, parent, architecture): children = architecture.kites_map[parent] total_moment_aero = np.zeros((3, 1)) for kite in children: aero_force = outputs['aerodynamics']['f_aero_earth' + str(kite)] kite_radius = actuator_geom.get_kite_radius_vector( model_options, kite, variables, architecture) aero_moment = vect_op.cross(kite_radius, aero_force) total_moment_aero = total_moment_aero + aero_moment return total_moment_aero
def get_binormal_nhat(parent, variables, parameters, architecture): children = architecture.kites_map[parent] nvec = np.zeros((3, 1)) for kite in children: dqk = variables['xddot']['dq' + str(kite) + str(parent)] ddqk = variables['xddot']['ddq' + str(kite) + str(parent)] binormal_dim = vect_op.cross(dqk, ddqk) nvec = nvec + binormal_dim scale = 1.e-3 factor = scale * get_factor_var(parent, variables, parameters) nhat = nvec * factor return nhat
def get_element_drag_and_moment_fun(wind, atmos, cd_tether_fun): info_sym = cas.SX.sym('info_sym', (16, 1)) q_upper = info_sym[:3] q_lower = info_sym[3:6] dq_upper = info_sym[6:9] dq_lower = info_sym[9:12] diam = info_sym[12] q_seg_avg = info_sym[13:16] q_average = (q_upper + q_lower) / 2. zz = q_average[2] ua = get_uapp(q_upper, q_lower, dq_upper, dq_lower, wind) epsilon = 1.e-6 ua_norm = vect_op.smooth_norm(ua, epsilon) ehat_ua = vect_op.smooth_normalize(ua, epsilon) tether = q_upper - q_lower length_sq = cas.mtimes(tether.T, tether) length_parallel_to_wind = cas.mtimes(tether.T, ehat_ua) length_perp_to_wind = vect_op.smooth_sqrt( length_sq - length_parallel_to_wind**2., epsilon) re_number = reynolds.get_reynolds_number(atmos, ua, diam, q_upper, q_lower) cd = cd_tether_fun(re_number) density = atmos.get_density(zz) drag = cd * 0.5 * density * ua_norm * diam * length_perp_to_wind * ua moment_arm = q_average - q_seg_avg moment = vect_op.cross(moment_arm, drag) element_drag_fun = cas.Function('element_drag_fun', [info_sym], [drag]) element_moment_fun = cas.Function('element_moment_fun', [info_sym], [moment]) return element_drag_fun, element_moment_fun
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