From 70521ce8bf44e37aa4e93bc587be0f7b78a7ad9f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stefanie=20D=C3=BCssler?= Date: Thu, 19 Nov 2020 08:42:54 +0000 Subject: [PATCH 001/232] add: Generation file for a fuselage wing test case - test case for new feature which allows SHARPy to consider fuselage effects - includes input parameters and function to generate structural and aerodynamic input files - solver setup is still missing --- .../fuselage_wing/generate_fuselage_wing.py | 409 ++++++++++++++++++ 1 file changed, 409 insertions(+) create mode 100644 cases/coupled/fuselage_wing/generate_fuselage_wing.py diff --git a/cases/coupled/fuselage_wing/generate_fuselage_wing.py b/cases/coupled/fuselage_wing/generate_fuselage_wing.py new file mode 100644 index 000000000..f6df0c64f --- /dev/null +++ b/cases/coupled/fuselage_wing/generate_fuselage_wing.py @@ -0,0 +1,409 @@ +#! /usr/bin/env python3 +import h5py as h5 +import numpy as np +import os +import pandas as pd +import sharpy.utils.algebra as algebra +import matplotlib.pyplot as plt + + +case_name = 'simple_wing_fuselage' +route = os.path.dirname(os.path.realpath(__file__)) + '/' + +# EXECUTION +flow = ['BeamLoader', + 'AerogridLoader', + # 'NonLinearStatic', + 'StaticUvlm', + #'StaticTrim', + # 'StaticCoupled', + 'BeamLoads', + 'AerogridPlot', + 'BeamPlot', + #'DynamicCoupled', + # 'Modal', + # 'LinearAssember', + # 'AsymptoticStability', + ] + + +# FLIGHT CONDITIONS +# the simulation is set such that the aircraft flies at a u_inf velocity while +# the air is calm. +u_inf = 10 +rho = 1.225 + +free_flight = True +if not free_flight: + case_name += '_prescribed' + amplitude = 0*np.pi/180 + period = 3 + case_name += '_amp_' + str(amplitude).replace('.', '') + '_period_' + str(period) + +alpha = 6.0*np.pi/180 +beta = 0 +roll = 0 +gravity = 'on' +thrust = 0 +sigma = 1.5 + +# gust settings +gust_intensity = 0.20 +gust_length = 1*u_inf +gust_offset = 0.5*u_inf + +# numerics +n_step = 5 +structural_relaxation_factor = 0.6 +relaxation_factor = 0.35 +tolerance = 1e-6 +fsi_tolerance = 1e-4 + +num_cores = 2 + +# MODEL GEOMETRY +# beam +span_main = 3.0 +ea_main = 0.3 + +ea = 1e7 +ga = 1e5 +gj = 1e4 +eiy = 2e4 +eiz = 4e6 +m_bar_main = 0.75 +j_bar_main = 0.075 + +length_fuselage = 10 +offset_fuselage_vertical = 0 +offset_fuselage_wing = 4 +diameter_fuselage = 1.3333333333333333 +sigma_fuselage = 10 +m_bar_fuselage = 0.2 +j_bar_fuselage = 0.08 + +# lumped masses +n_lumped_mass = 1 +lumped_mass_nodes = np.zeros((n_lumped_mass, ), dtype=int) +lumped_mass = np.zeros((n_lumped_mass, )) +lumped_mass[0] = 50 +lumped_mass_inertia = np.zeros((n_lumped_mass, 3, 3)) +lumped_mass_position = np.zeros((n_lumped_mass, 3)) +lumped_mass_position[0] = offset_fuselage_wing + +# aero +chord_main = 1.0 + +# DISCRETISATION +# spatial discretisation +# chordiwse panels +m = 4 +# spanwise elements +n_elem_multiplier = 2 +n_elem_main = int(2*n_elem_multiplier) #int(4*n_elem_multiplier) +n_elem_fuselage = 21 +n_surfaces = 2 + +# temporal discretisation +physical_time = 1 +tstep_factor = 1. +dt = 1.0/m/u_inf*tstep_factor +n_tstep = round(physical_time/dt) + +# END OF INPUT----------------------------------------------------------------- + +# beam processing +n_node_elem = 3 + +# total number of elements +n_elem = 0 +n_elem += n_elem_main + n_elem_main +n_elem += n_elem_fuselage + +# number of nodes per part +n_node_main = n_elem_main*(n_node_elem - 1) + 1 +n_node_fuselage = n_elem_fuselage*(n_node_elem - 1) + 1 + +# total number of nodes +n_node = 0 +n_node += n_node_main + n_node_main - 1 +n_node += n_node_fuselage - 1 + +# stiffness and mass matrices +n_stiffness = 2 +base_stiffness_main = sigma*np.diag([ea, ga, ga, gj, eiy, eiz]) +base_stiffness_fuselage = base_stiffness_main.copy()*sigma_fuselage +base_stiffness_fuselage[4, 4] = base_stiffness_fuselage[5, 5] + +n_mass = 2 +base_mass_main = np.diag([m_bar_main, m_bar_main, m_bar_main, j_bar_main, 0.5*j_bar_main, 0.5*j_bar_main]) +base_mass_fuselage = np.diag([m_bar_fuselage, + m_bar_fuselage, + m_bar_fuselage, + j_bar_fuselage, + j_bar_fuselage*0.5, + j_bar_fuselage*0.5]) + +# PLACEHOLDERS +# beam +x = np.zeros((n_node, )) +y = np.zeros((n_node, )) +z = np.zeros((n_node, )) +beam_number = np.zeros((n_elem, ), dtype=int) +frame_of_reference_delta = np.zeros((n_elem, n_node_elem, 3)) +structural_twist = np.zeros((n_elem, 3)) +conn = np.zeros((n_elem, n_node_elem), dtype=int) +stiffness = np.zeros((n_stiffness, 6, 6)) +elem_stiffness = np.zeros((n_elem, ), dtype=int) +mass = np.zeros((n_mass, 6, 6)) +elem_mass = np.zeros((n_elem, ), dtype=int) +boundary_conditions = np.zeros((n_node, ), dtype=int) +app_forces = np.zeros((n_node, 6)) + + +# aero +airfoil_distribution = np.zeros((n_elem, n_node_elem), dtype=int) +surface_distribution = np.zeros((n_elem,), dtype=int) - 1 +surface_m = np.zeros((n_surfaces, ), dtype=int) +m_distribution = 'uniform' +aero_node = np.zeros((n_node,), dtype=bool) +twist = np.zeros((n_elem, n_node_elem)) +sweep = np.zeros((n_elem, n_node_elem)) +chord = np.zeros((n_elem, n_node_elem,)) +elastic_axis = np.zeros((n_elem, n_node_elem,)) + + +# FUNCTIONS------------------------------------------------------------- +def clean_test_files(): + fem_file_name = route + '/' + case_name + '.fem.h5' + if os.path.isfile(fem_file_name): + os.remove(fem_file_name) + + dyn_file_name = route + '/' + case_name + '.dyn.h5' + if os.path.isfile(dyn_file_name): + os.remove(dyn_file_name) + + aero_file_name = route + '/' + case_name + '.aero.h5' + if os.path.isfile(aero_file_name): + os.remove(aero_file_name) + + solver_file_name = route + '/' + case_name + '.sharpy' + if os.path.isfile(solver_file_name): + os.remove(solver_file_name) + + flightcon_file_name = route + '/' + case_name + '.flightcon.txt' + if os.path.isfile(flightcon_file_name): + os.remove(flightcon_file_name) + + +def find_index_of_closest_entry(array_values, target_value): + return (np.abs(array_values - target_value)).argmin() + + + +def generate_fem(): + stiffness[0, ...] = base_stiffness_main + stiffness[1, ...] = base_stiffness_fuselage + + mass[0, ...] = base_mass_main + mass[1, ...] = base_mass_fuselage + + we = 0 + wn = 0 + + # inner right wing + beam_number[we:we + n_elem_main] = 0 + x[wn:wn + n_node_main] = offset_fuselage_wing + y[wn:wn + n_node_main] = np.linspace(0, span_main, n_node_main) + y[wn:wn + n_node_main] += diameter_fuselage + for ielem in range(n_elem_main): + conn[we + ielem, :] = ((np.ones((3, ))*(we + ielem)*(n_node_elem - 1)) + + [0, 2, 1]) + for inode in range(n_node_elem): + frame_of_reference_delta[we + ielem, inode, :] = [-1.0, 0.0, 0.0] + + elem_stiffness[we:we + n_elem_main] = 0 + elem_mass[we:we + n_elem_main] = 0 + boundary_conditions[wn] = 1 + boundary_conditions[wn + n_elem_main] = -1 + we += n_elem_main + wn += n_node_main + + # inner left wing + beam_number[we:we + n_elem_main] = 1 + x[wn:wn + n_node_main] = offset_fuselage_wing + y[wn:wn + n_node_main] = np.linspace(0, -span_main, n_node_main) + y[wn:wn + n_node_main] -= diameter_fuselage + for ielem in range(n_elem_main): + conn[we + ielem, :] = ((np.ones((3, ))*(we+ielem)*(n_node_elem - 1)) + + 1 + [0, 2, 1]) + for inode in range(n_node_elem): + frame_of_reference_delta[we + ielem, inode, :] = [1.0, 0.0, 0.0] + elem_stiffness[we:we + n_elem_main] = 0 + elem_mass[we:we + n_elem_main] = 0 + boundary_conditions[wn] = 1 + boundary_conditions[wn + n_elem_main] = -1 + + we += n_elem_main + wn += n_node_main + + # fuselage + beam_number[we:we + n_elem_fuselage] = 2 + x[wn:wn + n_node_fuselage] = np.linspace(0.0, length_fuselage, n_node_fuselage-2) + z[wn:wn + n_node_fuselage] = np.linspace(0.0, offset_fuselage_vertical, n_node_fuselage-2) + + # adjust node closes to fuselage wing junction to be in the same z-y-plane than wing nodes + idx_fuselage_wing_junction = find_index_of_closest_entry(x[wn:wn + n_node_fuselage - 3], offset_fuselage_wing) + z[idx_fuselage_wing_junction] = np.interp(offset_fuselage_wing, x[wn:wn + n_node_fuselage - 3], z[wn:wn + n_node_fuselage - 3]) + x[idx_fuselage_wing_junction] = offset_fuselage_wing + + for ielem in range(n_elem_fuselage-1): + conn[we + ielem, :] = ((np.ones((3,))*(we + ielem)*(n_node_elem - 1)) + + 2 + [0, 2, 1]) + for inode in range(n_node_elem): + frame_of_reference_delta[we + ielem, inode, :] = [0.0, 1.0, 0.0] + conn[we+n_elem_fuselage-1,:] = np.array([conn[0,0],conn[n_elem_main,0],idx_fuselage_wing_junction]) + for inode in range(n_node_elem): + # TO-DO: Correct reference frame for wing junction beam + frame_of_reference_delta[we+n_elem_fuselage-1, inode, :] = [0.0, 1.0, 0.0] + elem_stiffness[we:we + n_elem_fuselage] = 1 + elem_mass[we:we + n_elem_fuselage] = 1 + + boundary_conditions[wn] = -1 + boundary_conditions[idx_fuselage_wing_junction] = 1 + boundary_conditions[wn + n_elem_main] = -1 + + we += n_elem_fuselage + wn += n_node_fuselage + + with h5.File(route + '/' + case_name + '.fem.h5', 'a') as h5file: + coordinates = h5file.create_dataset('coordinates', data=np.column_stack((x, y, z))) + conectivities = h5file.create_dataset('connectivities', data=conn) + num_nodes_elem_handle = h5file.create_dataset( + 'num_node_elem', data=n_node_elem) + num_nodes_handle = h5file.create_dataset( + 'num_node', data=n_node) + num_elem_handle = h5file.create_dataset( + 'num_elem', data=n_elem) + stiffness_db_handle = h5file.create_dataset( + 'stiffness_db', data=stiffness) + stiffness_handle = h5file.create_dataset( + 'elem_stiffness', data=elem_stiffness) + mass_db_handle = h5file.create_dataset( + 'mass_db', data=mass) + mass_handle = h5file.create_dataset( + 'elem_mass', data=elem_mass) + frame_of_reference_delta_handle = h5file.create_dataset( + 'frame_of_reference_delta', data=frame_of_reference_delta) + structural_twist_handle = h5file.create_dataset( + 'structural_twist', data=structural_twist) + bocos_handle = h5file.create_dataset( + 'boundary_conditions', data=boundary_conditions) + beam_handle = h5file.create_dataset( + 'beam_number', data=beam_number) + app_forces_handle = h5file.create_dataset( + 'app_forces', data=app_forces) + lumped_mass_nodes_handle = h5file.create_dataset( + 'lumped_mass_nodes', data=lumped_mass_nodes) + lumped_mass_handle = h5file.create_dataset( + 'lumped_mass', data=lumped_mass) + lumped_mass_inertia_handle = h5file.create_dataset( + 'lumped_mass_inertia', data=lumped_mass_inertia) + lumped_mass_position_handle = h5file.create_dataset( + 'lumped_mass_position', data=lumped_mass_position) + +def generate_aero_file(): + global x, y, z + + we = 0 + wn = 0 + # right wing (surface 0, beam 0) + i_surf = 0 + airfoil_distribution[we:we + n_elem_main, :] = 0 + surface_distribution[we:we + n_elem_main] = i_surf + surface_m[i_surf] = m + aero_node[wn:wn + n_node_main] = True + temp_chord = np.linspace(chord_main, chord_main, n_node_main) + temp_sweep = np.linspace(0.0, 0*np.pi/180, n_node_main) + node_counter = 0 + for i_elem in range(we, we + n_elem_main): + for i_local_node in range(n_node_elem): + if not i_local_node == 0: + node_counter += 1 + chord[i_elem, i_local_node] = temp_chord[node_counter] + elastic_axis[i_elem, i_local_node] = ea_main + sweep[i_elem, i_local_node] = temp_sweep[node_counter] + + we += n_elem_main + wn += n_node_main + + # left wing (surface 1, beam 1) + i_surf = 1 + airfoil_distribution[we:we + n_elem_main, :] = 0 + surface_distribution[we:we + n_elem_main] = i_surf + surface_m[i_surf] = m + aero_node[wn:wn + n_node_main] = True + temp_chord = np.linspace(chord_main, chord_main, n_node_main) + node_counter = 0 + for i_elem in range(we, we + n_elem_main): + for i_local_node in range(n_node_elem): + if not i_local_node == 0: + node_counter += 1 + chord[i_elem, i_local_node] = temp_chord[node_counter] + elastic_axis[i_elem, i_local_node] = ea_main + sweep[i_elem, i_local_node] = -temp_sweep[node_counter] + + we += n_elem_main + wn += n_node_main - 1 + + # fuselage + we += n_elem_fuselage + wn += n_node_fuselage - 1 + + with h5.File(route + '/' + case_name + '.aero.h5', 'a') as h5file: + airfoils_group = h5file.create_group('airfoils') + # add one airfoil + naca_airfoil_main = airfoils_group.create_dataset('0', data=np.column_stack( + generate_naca_camber(P=0, M=0))) + + # chord + chord_input = h5file.create_dataset('chord', data=chord) + dim_attr = chord_input .attrs['units'] = 'm' + + # twist + twist_input = h5file.create_dataset('twist', data=twist) + dim_attr = twist_input.attrs['units'] = 'rad' + + # sweep + sweep_input = h5file.create_dataset('sweep', data=sweep) + dim_attr = sweep_input.attrs['units'] = 'rad' + + # airfoil distribution + airfoil_distribution_input = h5file.create_dataset('airfoil_distribution', data=airfoil_distribution) + + surface_distribution_input = h5file.create_dataset('surface_distribution', data=surface_distribution) + surface_m_input = h5file.create_dataset('surface_m', data=surface_m) + m_distribution_input = h5file.create_dataset('m_distribution', data=m_distribution.encode('ascii', 'ignore')) + + aero_node_input = h5file.create_dataset('aero_node', data=aero_node) + elastic_axis_input = h5file.create_dataset('elastic_axis', data=elastic_axis) + +def generate_naca_camber(M=0, P=0): + mm = M*1e-2 + p = P*1e-1 + + def naca(x, mm, p): + if x < 1e-6: + return 0.0 + elif x < p: + return mm/(p*p)*(2*p*x - x*x) + elif x > p and x < 1+1e-6: + return mm/((1-p)*(1-p))*(1 - 2*p + 2*p*x - x*x) + + x_vec = np.linspace(0, 1, 1000) + y_vec = np.array([naca(x, mm, p) for x in x_vec]) + return x_vec, y_vec + +clean_test_files() +generate_fem() +generate_aero_file() \ No newline at end of file From b7fd5d658f37964f4cfbed4883656dd3260a5462 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stefanie=20D=C3=BCssler?= Date: Wed, 25 Nov 2020 14:04:32 +0000 Subject: [PATCH 002/232] add: h5 file generation for nonlifting bodies - stores input parameter for later grid generation in h5 file - input radius for each specific beam node is generated automatically --- .../fuselage_wing/generate_fuselage_wing.py | 83 ++++++++++++++++++- 1 file changed, 80 insertions(+), 3 deletions(-) diff --git a/cases/coupled/fuselage_wing/generate_fuselage_wing.py b/cases/coupled/fuselage_wing/generate_fuselage_wing.py index f6df0c64f..dceab57bd 100644 --- a/cases/coupled/fuselage_wing/generate_fuselage_wing.py +++ b/cases/coupled/fuselage_wing/generate_fuselage_wing.py @@ -77,7 +77,8 @@ length_fuselage = 10 offset_fuselage_vertical = 0 offset_fuselage_wing = 4 -diameter_fuselage = 1.3333333333333333 +radius_fuselage = 1.3333333333333333/2 +list_cylinder_position_fuselage = [0.3, 0.7] # percent where fuselage has cylinder shape sigma_fuselage = 10 m_bar_fuselage = 0.2 j_bar_fuselage = 0.08 @@ -167,10 +168,17 @@ surface_m = np.zeros((n_surfaces, ), dtype=int) m_distribution = 'uniform' aero_node = np.zeros((n_node,), dtype=bool) +nonlifting_body_node = np.zeros((n_node,), dtype=bool) twist = np.zeros((n_elem, n_node_elem)) sweep = np.zeros((n_elem, n_node_elem)) chord = np.zeros((n_elem, n_node_elem,)) elastic_axis = np.zeros((n_elem, n_node_elem,)) +boundary_conditions_aero = np.zeros((n_node, ), dtype=int) + +# nonlifting body +nonlifting_body_distribution = np.zeros((n_elem,), dtype=int) - 1 +nonlifting_body_m = np.zeros((n_nonlifting_bodies, ), dtype=int) +radius = np.zeros((n_node,)) # FUNCTIONS------------------------------------------------------------- @@ -233,7 +241,7 @@ def generate_fem(): beam_number[we:we + n_elem_main] = 1 x[wn:wn + n_node_main] = offset_fuselage_wing y[wn:wn + n_node_main] = np.linspace(0, -span_main, n_node_main) - y[wn:wn + n_node_main] -= diameter_fuselage + y[wn:wn + n_node_main] -= radius_fuselage for ielem in range(n_elem_main): conn[we + ielem, :] = ((np.ones((3, ))*(we+ielem)*(n_node_elem - 1)) + 1 + [0, 2, 1]) @@ -388,6 +396,39 @@ def generate_aero_file(): aero_node_input = h5file.create_dataset('aero_node', data=aero_node) elastic_axis_input = h5file.create_dataset('elastic_axis', data=elastic_axis) + +def generate_nonlifting_body_file(): + we = 0 + wn = 0 + + # right wing + nonlifting_body_node[wn:wn + n_node_main] = False + we += n_elem_main + wn += n_node_main + + # left wing + nonlifting_body_node[wn:wn + n_node_main] = False + we += n_elem_main + wn += n_node_main + + #fuselage (beam?, body ID = 0) + i_body = 0 + nonlifting_body_node[wn:wn + n_node_fuselage] = True + nonlifting_body_distribution[wn:wn + n_node_fuselage] = i_body + nonlifting_body_m[i_body] = m_radial_elem_fuselage + radius[wn:wn + n_node_fuselage] = create_fuselage_geometry() + + with h5.File(route + '/' + case_name + '.nonlifting_body.h5', 'a') as h5file: + nonlifting_body_m_input = h5file.create_dataset('nonlifting_body_m', data=nonlifting_body_m) + nonlifting_body_node_input = h5file.create_dataset('nonlifting_body_node', data=nonlifting_body_node) + + nonlifting_body_distribution_input = h5file.create_dataset('nonlifting_body_distribution', data=nonlifting_body_distribution) + + # radius + radius_input = h5file.create_dataset('radius', data=radius) + dim_attr = radius_input.attrs['units'] = 'm' + + # right wing (surface 0, beam 0) def generate_naca_camber(M=0, P=0): mm = M*1e-2 p = P*1e-1 @@ -404,6 +445,42 @@ def naca(x, mm, p): y_vec = np.array([naca(x, mm, p) for x in x_vec]) return x_vec, y_vec +def find_index_of_closest_entry(array_values, target_value): + return (np.abs(array_values - target_value)).argmin() + +def create_ellipsoid(x_geom, a, b, flip): + x_geom -= x_geom.min() + y = b*np.sqrt(1-(x_geom/a)**2) + if flip: + y = np.flip(y.tolist()) + return y + +def add_nose_or_tail_shape(idx, array_x, nose = True): + if nose: + shape = create_ellipsoid(array_x[:idx], array_x[idx] - array_x[0], radius_fuselage, True) + if not nose: + #TO-DO: Add paraboloid shaped tail + shape = create_ellipsoid(array_x[idx:], array_x[-1]-array_x[idx], radius_fuselage, False) + return shape + +def create_fuselage_geometry(): + array_radius = np.zeros((sum(nonlifting_body_node))) + x_fuselage = x[nonlifting_body_node] + fuselage_length = max(x_fuselage)-min(x_fuselage) # useful?? + idx_cylinder_start = find_index_of_closest_entry(x_fuselage, list_cylinder_position_fuselage[0]*fuselage_length) + idx_cylinder_end = find_index_of_closest_entry(x_fuselage,list_cylinder_position_fuselage[1]*fuselage_length) + # set constant radius of cylinder + array_radius[idx_cylinder_start:idx_cylinder_end] = radius_fuselage + # set r(x) for nose and tail region + array_radius[:idx_cylinder_start] = add_nose_or_tail_shape(idx_cylinder_start, x_fuselage, nose = True) + array_radius[idx_cylinder_end:] = add_nose_or_tail_shape(idx_cylinder_end, x_fuselage, nose = False) + # ensure radius = 0 at nose/tail + array_radius[0] = 0 + array_radius[-1] = 0 + return array_radius + + clean_test_files() generate_fem() -generate_aero_file() \ No newline at end of file +generate_aero_file() +generate_nonlifting_body_file() \ No newline at end of file From 277d5e3f4e79e9fb44129f6d4ee961fc4a3449a2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stefanie=20D=C3=BCssler?= Date: Wed, 25 Nov 2020 14:06:23 +0000 Subject: [PATCH 003/232] add: Aerodynamic boundary conditions as input - aerodynamic boundary conditions for fuselage wing junctions are stored in h5 aero file for later use in SHARPy --- cases/coupled/fuselage_wing/generate_fuselage_wing.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/cases/coupled/fuselage_wing/generate_fuselage_wing.py b/cases/coupled/fuselage_wing/generate_fuselage_wing.py index dceab57bd..9f57e2acb 100644 --- a/cases/coupled/fuselage_wing/generate_fuselage_wing.py +++ b/cases/coupled/fuselage_wing/generate_fuselage_wing.py @@ -327,6 +327,7 @@ def generate_aero_file(): wn = 0 # right wing (surface 0, beam 0) i_surf = 0 + boundary_conditions_aero[wn] = 1 # BC at fuselage junction that Zirkulation = Zirkulation airfoil_distribution[we:we + n_elem_main, :] = 0 surface_distribution[we:we + n_elem_main] = i_surf surface_m[i_surf] = m @@ -347,6 +348,7 @@ def generate_aero_file(): # left wing (surface 1, beam 1) i_surf = 1 + boundary_conditions_aero[wn] = 1 # BC at fuselage junction airfoil_distribution[we:we + n_elem_main, :] = 0 surface_distribution[we:we + n_elem_main] = i_surf surface_m[i_surf] = m @@ -396,6 +398,8 @@ def generate_aero_file(): aero_node_input = h5file.create_dataset('aero_node', data=aero_node) elastic_axis_input = h5file.create_dataset('elastic_axis', data=elastic_axis) + bocos_handle = h5file.create_dataset( + 'boundary_conditions', data=boundary_conditions_aero) def generate_nonlifting_body_file(): we = 0 From 93689da93ff4bf6c31511d00ef0afddba4734496 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stefanie=20D=C3=BCssler?= Date: Wed, 25 Nov 2020 14:07:40 +0000 Subject: [PATCH 004/232] refactor: function clean_test_files --- .../fuselage_wing/generate_fuselage_wing.py | 28 ++++--------------- 1 file changed, 6 insertions(+), 22 deletions(-) diff --git a/cases/coupled/fuselage_wing/generate_fuselage_wing.py b/cases/coupled/fuselage_wing/generate_fuselage_wing.py index 9f57e2acb..915430187 100644 --- a/cases/coupled/fuselage_wing/generate_fuselage_wing.py +++ b/cases/coupled/fuselage_wing/generate_fuselage_wing.py @@ -183,32 +183,16 @@ # FUNCTIONS------------------------------------------------------------- def clean_test_files(): - fem_file_name = route + '/' + case_name + '.fem.h5' - if os.path.isfile(fem_file_name): - os.remove(fem_file_name) - - dyn_file_name = route + '/' + case_name + '.dyn.h5' - if os.path.isfile(dyn_file_name): - os.remove(dyn_file_name) - - aero_file_name = route + '/' + case_name + '.aero.h5' - if os.path.isfile(aero_file_name): - os.remove(aero_file_name) - - solver_file_name = route + '/' + case_name + '.sharpy' - if os.path.isfile(solver_file_name): - os.remove(solver_file_name) - - flightcon_file_name = route + '/' + case_name + '.flightcon.txt' - if os.path.isfile(flightcon_file_name): - os.remove(flightcon_file_name) - + list_file_extension = ['.fem.h5', '.dyn.h5', '.aero.h5', + '.nonlifting_body.h5', '.sharpy', '.flightcon.txt'] + for file_extension in list_file_extension: + file = route + '/' + case_name + file_extension + if os.path.isfile(file): + os.remove(file) def find_index_of_closest_entry(array_values, target_value): return (np.abs(array_values - target_value)).argmin() - - def generate_fem(): stiffness[0, ...] = base_stiffness_main stiffness[1, ...] = base_stiffness_fuselage From 034cb0215853d3aa8fb0a279dc3c3437c049086d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stefanie=20D=C3=BCssler?= Date: Thu, 26 Nov 2020 12:58:36 +0000 Subject: [PATCH 005/232] fix: add or rename missing parameters --- cases/coupled/fuselage_wing/generate_fuselage_wing.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/cases/coupled/fuselage_wing/generate_fuselage_wing.py b/cases/coupled/fuselage_wing/generate_fuselage_wing.py index 915430187..40ca13744 100644 --- a/cases/coupled/fuselage_wing/generate_fuselage_wing.py +++ b/cases/coupled/fuselage_wing/generate_fuselage_wing.py @@ -99,11 +99,13 @@ # spatial discretisation # chordiwse panels m = 4 +m_radial_elem_fuselage = 12 # spanwise elements n_elem_multiplier = 2 n_elem_main = int(2*n_elem_multiplier) #int(4*n_elem_multiplier) n_elem_fuselage = 21 n_surfaces = 2 +n_nonlifting_bodies = 1 # temporal discretisation physical_time = 1 @@ -207,7 +209,7 @@ def generate_fem(): beam_number[we:we + n_elem_main] = 0 x[wn:wn + n_node_main] = offset_fuselage_wing y[wn:wn + n_node_main] = np.linspace(0, span_main, n_node_main) - y[wn:wn + n_node_main] += diameter_fuselage + y[wn:wn + n_node_main] += radius_fuselage for ielem in range(n_elem_main): conn[we + ielem, :] = ((np.ones((3, ))*(we + ielem)*(n_node_elem - 1)) + [0, 2, 1]) From 29e89f000ca94a184235e51a9d9fa3cd29c5ec2e Mon Sep 17 00:00:00 2001 From: asd Date: Fri, 4 Dec 2020 09:22:18 +0000 Subject: [PATCH 006/232] fix: nonlifting body distribution is saved per element and not per node --- cases/coupled/fuselage_wing/generate_fuselage_wing.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cases/coupled/fuselage_wing/generate_fuselage_wing.py b/cases/coupled/fuselage_wing/generate_fuselage_wing.py index 40ca13744..51c9f844c 100644 --- a/cases/coupled/fuselage_wing/generate_fuselage_wing.py +++ b/cases/coupled/fuselage_wing/generate_fuselage_wing.py @@ -404,7 +404,7 @@ def generate_nonlifting_body_file(): #fuselage (beam?, body ID = 0) i_body = 0 nonlifting_body_node[wn:wn + n_node_fuselage] = True - nonlifting_body_distribution[wn:wn + n_node_fuselage] = i_body + nonlifting_body_distribution[we:we + n_elem_fuselage] = i_body nonlifting_body_m[i_body] = m_radial_elem_fuselage radius[wn:wn + n_node_fuselage] = create_fuselage_geometry() From 323724f09728f5404c8ac0896f2fd34d2158e69d Mon Sep 17 00:00:00 2001 From: asd Date: Fri, 4 Dec 2020 09:23:42 +0000 Subject: [PATCH 007/232] Add: add dynamic file configuration and solver settings - settings are copied and pasted from simple HALE case for the beginning --- .../fuselage_wing/generate_fuselage_wing.py | 266 +++++++++++++++++- 1 file changed, 265 insertions(+), 1 deletion(-) diff --git a/cases/coupled/fuselage_wing/generate_fuselage_wing.py b/cases/coupled/fuselage_wing/generate_fuselage_wing.py index 51c9f844c..7c7f3b0eb 100644 --- a/cases/coupled/fuselage_wing/generate_fuselage_wing.py +++ b/cases/coupled/fuselage_wing/generate_fuselage_wing.py @@ -470,7 +470,271 @@ def create_fuselage_geometry(): return array_radius +def generate_dyn_file(): + global dt + global n_tstep + global route + global case_name + global num_elem + global num_node_elem + global num_node + global amplitude + global period + global free_flight + + dynamic_forces_time = None + with_dynamic_forces = False + with_forced_vel = False + if not free_flight: + with_forced_vel = True + + if with_dynamic_forces: + f1 = 100 + dynamic_forces = np.zeros((num_node, 6)) + app_node = [int(num_node_main - 1), int(num_node_main)] + dynamic_forces[app_node, 2] = f1 + force_time = np.zeros((n_tstep, )) + limit = round(0.05/dt) + force_time[50:61] = 1 + + dynamic_forces_time = np.zeros((n_tstep, num_node, 6)) + for it in range(n_tstep): + dynamic_forces_time[it, :, :] = force_time[it]*dynamic_forces + + forced_for_vel = None + if with_forced_vel: + forced_for_vel = np.zeros((n_tstep, 6)) + forced_for_acc = np.zeros((n_tstep, 6)) + for it in range(n_tstep): + # if dt*it < period: + # forced_for_vel[it, 2] = 2*np.pi/period*amplitude*np.sin(2*np.pi*dt*it/period) + # forced_for_acc[it, 2] = (2*np.pi/period)**2*amplitude*np.cos(2*np.pi*dt*it/period) + + forced_for_vel[it, 3] = 2*np.pi/period*amplitude*np.sin(2*np.pi*dt*it/period) + forced_for_acc[it, 3] = (2*np.pi/period)**2*amplitude*np.cos(2*np.pi*dt*it/period) + + if with_dynamic_forces or with_forced_vel: + with h5.File(route + '/' + case_name + '.dyn.h5', 'a') as h5file: + if with_dynamic_forces: + h5file.create_dataset( + 'dynamic_forces', data=dynamic_forces_time) + if with_forced_vel: + h5file.create_dataset( + 'for_vel', data=forced_for_vel) + h5file.create_dataset( + 'for_acc', data=forced_for_acc) + h5file.create_dataset( + 'num_steps', data=n_tstep) + + +def generate_solver_file(): + file_name = route + '/' + case_name + '.sharpy' + settings = dict() + settings['SHARPy'] = {'case': case_name, + 'route': route, + 'flow': flow, + 'write_screen': 'on', + 'write_log': 'on', + 'log_folder': route + '/output/', + 'log_file': case_name + '.log'} + + settings['BeamLoader'] = {'unsteady': 'on', + 'orientation': algebra.euler2quat(np.array([roll, + alpha, + beta]))} + settings['AerogridLoader'] = {'unsteady': 'on', + 'aligned_grid': 'on', + 'mstar': int(20/tstep_factor), + 'freestream_dir': ['1', '0', '0']} + + settings['NonLinearStatic'] = {'print_info': 'off', + 'max_iterations': 150, + 'num_load_steps': 1, + 'delta_curved': 1e-1, + 'min_delta': tolerance, + 'gravity_on': gravity, + 'gravity': 9.81} + + settings['StaticUvlm'] = {'print_info': 'on', + 'horseshoe': 'off', + 'num_cores': num_cores, + 'n_rollup': 0, + 'rollup_dt': dt, + 'rollup_aic_refresh': 1, + 'rollup_tolerance': 1e-4, + 'velocity_field_generator': 'SteadyVelocityField', + 'velocity_field_input': {'u_inf': u_inf, + 'u_inf_direction': [1., 0, 0]}, + 'rho': rho} + + settings['StaticCoupled'] = {'print_info': 'off', + 'structural_solver': 'NonLinearStatic', + 'structural_solver_settings': settings['NonLinearStatic'], + 'aero_solver': 'StaticUvlm', + 'aero_solver_settings': settings['StaticUvlm'], + 'max_iter': 100, + 'n_load_steps': n_step, + 'tolerance': fsi_tolerance, + 'relaxation_factor': structural_relaxation_factor} + + settings['StaticTrim'] = {'solver': 'StaticCoupled', + 'solver_settings': settings['StaticCoupled'], + 'initial_alpha': alpha, + 'initial_deflection': 0, + 'initial_thrust': thrust} + + settings['NonLinearDynamicCoupledStep'] = {'print_info': 'off', + 'max_iterations': 950, + 'delta_curved': 1e-1, + 'min_delta': tolerance, + 'newmark_damp': 5e-3, + 'gravity_on': gravity, + 'gravity': 9.81, + 'num_steps': n_tstep, + 'dt': dt, + 'initial_velocity': u_inf} + + settings['NonLinearDynamicPrescribedStep'] = {'print_info': 'off', + 'max_iterations': 950, + 'delta_curved': 1e-1, + 'min_delta': tolerance, + 'newmark_damp': 5e-3, + 'gravity_on': gravity, + 'gravity': 9.81, + 'num_steps': n_tstep, + 'dt': dt, + 'initial_velocity': u_inf*int(free_flight)} + + relative_motion = 'off' + if not free_flight: + relative_motion = 'on' + settings['StepUvlm'] = {'print_info': 'off', + 'horseshoe': 'off', + 'num_cores': num_cores, + 'n_rollup': 0, + 'convection_scheme': 2, + 'rollup_dt': dt, + 'rollup_aic_refresh': 1, + 'rollup_tolerance': 1e-4, + 'gamma_dot_filtering': 6, + 'velocity_field_generator': 'GustVelocityField', + 'velocity_field_input': {'u_inf': int(not free_flight)*u_inf, + 'u_inf_direction': [1., 0, 0], + 'gust_shape': '1-cos', + 'gust_length': gust_length, + 'gust_intensity': gust_intensity*u_inf, + 'offset': gust_offset, + 'span': span_main, + 'relative_motion': relative_motion}, + 'rho': rho, + 'n_time_steps': n_tstep, + 'dt': dt} + + if free_flight: + solver = 'NonLinearDynamicCoupledStep' + else: + solver = 'NonLinearDynamicPrescribedStep' + settings['DynamicCoupled'] = {'structural_solver': solver, + 'structural_solver_settings': settings[solver], + 'aero_solver': 'StepUvlm', + 'aero_solver_settings': settings['StepUvlm'], + 'fsi_substeps': 200, + 'fsi_tolerance': fsi_tolerance, + 'relaxation_factor': relaxation_factor, + 'minimum_steps': 1, + 'relaxation_steps': 150, + 'final_relaxation_factor': 0.5, + 'n_time_steps': n_tstep, + 'dt': dt, + 'include_unsteady_force_contribution': 'on', + 'postprocessors': ['BeamLoads', 'BeamPlot', 'AerogridPlot'], + 'postprocessors_settings': {'BeamLoads': {'folder': route + '/output/', + 'csv_output': 'off'}, + 'BeamPlot': {'folder': route + '/output/', + 'include_rbm': 'on', + 'include_applied_forces': 'on'}, + 'AerogridPlot': { + 'folder': route + '/output/', + 'include_rbm': 'on', + 'include_applied_forces': 'on', + 'minus_m_star': 0}, + }} + + settings['BeamLoads'] = {'folder': route + '/output/', + 'csv_output': 'off'} + + settings['BeamPlot'] = {'folder': route + '/output/', + 'include_rbm': 'on', + 'include_applied_forces': 'on', + 'include_forward_motion': 'on'} + + settings['AerogridPlot'] = {'folder': route + '/output/', + 'include_rbm': 'on', + 'include_forward_motion': 'off', + 'include_applied_forces': 'on', + 'minus_m_star': 0, + 'u_inf': u_inf, + 'dt': dt} + + settings['Modal'] = {'print_info': True, + 'use_undamped_modes': True, + 'NumLambda': 30, + 'rigid_body_modes': True, + 'write_modes_vtk': 'on', + 'print_matrices': 'on', + 'write_data': 'on', + 'continuous_eigenvalues': 'off', + 'dt': dt, + 'plot_eigenvalues': False} + + settings['LinearAssembler'] = {'linear_system': 'LinearAeroelastic', + 'linear_system_settings': { + 'beam_settings': {'modal_projection': False, + 'inout_coords': 'nodes', + 'discrete_time': True, + 'newmark_damp': 0.05, + 'discr_method': 'newmark', + 'dt': dt, + 'proj_modes': 'undamped', + 'use_euler': 'off', + 'num_modes': 40, + 'print_info': 'on', + 'gravity': 'on', + 'remove_dofs': []}, + 'aero_settings': {'dt': dt, + 'integr_order': 2, + 'density': rho, + 'remove_predictor': False, + 'use_sparse': True, + 'rigid_body_motion': free_flight, + 'use_euler': False, + 'remove_inputs': ['u_gust']}, + 'rigid_body_motion': free_flight}} + + settings['AsymptoticStability'] = {'sys_id': 'LinearAeroelastic', + 'print_info': 'on', + 'modes_to_plot': [], + 'display_root_locus': 'off', + 'frequency_cutoff': 0, + 'export_eigenvalues': 'off', + 'num_evals': 40, + 'folder': route + '/output/'} + + + import configobj + config = configobj.ConfigObj() + config.filename = file_name + for k, v in settings.items(): + config[k] = v + config.write() + + + clean_test_files() generate_fem() generate_aero_file() -generate_nonlifting_body_file() \ No newline at end of file +generate_nonlifting_body_file() +generate_solver_file() +generate_dyn_file() + + From 8f85aeadd246f342dffef8e58ab631b35dc783e7 Mon Sep 17 00:00:00 2001 From: asd Date: Fri, 4 Dec 2020 12:54:22 +0000 Subject: [PATCH 008/232] refactor: remove matplotlib library - remove library as it is unused and might cause problems with Travis --- cases/coupled/fuselage_wing/generate_fuselage_wing.py | 1 - 1 file changed, 1 deletion(-) diff --git a/cases/coupled/fuselage_wing/generate_fuselage_wing.py b/cases/coupled/fuselage_wing/generate_fuselage_wing.py index 7c7f3b0eb..80cf0d856 100644 --- a/cases/coupled/fuselage_wing/generate_fuselage_wing.py +++ b/cases/coupled/fuselage_wing/generate_fuselage_wing.py @@ -4,7 +4,6 @@ import os import pandas as pd import sharpy.utils.algebra as algebra -import matplotlib.pyplot as plt case_name = 'simple_wing_fuselage' From c2ac8a4fba1ac14d103cb8df28552230836c299c Mon Sep 17 00:00:00 2001 From: asd Date: Fri, 4 Dec 2020 13:10:58 +0000 Subject: [PATCH 009/232] add: grid and nonlifting_body_grid class - nonlifting_body_grid contains similiar to Aerogrid all information of the grid of panels but for nonlifting bodies/surfaces instead of lifting surfaces - grid class serves as a parent class from which nonlifting_body_grid is inheritated from, and later also Aerogrid --- sharpy/aero/models/grid.py | 177 +++++++++++++++++++++ sharpy/aero/models/nonlifting_body_grid.py | 155 ++++++++++++++++++ 2 files changed, 332 insertions(+) create mode 100644 sharpy/aero/models/grid.py create mode 100644 sharpy/aero/models/nonlifting_body_grid.py diff --git a/sharpy/aero/models/grid.py b/sharpy/aero/models/grid.py new file mode 100644 index 000000000..47e1f328d --- /dev/null +++ b/sharpy/aero/models/grid.py @@ -0,0 +1,177 @@ +"""Grid + +Grid contains +""" +import ctypes as ct +import warnings + +import numpy as np +import scipy.interpolate + +import sharpy.utils.algebra as algebra +import sharpy.utils.cout_utils as cout +from sharpy.utils.datastructures import AeroTimeStepInfo +import sharpy.utils.generator_interface as gen_interface + + +class Grid(object): + """ + ``Grid``is the parent class for the lifting surface grid and nonlifting + body grids. + + It is created by the solver :class:`sharpy.solvers.aerogridloader.AerogridLoader` + + """ + def __init__(self): + self.data_dict = None + self.beam = None + self.aero_settings = None + self.timestep_info = [] + self.ini_info = None + + self.surface_distribution = None + self.surface_m = None + self.dimensions = None + self.grid_type = None + + self.n_node = 0 + self.n_elem = 0 + self.n_surf = 0 + self.n_aero_node = 0 + self.grid_type = None + + self.struct2aero_mapping = None + self.aero2struct_mapping = [] + + + def generate(self, data_dict, beam, aero_settings, ts): + + + self.data_dict = data_dict + self.beam = beam + self.aero_settings = aero_settings + # key words = safe in aero_settings? --> grid_type + # number of total nodes (structural + aero&struc) + self.n_node = len(data_dict[self.grid_type + '_node']) # gridtype + '_node' + # number of elements + self.n_elem = len(data_dict['surface_distribution']) + # surface distribution + self.surface_distribution = data_dict['surface_distribution'] + # number of surfaces + temp = set(data_dict['surface_distribution']) + #TO-DO: improve: avoid for loops + self.n_surf = sum(1 for i in temp if i >= 0) + # number of chordwise panels + self.surface_m = data_dict['surface_m'] + # number of aero nodes + self.n_aero_node = sum(data_dict[self.grid_type + '_node']) + + + # get N per surface + self.calculate_dimensions() + + # write grid info to screen + # self.output_info() + + def calculate_dimensions(self): + self.dimensions = np.zeros((self.n_surf, 2), dtype=int) + for i in range(self.n_surf): + # adding M values + self.dimensions[i, 0] = self.surface_m[i] + # Improvement: + # self.aero.dimensions[:, 0] = self.surface_m[:] + # count N values (actually, the count result + # will be N+1) + nodes_in_surface = [] + + #IMPROVEMENT + for i_surf in range(self.n_surf): + nodes_in_surface.append([]) + + # Improvement! + for i_elem in range(self.beam.num_elem): + nodes = self.beam.elements[i_elem].global_connectivities + i_surf = self.surface_distribution[i_elem] + if i_surf < 0: + continue + for i_global_node in nodes: + if i_global_node in nodes_in_surface[i_surf]: + continue + else: + nodes_in_surface[i_surf].append(i_global_node) + if self.data_dict[self.grid_type + '_node'][i_global_node]: + self.dimensions[i_surf, 1] += 1 + + # accounting for N+1 nodes -> N panels + self.dimensions[:, 1] -= 1 + + + def add_timestep(self): + try: + self.timestep_info.append(self.timestep_info[-1].copy()) + except IndexError: + self.timestep_info.append(self.ini_info.copy()) + + def generate_zeta_timestep_info(self, structure_tstep, aero_tstep, beam, aero_settings, it=None, dt=None): + if it is None: + it = len(beam.timestep_info) - 1 + + + def generate_zeta(self, beam, aero_settings, ts=-1, beam_ts=-1): + self.generate_zeta_timestep_info(beam.timestep_info[beam_ts], + self.timestep_info[ts], + beam, + aero_settings) + + def generate_mapping(self): + self.struct2aero_mapping = [[]]*self.n_node + surf_n_counter = np.zeros((self.n_surf,), dtype=int) + nodes_in_surface = [] + for i_surf in range(self.n_surf): + nodes_in_surface.append([]) + for i_elem in range(self.n_elem): + i_surf = self.surface_distribution[i_elem] + if i_surf == -1: + continue + for i_global_node in self.beam.elements[i_elem].reordered_global_connectivities: + if not self.data_dict[self.grid_type + '_node'][i_global_node]: + continue + + if i_global_node in nodes_in_surface[i_surf]: + continue + else: + nodes_in_surface[i_surf].append(i_global_node) + surf_n_counter[i_surf] += 1 + try: + self.struct2aero_mapping[i_global_node][0] + except IndexError: + self.struct2aero_mapping[i_global_node] = [] + + i_n = surf_n_counter[i_surf] - 1 + self.struct2aero_mapping[i_global_node].append({'i_surf': i_surf, + 'i_n': i_n}) + + nodes_in_surface = [] + for i_surf in range(self.n_surf): + nodes_in_surface.append([]) + + for i_surf in range(self.n_surf): + self.aero2struct_mapping.append([-1]*(surf_n_counter[i_surf])) + + for i_elem in range(self.n_elem): + for i_global_node in self.beam.elements[i_elem].global_connectivities: + for i in range(len(self.struct2aero_mapping[i_global_node])): + try: + i_surf = self.struct2aero_mapping[i_global_node][i]['i_surf'] + i_n = self.struct2aero_mapping[i_global_node][i]['i_n'] + if i_global_node in nodes_in_surface[i_surf]: + continue + else: + nodes_in_surface[i_surf].append(i_global_node) + except KeyError: + continue + self.aero2struct_mapping[i_surf][i_n] = i_global_node + + def update_orientation(self, quat, ts=-1): + rot = algebra.quat2rotation(quat) + self.timestep_info[ts].update_orientation(rot.T) diff --git a/sharpy/aero/models/nonlifting_body_grid.py b/sharpy/aero/models/nonlifting_body_grid.py new file mode 100644 index 000000000..7f2da7be0 --- /dev/null +++ b/sharpy/aero/models/nonlifting_body_grid.py @@ -0,0 +1,155 @@ +"""Nonlifting Body grid + +Description +""" + +from sharpy.aero.models.grid import Grid +from sharpy.utils.datastructures import NonliftingBodyTimeStepInfo +import numpy as np +import itertools +from scipy.optimize import fsolve + +import pandas as pd + + +class Nonlifting_body_grid(Grid): + """ + ``Nonlifting Body Grid`` is the main object containing information of the + nonlifting bodygrid, consisting of triangular and quadrilateral panels. + It is created by the solver :class:`sharpy.solvers.aerogridloader.AerogridLoader` + + """ + def __init__(self): + super().__init__() + self.grid_type = 'nonlifting_body' + + + def generate(self, data_dict, beam, nonlifting_body_settings, ts): ##input? + super().generate(data_dict, beam, nonlifting_body_settings, ts) + + # allocating initial grid storage + self.ini_info = NonliftingBodyTimeStepInfo(self.dimensions) + + self.add_timestep() + self.generate_mapping() + self.generate_zeta(self.beam, self.aero_settings, ts) + + def generate_zeta_timestep_info(self, structure_tstep, nonlifting_body_tstep, beam, aero_settings, it=None, dt=None): + super().generate_zeta_timestep_info(structure_tstep, nonlifting_body_tstep, beam, aero_settings, it, dt) + + for i_surf in range(self.n_surf): + # Get Zeta (Collocation point positions in A? frame) + # TO-DO: Consider fuselage deformation for node position calculations + nonlifting_body_tstep.zeta[i_surf] = self.get_collocation_point_pos(i_surf, self.dimensions[i_surf][-1], structure_tstep) + # TO-DO: Add Zeta Dot Calculation + # aero_tstep.zeta_dot[i_surf] + + def get_triangle_center(self, p1, p2, p3): + return (p1+p2+p3)/3 + + def get_quadrilateral_center(self, list_points): + # based on http://jwilson.coe.uga.edu/EMT668/EMT668.Folders.F97/Patterson/EMT%20669/centroid%20of%20quad/Centroid.html + array_triangle_centroids = np.zeros((4,3)) + counter = 0 + triangle_combinations = [[0, 1, 2], [0, 2, 3], [0, 1, 3], [1, 2, 3]] + for triangle in triangle_combinations: #list(itertools.combinations([0, 1, 2, 3], 3)): + array_triangle_centroids[counter] = self.get_triangle_center( + list_points[triangle[0]], + list_points[triangle[1]], + list_points[triangle[2]]) + counter+=1 + centroid_quadrilateral = self.find_intersection_points(array_triangle_centroids) + return centroid_quadrilateral + + def get_nodes_position(self,i_surf, structure_tstep, numb_radial_nodes): + matrix_nodes = np.zeros(((self.dimensions[i_surf][1]-1) + *(numb_radial_nodes)+2, 3)) + array_phi_coordinates = np.linspace(0, 2*np.pi, numb_radial_nodes) + print(i_surf) + print(self.aero2struct_mapping) + print(self.struct2aero_mapping) + # cache sin and cos values + array_sin_phi = np.sin(array_phi_coordinates) + array_cos_phi = np.cos(array_phi_coordinates) + phi_counter = 0 + row_idx_start, row_idx_end = 0, 0 + for i_global_node in self.aero2struct_mapping[i_surf]: + radius = self.data_dict["radius"][i_global_node] + # axissymmetric body has only one node at nose and tail (r = 0) + if radius == 0.0: + matrix_nodes[row_idx_start, :] = structure_tstep.pos[i_global_node, :] + row_idx_start += 1 + else: + row_idx_end = row_idx_start + numb_radial_nodes + matrix_nodes[row_idx_start:row_idx_end, 0] = structure_tstep.pos[i_global_node, 0] + matrix_nodes[row_idx_start:row_idx_end, 1] = np.array( + structure_tstep.pos[i_global_node, 1] + ) + radius*array_cos_phi + matrix_nodes[row_idx_start:row_idx_end, 2] = np.array( + structure_tstep.pos[i_global_node, 2] + ) + radius*array_sin_phi + row_idx_start += numb_radial_nodes + phi_counter += 1 + + return matrix_nodes + + + def get_collocation_point_pos(self, i_surf, numb_spanwise_elements, structure_tstep): + numb_radial_nodes = self.surface_m[i_surf]+1 + matrix_nodes = self.get_nodes_position(i_surf, structure_tstep, numb_radial_nodes) + counter, i_row = 0, 0 + matrix_collocation = np.zeros(((numb_spanwise_elements)*(numb_radial_nodes-1),3)) + print(numb_spanwise_elements, numb_radial_nodes) + for i in range(0, numb_spanwise_elements): + if i == 0: + for i_rad_node in range(0, numb_radial_nodes-1): + print(i_rad_node, counter) + matrix_collocation[i_row] = self.get_triangle_center( + matrix_nodes[0], + matrix_nodes[i_rad_node+1], + matrix_nodes[i_rad_node+2]) + i_row += 1 + counter+=1 + elif i == numb_spanwise_elements-1: + for i_rad_node in range(0, numb_radial_nodes-1): + matrix_collocation[i_row] = self.get_triangle_center( + matrix_nodes[-1], + matrix_nodes[counter], + matrix_nodes[counter+1]) + i_row+=1 + counter+=1 + else: + for i_rad_node in range(0, numb_radial_nodes-1): + matrix_collocation[i_row] = self.get_quadrilateral_center( + matrix_nodes[[counter, counter +1, + counter+numb_radial_nodes+1, + counter+numb_radial_nodes]]) + counter+=1 + i_row+=1 + counter+=1 + np.savetxt("./collocation.csv", matrix_collocation, delimiter=",") + return matrix_collocation + + + def find_intersection_points(self, array_points): + sol_parameter = fsolve(self.intersection_point_equation, + [0.3, 0.3, 0.0], args = tuple(array_points)) + return array_points[0]+sol_parameter[0]*(array_points[1]-array_points[0]) + + def intersection_point_equation(self,z, *data): + """ + Function to determine intersection point between 4 points for fsolve. + Math: A+t*(B-A) -C - u*(D-C) = 0 + TO-DO: Define function in utils as helper function (algebra??) + """ + point_A, point_B, point_C, point_D = data + t = z[0] + u = z[1] + F = point_A+t*(point_B-point_A) - point_C - u*(point_D-point_C) + return F + + + + + + From 80c1592405db6c85ec9f08aaa30d2b8cb111029e Mon Sep 17 00:00:00 2001 From: asd Date: Fri, 4 Dec 2020 13:16:15 +0000 Subject: [PATCH 010/232] Add: TimeStepInfo and NonliftingBodyTimestepInfo Class - NonliftingBodyTimeStepInfo is similar to AeroTimestepInfo but contains the relevant aerodynamic attributes for a single time step for nonlifting bodies instead of nonlifting surfaces - Its parent class TimeStepInfo will later also be used as a parent class for AeroTimeStepInfo --- sharpy/utils/datastructures.py | 383 +++++++++++++++++++++++++++++++++ 1 file changed, 383 insertions(+) diff --git a/sharpy/utils/datastructures.py b/sharpy/utils/datastructures.py index 5b9434789..417a2de09 100644 --- a/sharpy/utils/datastructures.py +++ b/sharpy/utils/datastructures.py @@ -11,6 +11,389 @@ import sharpy.utils.multibody as mb +class TimeStepInfo(object): + """ + Time step class. + + It is the parent class of the AeroTimeStepInfo and NonliftingBodyTimeStepInfo, which contain the relevant + aerodynamic attributes for a single time step. All variables should be expressed in ``G`` FoR unless + otherwise stated. + + Attributes: + ct_dimensions: Pointer to ``dimensions`` to interface the C++ library `uvlmlib`` + + dimensions (np.ndarray): Matrix defining the dimensions of the vortex grid on solid surfaces + ``[num_surf x chordwise panels x spanwise panels]`` + + n_surf (int): Number of aerodynamic surfaces on solid bodies. Each aerodynamic surface on solid bodies will + have an associted wake. + + zeta (list(np.ndarray): Location of solid grid vertices + ``[n_surf][3 x (chordwise nodes + 1) x (spanwise nodes + 1)]`` + zeta_dot (list(np.ndarray)): Time derivative of ``zeta`` + normals (list(np.ndarray)): Normal direction to panels at the panel center + ``[n_surf][3 x chordwise nodes x spanwise nodes]`` + forces (list(np.ndarray)): Forces not associated to time derivatives on grid vertices + ``[n_surf][3 x (chordwise nodes + 1) x (spanwise nodes + 1)]`` + dynamic_forces (list(np.ndarray)): Forces associated to time derivatives on grid vertices + ``[n_surf][3 x (chordwise nodes + 1) x (spanwise nodes + 1)]`` + u_ext (list(np.ndarray)): Background flow velocity on solid grid nodes + ``[n_surf][3 x (chordwise nodes + 1) x (spanwise nodes + 1)]`` + + + inertial_total_forces (list(np.ndarray)): Total aerodynamic forces in ``G`` FoR ``[n_surf x 6]`` + body_total_forces (list(np.ndarray)): Total aerodynamic forces in ``A`` FoR ``[n_surf x 6]`` + inertial_steady_forces (list(np.ndarray)): Total aerodynamic steady forces in ``G`` FoR ``[n_surf x 6]`` + body_steady_forces (list(np.ndarray)): Total aerodynamic steady forces in ``A`` FoR ``[n_surf x 6]`` + inertial_unsteady_forces (list(np.ndarray)): Total aerodynamic unsteady forces in ``G`` FoR ``[n_surf x 6]`` + body_unsteady_forces (list(np.ndarray)): Total aerodynamic unsteady forces in ``A`` FoR ``[n_surf x 6]`` + + postproc_cell (dict): Variables associated to cells to be postprocessed + postproc_node (dict): Variables associated to nodes to be postprocessed + + in_global_AFoR (bool): ``True`` if the variables are stored in the global A FoR. ``False`` if they are stored + in the local A FoR of each body. Always ``True`` for single-body simulations. Currently not used. + + + Args: + dimensions (np.ndarray): Matrix defining the dimensions of the vortex grid on solid surfaces + ``[num_surf x chordwise panels x spanwise panels]`` + """ + def __init__(self, dimensions): + self.ct_dimensions = None + + self.dimensions = dimensions.copy() + self.n_surf = self.dimensions.shape[0] + # generate placeholder for aero grid zeta coordinates + self.zeta = [] + for i_surf in range(self.n_surf): + self.zeta.append(np.zeros((3, #x,y,z + dimensions[i_surf, 0], + dimensions[i_surf, 1]), + dtype=ct.c_double)) + + self.zeta_dot = [] + for i_surf in range(self.n_surf): + self.zeta_dot.append(np.zeros((3, + dimensions[i_surf, 0], + dimensions[i_surf, 1]), + dtype=ct.c_double)) + + # panel normals + self.normals = [] + for i_surf in range(self.n_surf): + self.normals.append(np.zeros((3, + dimensions[i_surf, 0], + dimensions[i_surf, 1]), + dtype=ct.c_double)) + + # panel forces + self.forces = [] + for i_surf in range(self.n_surf): + self.forces.append(np.zeros((6, + dimensions[i_surf, 0], + dimensions[i_surf, 1]), + dtype=ct.c_double)) + # panel forces + self.dynamic_forces = [] + for i_surf in range(self.n_surf): + self.dynamic_forces.append(np.zeros((6, + dimensions[i_surf, 0], + dimensions[i_surf, 1]), + dtype=ct.c_double)) + + self.u_ext = [] + for i_surf in range(self.n_surf): + self.u_ext.append(np.zeros((3, + dimensions[i_surf, 0], + dimensions[i_surf, 1]), + dtype=ct.c_double)) + + # total forces + self.inertial_total_forces = np.zeros((self.n_surf, 6)) + self.body_total_forces = np.zeros((self.n_surf, 6)) + self.inertial_steady_forces = np.zeros((self.n_surf, 6)) + self.body_steady_forces = np.zeros((self.n_surf, 6)) + self.inertial_unsteady_forces = np.zeros((self.n_surf, 6)) + self.body_unsteady_forces = np.zeros((self.n_surf, 6)) + + self.postproc_cell = dict() + self.postproc_node = dict() + + # Multibody variables + self.in_global_AFoR = True + + + def copy(self): + """ + Returns a copy of a deepcopy of a :class:`~sharpy.utils.datastructures.TimeStepInfo` + """ + return create_placeholder(TimeStepInfo(self.dimensions)) + + def create_placeholder(self, copied): + # generate placeholder for aero grid zeta coordinates + for i_surf in range(copied.n_surf): + copied.zeta[i_surf] = self.zeta[i_surf].astype(dtype=ct.c_double, copy=True, order='C') + + for i_surf in range(copied.n_surf): + copied.zeta_dot[i_surf] = self.zeta_dot[i_surf].astype(dtype=ct.c_double, copy=True, order='C') + + # panel normals + for i_surf in range(copied.n_surf): + copied.normals[i_surf] = self.normals[i_surf].astype(dtype=ct.c_double, copy=True, order='C') + + # panel forces + for i_surf in range(copied.n_surf): + copied.forces[i_surf] = self.forces[i_surf].astype(dtype=ct.c_double, copy=True, order='C') + + # panel forces + for i_surf in range(copied.n_surf): + copied.dynamic_forces[i_surf] = self.dynamic_forces[i_surf].astype(dtype=ct.c_double, copy=True, order='C') + + # placeholder for external velocity + for i_surf in range(copied.n_surf): + copied.u_ext[i_surf] = self.u_ext[i_surf].astype(dtype=ct.c_double, copy=True, order='C') + + # total forces + copied.inertial_total_forces = self.inertial_total_forces.astype(dtype=ct.c_double, copy=True, order='C') + copied.body_total_forces = self.body_total_forces.astype(dtype=ct.c_double, copy=True, order='C') + copied.inertial_steady_forces = self.inertial_steady_forces.astype(dtype=ct.c_double, copy=True, order='C') + copied.body_steady_forces = self.body_steady_forces.astype(dtype=ct.c_double, copy=True, order='C') + copied.inertial_unsteady_forces = self.inertial_unsteady_forces.astype(dtype=ct.c_double, copy=True, order='C') + copied.body_unsteady_forces = self.body_unsteady_forces.astype(dtype=ct.c_double, copy=True, order='C') + + copied.postproc_cell = copy.deepcopy(self.postproc_cell) + copied.postproc_node = copy.deepcopy(self.postproc_node) + + return copied + + def generate_ctypes_pointers(self): + """ + Generates the pointers to aerodynamic variables used to interface the C++ library ``uvlmlib`` + """ + self.ct_dimensions = self.dimensions.astype(dtype=ct.c_uint, copy=True) + + n_surf = len(self.dimensions) + + from sharpy.utils.constants import NDIM + + self.ct_zeta_list = [] + for i_surf in range(self.n_surf): + for i_dim in range(NDIM): + self.ct_zeta_list.append(self.zeta[i_surf][i_dim, :, :].reshape(-1)) + + self.ct_zeta_dot_list = [] + for i_surf in range(self.n_surf): + for i_dim in range(NDIM): + self.ct_zeta_dot_list.append(self.zeta_dot[i_surf][i_dim, :, :].reshape(-1)) + + self.ct_u_ext_list = [] + for i_surf in range(self.n_surf): + for i_dim in range(NDIM): + self.ct_u_ext_list.append(self.u_ext[i_surf][i_dim, :, :].reshape(-1)) + + self.ct_normals_list = [] + for i_surf in range(self.n_surf): + for i_dim in range(NDIM): + self.ct_normals_list.append(self.normals[i_surf][i_dim, :, :].reshape(-1)) + + self.ct_forces_list = [] + for i_surf in range(self.n_surf): + for i_dim in range(NDIM*2): + self.ct_forces_list.append(self.forces[i_surf][i_dim, :, :].reshape(-1)) + + self.ct_dynamic_forces_list = [] + for i_surf in range(self.n_surf): + for i_dim in range(NDIM*2): + self.ct_dynamic_forces_list.append(self.dynamic_forces[i_surf][i_dim, :, :].reshape(-1)) + + try: + self.postproc_cell['incidence_angle'] + except KeyError: + with_incidence_angle = False + else: + with_incidence_angle = True + + if with_incidence_angle: + self.ct_incidence_list = [] + for i_surf in range(self.n_surf): + self.ct_incidence_list.append(self.postproc_cell['incidence_angle'][i_surf][:, :].reshape(-1)) + + self.ct_p_dimensions = ((ct.POINTER(ct.c_uint)*n_surf) + (* np.ctypeslib.as_ctypes(self.ct_dimensions))) + self.ct_p_zeta = ((ct.POINTER(ct.c_double)*len(self.ct_zeta_list)) + (* [np.ctypeslib.as_ctypes(array) for array in self.ct_zeta_list])) + self.ct_p_zeta_dot = ((ct.POINTER(ct.c_double)*len(self.ct_zeta_dot_list)) + (* [np.ctypeslib.as_ctypes(array) for array in self.ct_zeta_dot_list])) + self.ct_p_u_ext = ((ct.POINTER(ct.c_double)*len(self.ct_u_ext_list)) + (* [np.ctypeslib.as_ctypes(array) for array in self.ct_u_ext_list])) + self.ct_p_normals = ((ct.POINTER(ct.c_double)*len(self.ct_normals_list)) + (* [np.ctypeslib.as_ctypes(array) for array in self.ct_normals_list])) + self.ct_p_forces = ((ct.POINTER(ct.c_double)*len(self.ct_forces_list)) + (* [np.ctypeslib.as_ctypes(array) for array in self.ct_forces_list])) + self.ct_p_dynamic_forces = ((ct.POINTER(ct.c_double)*len(self.ct_dynamic_forces_list)) + (* [np.ctypeslib.as_ctypes(array) for array in self.ct_dynamic_forces_list])) + + if with_incidence_angle: + self.postproc_cell['incidence_angle_ct_pointer'] = ((ct.POINTER(ct.c_double)*len(self.ct_incidence_list)) + (* [np.ctypeslib.as_ctypes(array) for array in self.ct_incidence_list])) + + def remove_ctypes_pointers(self): + """ + Removes the pointers to aerodynamic variables used to interface the C++ library ``uvlmlib`` + """ + try: + del self.ct_p_dimensions + except AttributeError: + pass + + try: + del self.ct_p_zeta + except AttributeError: + pass + + try: + del self.ct_p_zeta_dot + except AttributeError: + pass + + try: + del self.ct_p_u_ext + except AttributeError: + pass + + try: + del self.ct_p_normals + except AttributeError: + pass + + try: + del self.ct_p_forces + except AttributeError: + pass + + try: + del self.ct_p_dynamic_forces + except AttributeError: + pass + + for k in list(self.postproc_cell.keys()): + if 'ct_list' in k: + del self.postproc_cell[k] + elif 'ct_pointer' in k: + del self.postproc_cell[k] + + +class NonliftingBodyTimeStepInfo(TimeStepInfo): + """ + Nonlifting Body Time step class. + + It is the inheritance from ``TimeStepInfo`` and contains the relevant aerodynamic attributes for + a single time step of a nonlifting body. All variables should be expressed in ``G`` + FoR unless otherwise stated. + + Attributes: + ct_dimensions: Pointer to ``dimensions`` to interface the C++ library `uvlmlib`` + + dimensions (np.ndarray): Matrix defining the dimensions of the vortex grid on solid surfaces + ``[num_surf x radial panels x spanwise panels]`` + + n_surf (int): Number of aerodynamic surfaces on nonlifting bodies. + + zeta (list(np.ndarray): Location of solid grid vertices + ``[n_surf][3 x (radial panel) x (spanwise panel)]`` + zeta_dot (list(np.ndarray)): Time derivative of ``zeta`` + normals (list(np.ndarray)): Normal direction to panels at the panel center + ``[n_surf][3 x radial panels x spanwise panels]`` + forces (list(np.ndarray)): Forces not associated to time derivatives on grid vertices + ``[n_surf][3 x (radial panels) x (spanwise panels)]`` + dynamic_forces (list(np.ndarray)): Forces associated to time derivatives on grid vertices + ``[n_surf][3 x (radial panels) x (spanwise panels)]`` + + u_ext (list(np.ndarray)): Background flow velocity on solid grid panel + ``[n_surf][3 x (radial panels) x (spanwise panel + 1)]`` + sigma (list(np.ndarray)): Source strength associated to solid panels + ``[n_surf][3 x radial panel x spanwise panel]`` + sigma_dot (list(np.ndarray)): Time derivative of ``sigma`` + + inertial_total_forces (list(np.ndarray)): Total aerodynamic forces in ``G`` FoR ``[n_surf x 6]`` + body_total_forces (list(np.ndarray)): Total aerodynamic forces in ``A`` FoR ``[n_surf x 6]`` + inertial_steady_forces (list(np.ndarray)): Total aerodynamic steady forces in ``G`` FoR ``[n_surf x 6]`` + body_steady_forces (list(np.ndarray)): Total aerodynamic steady forces in ``A`` FoR ``[n_surf x 6]`` + inertial_unsteady_forces (list(np.ndarray)): Total aerodynamic unsteady forces in ``G`` FoR ``[n_surf x 6]`` + body_unsteady_forces (list(np.ndarray)): Total aerodynamic unsteady forces in ``A`` FoR ``[n_surf x 6]`` + + postproc_cell (dict): Variables associated to cells to be postprocessed + postproc_node (dict): Variables associated to panel to be postprocessed + + in_global_AFoR (bool): ``True`` if the variables are stored in the global A FoR. ``False`` if they are stored + in the local A FoR of each body. Always ``True`` for single-body simulations. Currently not used. + + Args: + dimensions (np.ndarray): Matrix defining the dimensions of the vortex grid on solid surfaces + ``[num_surf x radial panels x spanwise panels]`` + """ + def __init__(self, dimensions): #remove dimensions_star as input + super().__init__(dimensions) + + # allocate sigma matrices + self.sigma = [] + for i_surf in range(self.n_surf): + self.sigma.append(np.zeros((dimensions[i_surf, 0], + dimensions[i_surf, 1]), + dtype=ct.c_double)) + + self.sigma_dot = [] + for i_surf in range(self.n_surf): + self.sigma_dot.append(np.zeros((dimensions[i_surf, 0], + dimensions[i_surf, 1]), + dtype=ct.c_double)) + def copy(self): + """ + Returns a copy of a deepcopy of a :class:`~sharpy.utils.datastructures.AeroTimeStepInfo` + """ + return self.create_placeholder(NonliftingBodyTimeStepInfo(self.dimensions)) + + def create_placeholder(self, copied): + super().create_placeholder(copied) + + # allocate sigma star matrices + for i_surf in range(copied.n_surf): + copied.sigma[i_surf] = self.sigma[i_surf].astype(dtype=ct.c_double, copy=True, order='C') + + for i_surf in range(copied.n_surf): + copied.sigma_dot[i_surf] = self.sigma_dot[i_surf].astype(dtype=ct.c_double, copy=True, order='C') + + return copied + + + def generate_ctypes_pointers(self): + """ + Generates the pointers to aerodynamic variables used to interface the C++ library ``uvlmlib`` + """ + super().generate_ctypes_points() + self.ct_p_sigma = ((ct.POINTER(ct.c_double)*len(self.ct_sigma_list)) + (* [np.ctypeslib.as_ctypes(array) for array in self.ct_sigma_list])) + self.ct_p_sigma_dot = ((ct.POINTER(ct.c_double)*len(self.ct_sigma_dot_list)) + (* [np.ctypeslib.as_ctypes(array) for array in self.ct_sigma_dot_list])) + + + def remove_ctypes_pointers(self): + """ + Removes the pointers to aerodynamic variables used to interface the C++ library ``uvlmlib`` + """ + super().remove_ctypes_points() + try: + del self.ct_p_sigma + except AttributeError: + pass + + try: + del self.ct_p_sigma_dot + except AttributeError: + pass + + class AeroTimeStepInfo(object): """ Aerodynamic Time step class. From 2cb07fd8002987c555cea3b648b2d5c2e58a9226 Mon Sep 17 00:00:00 2001 From: asd Date: Fri, 4 Dec 2020 18:23:32 +0000 Subject: [PATCH 011/232] refactor: AeroTimeStepInfo inherits now from TimeStepInfo class - functionality of class remains unchanged --- sharpy/utils/datastructures.py | 190 +++++---------------------------- 1 file changed, 24 insertions(+), 166 deletions(-) diff --git a/sharpy/utils/datastructures.py b/sharpy/utils/datastructures.py index 417a2de09..682ab3880 100644 --- a/sharpy/utils/datastructures.py +++ b/sharpy/utils/datastructures.py @@ -394,7 +394,8 @@ def remove_ctypes_pointers(self): pass -class AeroTimeStepInfo(object): + +class AeroTimeStepInfo(TimeStepInfo): """ Aerodynamic Time step class. @@ -456,12 +457,10 @@ class AeroTimeStepInfo(object): ``[num_surf x streamwise panels x spanwise panels]`` """ def __init__(self, dimensions, dimensions_star): - self.ct_dimensions = None + super().__init__(dimensions) self.ct_dimensions_star = None - self.dimensions = dimensions.copy() self.dimensions_star = dimensions_star.copy() - self.n_surf = self.dimensions.shape[0] # generate placeholder for aero grid zeta coordinates self.zeta = [] for i_surf in range(self.n_surf): @@ -476,14 +475,6 @@ def __init__(self, dimensions, dimensions_star): dimensions[i_surf, 1] + 1), dtype=ct.c_double)) - # panel normals - self.normals = [] - for i_surf in range(self.n_surf): - self.normals.append(np.zeros((3, - dimensions[i_surf, 0], - dimensions[i_surf, 1]), - dtype=ct.c_double)) - # panel forces self.forces = [] for i_surf in range(self.n_surf): @@ -548,54 +539,25 @@ def __init__(self, dimensions, dimensions_star): dimensions_star[i_surf, 1] + 1), dtype=ct.c_double)) - # total forces - self.inertial_total_forces = np.zeros((self.n_surf, 6)) - self.body_total_forces = np.zeros((self.n_surf, 6)) - self.inertial_steady_forces = np.zeros((self.n_surf, 6)) - self.body_steady_forces = np.zeros((self.n_surf, 6)) - self.inertial_unsteady_forces = np.zeros((self.n_surf, 6)) - self.body_unsteady_forces = np.zeros((self.n_surf, 6)) - - self.postproc_cell = dict() - self.postproc_node = dict() + self.wake_conv_vel = [] + for i_surf in range(self.n_surf): + self.wake_conv_vel.append(np.zeros((dimensions_star[i_surf, 0], + dimensions_star[i_surf, 1]), + dtype=ct.c_double)) - # Multibody variables - self.in_global_AFoR = True self.control_surface_deflection = np.array([]) def copy(self): - """ - Returns a copy of a deepcopy of a :class:`~sharpy.utils.datastructures.AeroTimeStepInfo` - """ - copied = AeroTimeStepInfo(self.dimensions, self.dimensions_star) - # generate placeholder for aero grid zeta coordinates - for i_surf in range(copied.n_surf): - copied.zeta[i_surf] = self.zeta[i_surf].astype(dtype=ct.c_double, copy=True, order='C') - - for i_surf in range(copied.n_surf): - copied.zeta_dot[i_surf] = self.zeta_dot[i_surf].astype(dtype=ct.c_double, copy=True, order='C') - - # panel normals - for i_surf in range(copied.n_surf): - copied.normals[i_surf] = self.normals[i_surf].astype(dtype=ct.c_double, copy=True, order='C') - - # panel forces - for i_surf in range(copied.n_surf): - copied.forces[i_surf] = self.forces[i_surf].astype(dtype=ct.c_double, copy=True, order='C') - - # panel forces - for i_surf in range(copied.n_surf): - copied.dynamic_forces[i_surf] = self.dynamic_forces[i_surf].astype(dtype=ct.c_double, copy=True, order='C') + return self.create_placeholder(AeroTimeStepInfo(self.dimensions, self.dimensions_star)) + def create_placeholder(self, copied): + super().create_placeholder(copied) # generate placeholder for aero grid zeta_star coordinates for i_surf in range(copied.n_surf): copied.zeta_star[i_surf] = self.zeta_star[i_surf].astype(dtype=ct.c_double, copy=True, order='C') # placeholder for external velocity - for i_surf in range(copied.n_surf): - copied.u_ext[i_surf] = self.u_ext[i_surf].astype(dtype=ct.c_double, copy=True, order='C') - for i_surf in range(copied.n_surf): copied.u_ext_star[i_surf] = self.u_ext_star[i_surf].astype(dtype=ct.c_double, copy=True, order='C') @@ -612,52 +574,24 @@ def copy(self): for i_surf in range(copied.n_surf): copied.dist_to_orig[i_surf] = self.dist_to_orig[i_surf].astype(dtype=ct.c_double, copy=True, order='C') - # total forces - copied.inertial_total_forces = self.inertial_total_forces.astype(dtype=ct.c_double, copy=True, order='C') - copied.body_total_forces = self.body_total_forces.astype(dtype=ct.c_double, copy=True, order='C') - copied.inertial_steady_forces = self.inertial_steady_forces.astype(dtype=ct.c_double, copy=True, order='C') - copied.body_steady_forces = self.body_steady_forces.astype(dtype=ct.c_double, copy=True, order='C') - copied.inertial_unsteady_forces = self.inertial_unsteady_forces.astype(dtype=ct.c_double, copy=True, order='C') - copied.body_unsteady_forces = self.body_unsteady_forces.astype(dtype=ct.c_double, copy=True, order='C') - - copied.postproc_cell = copy.deepcopy(self.postproc_cell) - copied.postproc_node = copy.deepcopy(self.postproc_node) + for i_surf in range(copied.n_surf): + copied.wake_conv_vel[i_surf] = self.wake_conv_vel[i_surf].astype(dtype=ct.c_double, copy=True, order='C') copied.control_surface_deflection = self.control_surface_deflection.astype(dtype=ct.c_double, copy=True) return copied def generate_ctypes_pointers(self): - """ - Generates the pointers to aerodynamic variables used to interface the C++ library ``uvlmlib`` - """ - self.ct_dimensions = self.dimensions.astype(dtype=ct.c_uint, copy=True) - self.ct_dimensions_star = self.dimensions_star.astype(dtype=ct.c_uint, copy=True) - - n_surf = len(self.dimensions) - from sharpy.utils.constants import NDIM - - self.ct_zeta_list = [] - for i_surf in range(self.n_surf): - for i_dim in range(NDIM): - self.ct_zeta_list.append(self.zeta[i_surf][i_dim, :, :].reshape(-1)) - - self.ct_zeta_dot_list = [] - for i_surf in range(self.n_surf): - for i_dim in range(NDIM): - self.ct_zeta_dot_list.append(self.zeta_dot[i_surf][i_dim, :, :].reshape(-1)) + n_surf = len(self.dimensions) + super().generate_ctypes_pointers() + self.ct_dimensions_star = self.dimensions_star.astype(dtype=ct.c_uint, copy=True) self.ct_zeta_star_list = [] for i_surf in range(self.n_surf): for i_dim in range(NDIM): self.ct_zeta_star_list.append(self.zeta_star[i_surf][i_dim, :, :].reshape(-1)) - self.ct_u_ext_list = [] - for i_surf in range(self.n_surf): - for i_dim in range(NDIM): - self.ct_u_ext_list.append(self.u_ext[i_surf][i_dim, :, :].reshape(-1)) - self.ct_u_ext_star_list = [] for i_surf in range(self.n_surf): for i_dim in range(NDIM): @@ -675,49 +609,18 @@ def generate_ctypes_pointers(self): for i_surf in range(self.n_surf): self.ct_gamma_star_list.append(self.gamma_star[i_surf][:, :].reshape(-1)) - self.ct_normals_list = [] - for i_surf in range(self.n_surf): - for i_dim in range(NDIM): - self.ct_normals_list.append(self.normals[i_surf][i_dim, :, :].reshape(-1)) - - self.ct_forces_list = [] - for i_surf in range(self.n_surf): - for i_dim in range(NDIM*2): - self.ct_forces_list.append(self.forces[i_surf][i_dim, :, :].reshape(-1)) - - self.ct_dynamic_forces_list = [] - for i_surf in range(self.n_surf): - for i_dim in range(NDIM*2): - self.ct_dynamic_forces_list.append(self.dynamic_forces[i_surf][i_dim, :, :].reshape(-1)) - self.ct_dist_to_orig_list = [] for i_surf in range(self.n_surf): self.ct_dist_to_orig_list.append(self.dist_to_orig[i_surf][:, :].reshape(-1)) - try: - self.postproc_cell['incidence_angle'] - except KeyError: - with_incidence_angle = False - else: - with_incidence_angle = True - - if with_incidence_angle: - self.ct_incidence_list = [] - for i_surf in range(self.n_surf): - self.ct_incidence_list.append(self.postproc_cell['incidence_angle'][i_surf][:, :].reshape(-1)) + self.ct_wake_conv_vel_list = [] + for i_surf in range(self.n_surf): + self.ct_wake_conv_vel_list.append(self.wake_conv_vel[i_surf][:, :].reshape(-1)) - self.ct_p_dimensions = ((ct.POINTER(ct.c_uint)*n_surf) - (* np.ctypeslib.as_ctypes(self.ct_dimensions))) self.ct_p_dimensions_star = ((ct.POINTER(ct.c_uint)*n_surf) (* np.ctypeslib.as_ctypes(self.ct_dimensions_star))) - self.ct_p_zeta = ((ct.POINTER(ct.c_double)*len(self.ct_zeta_list)) - (* [np.ctypeslib.as_ctypes(array) for array in self.ct_zeta_list])) - self.ct_p_zeta_dot = ((ct.POINTER(ct.c_double)*len(self.ct_zeta_dot_list)) - (* [np.ctypeslib.as_ctypes(array) for array in self.ct_zeta_dot_list])) self.ct_p_zeta_star = ((ct.POINTER(ct.c_double)*len(self.ct_zeta_star_list)) (* [np.ctypeslib.as_ctypes(array) for array in self.ct_zeta_star_list])) - self.ct_p_u_ext = ((ct.POINTER(ct.c_double)*len(self.ct_u_ext_list)) - (* [np.ctypeslib.as_ctypes(array) for array in self.ct_u_ext_list])) self.ct_p_u_ext_star = ((ct.POINTER(ct.c_double)*len(self.ct_u_ext_star_list)) (* [np.ctypeslib.as_ctypes(array) for array in self.ct_u_ext_star_list])) self.ct_p_gamma = ((ct.POINTER(ct.c_double)*len(self.ct_gamma_list)) @@ -726,53 +629,23 @@ def generate_ctypes_pointers(self): (* [np.ctypeslib.as_ctypes(array) for array in self.ct_gamma_dot_list])) self.ct_p_gamma_star = ((ct.POINTER(ct.c_double)*len(self.ct_gamma_star_list)) (* [np.ctypeslib.as_ctypes(array) for array in self.ct_gamma_star_list])) - self.ct_p_normals = ((ct.POINTER(ct.c_double)*len(self.ct_normals_list)) - (* [np.ctypeslib.as_ctypes(array) for array in self.ct_normals_list])) - self.ct_p_forces = ((ct.POINTER(ct.c_double)*len(self.ct_forces_list)) - (* [np.ctypeslib.as_ctypes(array) for array in self.ct_forces_list])) - self.ct_p_dynamic_forces = ((ct.POINTER(ct.c_double)*len(self.ct_dynamic_forces_list)) - (* [np.ctypeslib.as_ctypes(array) for array in self.ct_dynamic_forces_list])) self.ct_p_dist_to_orig = ((ct.POINTER(ct.c_double)*len(self.ct_dist_to_orig_list)) (* [np.ctypeslib.as_ctypes(array) for array in self.ct_dist_to_orig_list])) - - if with_incidence_angle: - self.postproc_cell['incidence_angle_ct_pointer'] = ((ct.POINTER(ct.c_double)*len(self.ct_incidence_list)) - (* [np.ctypeslib.as_ctypes(array) for array in self.ct_incidence_list])) + self.ct_p_wake_conv_vel = ((ct.POINTER(ct.c_double)*len(self.ct_wake_conv_vel_list)) + (* [np.ctypeslib.as_ctypes(array) for array in self.ct_wake_conv_vel_list])) def remove_ctypes_pointers(self): - """ - Removes the pointers to aerodynamic variables used to interface the C++ library ``uvlmlib`` - """ - try: - del self.ct_p_dimensions - except AttributeError: - pass - + super().remove_ctypes_pointers() try: del self.ct_p_dimensions_star except AttributeError: pass - try: - del self.ct_p_zeta - except AttributeError: - pass - try: del self.ct_p_zeta_star except AttributeError: pass - try: - del self.ct_p_zeta_dot - except AttributeError: - pass - - try: - del self.ct_p_u_ext - except AttributeError: - pass - try: del self.ct_p_u_ext_star except AttributeError: @@ -794,30 +667,15 @@ def remove_ctypes_pointers(self): pass try: - del self.ct_p_normals - except AttributeError: - pass - - try: - del self.ct_p_forces - except AttributeError: - pass - - try: - del self.ct_p_dynamic_forces + del self.ct_p_dist_to_orig except AttributeError: pass try: - del self.ct_p_dist_to_orig + del self.ct_p_wake_conv_vel except AttributeError: pass - for k in list(self.postproc_cell.keys()): - if 'ct_list' in k: - del self.postproc_cell[k] - elif 'ct_pointer' in k: - del self.postproc_cell[k] def init_matrix_structure(dimensions, with_dim_dimension, added_size=0): From c6d0296c5f2828296db27199171e54b74fa844aa Mon Sep 17 00:00:00 2001 From: asd Date: Fri, 4 Dec 2020 18:38:43 +0000 Subject: [PATCH 012/232] refactor: Aerogrid inherits from Grid class - functionality of Aerogrid class remains unchanged - rename attributes for proper inheritance --- sharpy/aero/models/aerogrid.py | 273 ++++++++++----------------------- 1 file changed, 77 insertions(+), 196 deletions(-) diff --git a/sharpy/aero/models/aerogrid.py b/sharpy/aero/models/aerogrid.py index 2b49490e6..409c98133 100644 --- a/sharpy/aero/models/aerogrid.py +++ b/sharpy/aero/models/aerogrid.py @@ -14,8 +14,10 @@ from sharpy.utils.datastructures import AeroTimeStepInfo import sharpy.utils.generator_interface as gen_interface +from sharpy.aero.models.grid import Grid -class Aerogrid(object): + +class Aerogrid(Grid): """ ``Aerogrid`` is the main object containing information of the grid of panels @@ -23,70 +25,34 @@ class Aerogrid(object): """ def __init__(self): - self.aero_dict = None - self.beam = None - self.aero_settings = None - - self.timestep_info = [] - self.ini_info = None - - self.surface_distribution = None - self.surface_m = None - self.aero_dimensions = None - self.aero_dimensions_star = None + super().__init__() + self.dimensions_star = None self.airfoil_db = dict() - self.struct2aero_mapping = None - self.aero2struct_mapping = [] - self.n_node = 0 - self.n_elem = 0 - self.n_surf = 0 - self.n_aero_node = 0 + self.grid_type = "aero" self.n_control_surfaces = 0 self.cs_generators = [] - self.polars = None - self.wake_shape_generator = None - - def generate(self, aero_dict, beam, aero_settings, ts): - self.aero_dict = aero_dict - self.beam = beam - self.aero_settings = aero_settings - - # number of total nodes (structural + aero&struc) - self.n_node = len(aero_dict['aero_node']) - # number of elements - self.n_elem = len(aero_dict['surface_distribution']) - # surface distribution - self.surface_distribution = aero_dict['surface_distribution'] - # number of surfaces - temp = set(aero_dict['surface_distribution']) - self.n_surf = sum(1 for i in temp if i >= 0) - # number of chordwise panels - self.surface_m = aero_dict['surface_m'] - # number of aero nodes - self.n_aero_node = sum(aero_dict['aero_node']) - - # get N per surface - self.calculate_dimensions() + def generate(self, data_dict, beam, settings, ts): + super().generate(data_dict, beam, settings, ts) # write grid info to screen self.output_info() # allocating initial grid storage - self.ini_info = AeroTimeStepInfo(self.aero_dimensions, - self.aero_dimensions_star) + self.ini_info = AeroTimeStepInfo(self.dimensions, + self.dimensions_star) # load airfoils db # for i_node in range(self.n_node): for i_elem in range(self.n_elem): for i_local_node in range(self.beam.num_node_elem): try: - self.airfoil_db[self.aero_dict['airfoil_distribution'][i_elem, i_local_node]] + self.airfoil_db[self.data_dict['airfoil_distribution'][i_elem, i_local_node]] except KeyError: - airfoil_coords = self.aero_dict['airfoils'][str(self.aero_dict['airfoil_distribution'][i_elem, i_local_node])] - self.airfoil_db[self.aero_dict['airfoil_distribution'][i_elem, i_local_node]] = ( + airfoil_coords = self.data_dict['airfoils'][str(self.data_dict['airfoil_distribution'][i_elem, i_local_node])] + self.airfoil_db[self.data_dict['airfoil_distribution'][i_elem, i_local_node]] = ( scipy.interpolate.interp1d(airfoil_coords[:, 0], airfoil_coords[:, 1], kind='quadratic', @@ -94,31 +60,32 @@ def generate(self, aero_dict, beam, aero_settings, ts): fill_value='extrapolate', assume_sorted=True)) try: - self.n_control_surfaces = np.sum(np.unique(self.aero_dict['control_surface']) >= 0) + self.n_control_surfaces = np.sum(np.unique(self.data_dict['control_surface']) >= 0) except KeyError: pass - # Backward compatibility: check whether control surface deflection settings have been specified. If not, create + # Backward compatibility: check whether control surface deflection aero_settings have been specified. If not, create # section with empty list, such that no cs generator is appended try: - aero_settings['control_surface_deflection'] + settings['control_surface_deflection'] except KeyError: - aero_settings.update({'control_surface_deflection': ['']*self.n_control_surfaces}) + settings.update({'control_surface_deflection': ['']*self.n_control_surfaces}) # pad ctrl surfaces dict with empty strings if not defined - if len(aero_settings['control_surface_deflection']) != self.n_control_surfaces: - undef_ctrl_sfcs = ['']*(self.n_control_surfaces - len(aero_settings['control_surface_deflection'])) - aero_settings['control_surface_deflection'].extend(undef_ctrl_sfcs) + if len(settings['control_surface_deflection']) != self.n_control_surfaces: + undef_ctrl_sfcs = ['']*(self.n_control_surfaces - len(settings['control_surface_deflection'])) + settings['control_surface_deflection'].extend(undef_ctrl_sfcs) + # initialise generators with_error_initialising_cs = False for i_cs in range(self.n_control_surfaces): - if aero_settings['control_surface_deflection'][i_cs] == '': + if settings['control_surface_deflection'][i_cs] == '': self.cs_generators.append(None) else: cout.cout_wrap('Initialising Control Surface {:g} generator'.format(i_cs), 1) # check that the control surface is not static - if self.aero_dict['control_surface_type'][i_cs] == 0: + if self.data_dict['control_surface_type'][i_cs] == 0: raise TypeError('Control surface {:g} is defined as static but there is a control surface generator' 'associated with it'.format(i_cs)) generator_type = gen_interface.generator_from_string( @@ -126,7 +93,7 @@ def generate(self, aero_dict, beam, aero_settings, ts): self.cs_generators.append(generator_type()) try: self.cs_generators[i_cs].initialise( - aero_settings['control_surface_deflection_generator_settings'][str(i_cs)]) + settings['control_surface_deflection_generator_settings'][str(i_cs)]) except KeyError: with_error_initialising_cs = True cout.cout_wrap('Error, unable to locate a settings dictionary for control surface ' @@ -135,72 +102,44 @@ def generate(self, aero_dict, beam, aero_settings, ts): if with_error_initialising_cs: raise KeyError('Unable to locate settings for at least one control surface.') + self.add_timestep() self.generate_mapping() - self.generate_zeta(self.beam, self.aero_settings, ts) + self.generate_zeta(self.beam, self.settings, ts) - if 'polars' in aero_dict: + if 'polars' in self.data_dict: import sharpy.aero.utils.airfoilpolars as ap self.polars = [] - nairfoils = np.amax(self.aero_dict['airfoil_distribution']) + 1 + nairfoils = np.amax(self.data_dict['airfoil_distribution']) + 1 for iairfoil in range(nairfoils): new_polar = ap.polar() - new_polar.initialise(aero_dict['polars'][str(iairfoil)]) + new_polar.initialise(self.data_dict['polars'][str(iairfoil)]) self.polars.append(new_polar) def output_info(self): cout.cout_wrap('The aerodynamic grid contains %u surfaces' % self.n_surf, 1) for i_surf in range(self.n_surf): cout.cout_wrap(' Surface %u, M=%u, N=%u' % (i_surf, - self.aero_dimensions[i_surf, 0], - self.aero_dimensions[i_surf, 1]), 1) + self.dimensions[i_surf, 0], + self.dimensions[i_surf, 1]), 1) cout.cout_wrap(' Wake %u, M=%u, N=%u' % (i_surf, - self.aero_dimensions_star[i_surf, 0], - self.aero_dimensions_star[i_surf, 1])) - cout.cout_wrap(' In total: %u bound panels' % (sum(self.aero_dimensions[:, 0]* - self.aero_dimensions[:, 1]))) - cout.cout_wrap(' In total: %u wake panels' % (sum(self.aero_dimensions_star[:, 0]* - self.aero_dimensions_star[:, 1]))) - cout.cout_wrap(' Total number of panels = %u' % (sum(self.aero_dimensions[:, 0]* - self.aero_dimensions[:, 1]) + - sum(self.aero_dimensions_star[:, 0]* - self.aero_dimensions_star[:, 1]))) + self.dimensions_star[i_surf, 0], + self.dimensions_star[i_surf, 1])) + cout.cout_wrap(' In total: %u bound panels' % (sum(self.dimensions[:, 0]* + self.dimensions[:, 1]))) + cout.cout_wrap(' In total: %u wake panels' % (sum(self.dimensions_star[:, 0]* + self.dimensions_star[:, 1]))) + cout.cout_wrap(' Total number of panels = %u' % (sum(self.dimensions[:, 0]* + self.dimensions[:, 1]) + + sum(self.dimensions_star[:, 0]* + self.dimensions_star[:, 1]))) def calculate_dimensions(self): - self.aero_dimensions = np.zeros((self.n_surf, 2), dtype=int) - for i in range(self.n_surf): - # adding M values - self.aero_dimensions[i, 0] = self.surface_m[i] - # count N values (actually, the count result - # will be N+1) - nodes_in_surface = [] - for i_surf in range(self.n_surf): - nodes_in_surface.append([]) - for i_elem in range(self.beam.num_elem): - nodes = self.beam.elements[i_elem].global_connectivities - i_surf = self.aero_dict['surface_distribution'][i_elem] - if i_surf < 0: - continue - for i_global_node in nodes: - if i_global_node in nodes_in_surface[i_surf]: - continue - else: - nodes_in_surface[i_surf].append(i_global_node) - if self.aero_dict['aero_node'][i_global_node]: - self.aero_dimensions[i_surf, 1] += 1 - - # accounting for N+1 nodes -> N panels - self.aero_dimensions[:, 1] -= 1 + super().calculate_dimensions() - self.aero_dimensions_star = self.aero_dimensions.copy() + self.dimensions_star = self.dimensions.copy() for i_surf in range(self.n_surf): - self.aero_dimensions_star[i_surf, 0] = self.aero_settings['mstar'].value - - def add_timestep(self): - try: - self.timestep_info.append(self.timestep_info[-1].copy()) - except IndexError: - self.timestep_info.append(self.ini_info.copy()) + self.dimensions_star[i_surf, 0] = self.settings['mstar'].value def generate_zeta_timestep_info(self, structure_tstep, aero_tstep, beam, aero_settings, it=None, dt=None): if it is None: @@ -211,20 +150,20 @@ def generate_zeta_timestep_info(self, structure_tstep, aero_tstep, beam, aero_se # check that we have control surface information try: - self.aero_dict['control_surface'] + self.data_dict['control_surface'] with_control_surfaces = True except KeyError: with_control_surfaces = False # check that we have sweep information try: - self.aero_dict['sweep'] + self.data_dict['sweep'] except KeyError: - self.aero_dict['sweep'] = np.zeros_like(self.aero_dict['twist']) + self.data_dict['sweep'] = np.zeros_like(self.data_dict['twist']) # one surface per element for i_elem in range(self.n_elem): - i_surf = self.aero_dict['surface_distribution'][i_elem] + i_surf = self.data_dict['surface_distribution'][i_elem] # check if we have to generate a surface here if i_surf == -1: continue @@ -233,7 +172,7 @@ def generate_zeta_timestep_info(self, structure_tstep, aero_tstep, beam, aero_se i_global_node = self.beam.elements[i_elem].global_connectivities[i_local_node] # i_global_node = self.beam.elements[i_elem].global_connectivities[ # self.beam.elements[i_elem].ordering[i_local_node]] - if not self.aero_dict['aero_node'][i_global_node]: + if not self.data_dict['aero_node'][i_global_node]: continue if i_global_node in global_node_in_surface[i_surf]: continue @@ -261,23 +200,23 @@ def generate_zeta_timestep_info(self, structure_tstep, aero_tstep, beam, aero_se control_surface_info = None if with_control_surfaces: # 1) check that this node and elem have a control surface - if self.aero_dict['control_surface'][i_elem, i_local_node] >= 0: - i_control_surface = self.aero_dict['control_surface'][i_elem, i_local_node] - # 2) type of control surface + write info + if self.data_dict['control_surface'][i_elem, i_local_node] >= 0: + i_control_surface = self.data_dict['control_surface'][i_elem, i_local_node] + # 2) type of control surface + write info control_surface_info = dict() - if self.aero_dict['control_surface_type'][i_control_surface] == 0: + if self.data_dict['control_surface_type'][i_control_surface] == 0: control_surface_info['type'] = 'static' - control_surface_info['deflection'] = self.aero_dict['control_surface_deflection'][i_control_surface] - control_surface_info['chord'] = self.aero_dict['control_surface_chord'][i_control_surface] + control_surface_info['deflection'] = self.data_dict['control_surface_deflection'][i_control_surface] + control_surface_info['chord'] = self.data_dict['control_surface_chord'][i_control_surface] try: - control_surface_info['hinge_coords'] = self.aero_dict['control_surface_hinge_coords'][i_control_surface] + control_surface_info['hinge_coords'] = self.data_dict['control_surface_hinge_coords'][i_control_surface] except KeyError: control_surface_info['hinge_coords'] = None - elif self.aero_dict['control_surface_type'][i_control_surface] == 1: + elif self.data_dict['control_surface_type'][i_control_surface] == 1: control_surface_info['type'] = 'dynamic' - control_surface_info['chord'] = self.aero_dict['control_surface_chord'][i_control_surface] + control_surface_info['chord'] = self.data_dict['control_surface_chord'][i_control_surface] try: - control_surface_info['hinge_coords'] = self.aero_dict['control_surface_hinge_coords'][i_control_surface] + control_surface_info['hinge_coords'] = self.data_dict['control_surface_hinge_coords'][i_control_surface] except KeyError: control_surface_info['hinge_coords'] = None @@ -285,7 +224,7 @@ def generate_zeta_timestep_info(self, structure_tstep, aero_tstep, beam, aero_se control_surface_info['deflection'], control_surface_info['deflection_dot'] = \ self.cs_generators[i_control_surface](params) - elif self.aero_dict['control_surface_type'][i_control_surface] == 2: + elif self.data_dict['control_surface_type'][i_control_surface] == 2: control_surface_info['type'] = 'controlled' try: @@ -294,12 +233,12 @@ def generate_zeta_timestep_info(self, structure_tstep, aero_tstep, beam, aero_se try: old_deflection = aero_tstep.control_surface_deflection[i_control_surface] except IndexError: - old_deflection = self.aero_dict['control_surface_deflection'][i_control_surface] + old_deflection = self.data_dict['control_surface_deflection'][i_control_surface] try: control_surface_info['deflection'] = aero_tstep.control_surface_deflection[i_control_surface] except IndexError: - control_surface_info['deflection'] = self.aero_dict['control_surface_deflection'][i_control_surface] + control_surface_info['deflection'] = self.data_dict['control_surface_deflection'][i_control_surface] if dt is not None: control_surface_info['deflection_dot'] = ( @@ -307,15 +246,14 @@ def generate_zeta_timestep_info(self, structure_tstep, aero_tstep, beam, aero_se else: control_surface_info['deflection_dot'] = 0.0 - control_surface_info['chord'] = self.aero_dict['control_surface_chord'][i_control_surface] + control_surface_info['chord'] = self.data_dict['control_surface_chord'][i_control_surface] try: - control_surface_info['hinge_coords'] = self.aero_dict['control_surface_hinge_coords'][i_control_surface] + control_surface_info['hinge_coords'] = self.data_dict['control_surface_hinge_coords'][i_control_surface] except KeyError: control_surface_info['hinge_coords'] = None - else: - raise NotImplementedError(str(self.aero_dict['control_surface_type'][i_control_surface]) + + raise NotImplementedError(str(self.data_dict['control_surface_type'][i_control_surface]) + ' control surfaces are not yet implemented') @@ -323,13 +261,13 @@ def generate_zeta_timestep_info(self, structure_tstep, aero_tstep, beam, aero_se node_info = dict() node_info['i_node'] = i_global_node node_info['i_local_node'] = i_local_node - node_info['chord'] = self.aero_dict['chord'][i_elem, i_local_node] - node_info['eaxis'] = self.aero_dict['elastic_axis'][i_elem, i_local_node] - node_info['twist'] = self.aero_dict['twist'][i_elem, i_local_node] - node_info['sweep'] = self.aero_dict['sweep'][i_elem, i_local_node] - node_info['M'] = self.aero_dimensions[i_surf, 0] - node_info['M_distribution'] = self.aero_dict['m_distribution'].decode('ascii') - node_info['airfoil'] = self.aero_dict['airfoil_distribution'][i_elem, i_local_node] + node_info['chord'] = self.data_dict['chord'][i_elem, i_local_node] + node_info['eaxis'] = self.data_dict['elastic_axis'][i_elem, i_local_node] + node_info['twist'] = self.data_dict['twist'][i_elem, i_local_node] + node_info['sweep'] = self.data_dict['sweep'][i_elem, i_local_node] + node_info['M'] = self.dimensions[i_surf, 0] + node_info['M_distribution'] = self.data_dict['m_distribution'].decode('ascii') + node_info['airfoil'] = self.data_dict['airfoil_distribution'][i_elem, i_local_node] node_info['control_surface'] = control_surface_info node_info['beam_coord'] = structure_tstep.pos[i_global_node, :] node_info['pos_dot'] = structure_tstep.pos_dot[i_global_node, :] @@ -339,76 +277,19 @@ def generate_zeta_timestep_info(self, structure_tstep, aero_tstep, beam, aero_se node_info['elem'] = beam.elements[i_elem] node_info['for_pos'] = structure_tstep.for_pos node_info['cga'] = structure_tstep.cga() + # print("PSI WING") + # print(structure_tstep.psi[i_elem]) if node_info['M_distribution'].lower() == 'user_defined': ielem_in_surf = i_elem - np.sum(self.surface_distribution < i_surf) - node_info['user_defined_m_distribution'] = self.aero_dict['user_defined_m_distribution'][str(i_surf)][:, ielem_in_surf, i_local_node] + node_info['user_defined_m_distribution'] = self.data_dict['user_defined_m_distribution'][str(i_surf)][:, ielem_in_surf, i_local_node] (aero_tstep.zeta[i_surf][:, :, i_n], aero_tstep.zeta_dot[i_surf][:, :, i_n]) = ( generate_strip(node_info, self.airfoil_db, - aero_settings['aligned_grid'], - orientation_in=aero_settings['freestream_dir'], + self.settings['aligned_grid'], + orientation_in=self.settings['freestream_dir'], calculate_zeta_dot=True)) - def generate_zeta(self, beam, aero_settings, ts=-1, beam_ts=-1): - self.generate_zeta_timestep_info(beam.timestep_info[beam_ts], - self.timestep_info[ts], - beam, - aero_settings) - - def generate_mapping(self): - self.struct2aero_mapping = [[]]*self.n_node - surf_n_counter = np.zeros((self.n_surf,), dtype=int) - nodes_in_surface = [] - for i_surf in range(self.n_surf): - nodes_in_surface.append([]) - - for i_elem in range(self.n_elem): - i_surf = self.aero_dict['surface_distribution'][i_elem] - if i_surf == -1: - continue - for i_global_node in self.beam.elements[i_elem].reordered_global_connectivities: - if not self.aero_dict['aero_node'][i_global_node]: - continue - - if i_global_node in nodes_in_surface[i_surf]: - continue - else: - nodes_in_surface[i_surf].append(i_global_node) - surf_n_counter[i_surf] += 1 - try: - self.struct2aero_mapping[i_global_node][0] - except IndexError: - self.struct2aero_mapping[i_global_node] = [] - - i_n = surf_n_counter[i_surf] - 1 - self.struct2aero_mapping[i_global_node].append({'i_surf': i_surf, - 'i_n': i_n}) - - nodes_in_surface = [] - for i_surf in range(self.n_surf): - nodes_in_surface.append([]) - - for i_surf in range(self.n_surf): - self.aero2struct_mapping.append([-1]*(surf_n_counter[i_surf])) - - for i_elem in range(self.n_elem): - for i_global_node in self.beam.elements[i_elem].global_connectivities: - for i in range(len(self.struct2aero_mapping[i_global_node])): - try: - i_surf = self.struct2aero_mapping[i_global_node][i]['i_surf'] - i_n = self.struct2aero_mapping[i_global_node][i]['i_n'] - if i_global_node in nodes_in_surface[i_surf]: - continue - else: - nodes_in_surface[i_surf].append(i_global_node) - except KeyError: - continue - self.aero2struct_mapping[i_surf][i_n] = i_global_node - - def update_orientation(self, quat, ts=-1): - rot = algebra.quat2rotation(quat) - self.timestep_info[ts].update_orientation(rot.T) @staticmethod def compute_gamma_dot(dt, tstep, previous_tsteps): From 21b37a0b51bec079f5470dc89d03318027220437 Mon Sep 17 00:00:00 2001 From: sduess Date: Mon, 7 Dec 2020 13:48:52 +0000 Subject: [PATCH 013/232] add: Get grid nodes in A frame - Nodes position are first calculate in B frame and are then converted to the A frame - add necessary and delete unnecessary imported packages --- sharpy/aero/models/nonlifting_body_grid.py | 56 ++++++++++++---------- 1 file changed, 32 insertions(+), 24 deletions(-) diff --git a/sharpy/aero/models/nonlifting_body_grid.py b/sharpy/aero/models/nonlifting_body_grid.py index 7f2da7be0..cd67ffa91 100644 --- a/sharpy/aero/models/nonlifting_body_grid.py +++ b/sharpy/aero/models/nonlifting_body_grid.py @@ -6,10 +6,8 @@ from sharpy.aero.models.grid import Grid from sharpy.utils.datastructures import NonliftingBodyTimeStepInfo import numpy as np -import itertools from scipy.optimize import fsolve - -import pandas as pd +import sharpy.utils.algebra as algebra class Nonlifting_body_grid(Grid): @@ -35,14 +33,14 @@ def generate(self, data_dict, beam, nonlifting_body_settings, ts): ##input? self.generate_zeta(self.beam, self.aero_settings, ts) def generate_zeta_timestep_info(self, structure_tstep, nonlifting_body_tstep, beam, aero_settings, it=None, dt=None): - super().generate_zeta_timestep_info(structure_tstep, nonlifting_body_tstep, beam, aero_settings, it, dt) + super().generate_zeta_timestep_info(structure_tstep, nonlifting_body_tstep, beam, aero_settings, it, dt) - for i_surf in range(self.n_surf): - # Get Zeta (Collocation point positions in A? frame) - # TO-DO: Consider fuselage deformation for node position calculations - nonlifting_body_tstep.zeta[i_surf] = self.get_collocation_point_pos(i_surf, self.dimensions[i_surf][-1], structure_tstep) - # TO-DO: Add Zeta Dot Calculation - # aero_tstep.zeta_dot[i_surf] + for i_surf in range(self.n_surf): + # Get Zeta (Collocation point positions in A? frame) + # TO-DO: Consider fuselage deformation for node position calculations + nonlifting_body_tstep.zeta[i_surf] = self.get_collocation_point_pos(i_surf, self.dimensions[i_surf][-1], structure_tstep) + # TO-DO: Add Zeta Dot Calculation + # aero_tstep.zeta_dot[i_surf] def get_triangle_center(self, p1, p2, p3): return (p1+p2+p3)/3 @@ -65,29 +63,33 @@ def get_nodes_position(self,i_surf, structure_tstep, numb_radial_nodes): matrix_nodes = np.zeros(((self.dimensions[i_surf][1]-1) *(numb_radial_nodes)+2, 3)) array_phi_coordinates = np.linspace(0, 2*np.pi, numb_radial_nodes) - print(i_surf) - print(self.aero2struct_mapping) - print(self.struct2aero_mapping) # cache sin and cos values array_sin_phi = np.sin(array_phi_coordinates) array_cos_phi = np.cos(array_phi_coordinates) + phi_counter = 0 row_idx_start, row_idx_end = 0, 0 for i_global_node in self.aero2struct_mapping[i_surf]: radius = self.data_dict["radius"][i_global_node] - # axissymmetric body has only one node at nose and tail (r = 0) if radius == 0.0: + # axissymmetric body has only one node at nose and tail (r = 0) + # position is already in A frame matrix_nodes[row_idx_start, :] = structure_tstep.pos[i_global_node, :] row_idx_start += 1 else: row_idx_end = row_idx_start + numb_radial_nodes - matrix_nodes[row_idx_start:row_idx_end, 0] = structure_tstep.pos[i_global_node, 0] - matrix_nodes[row_idx_start:row_idx_end, 1] = np.array( - structure_tstep.pos[i_global_node, 1] - ) + radius*array_cos_phi - matrix_nodes[row_idx_start:row_idx_end, 2] = np.array( - structure_tstep.pos[i_global_node, 2] - ) + radius*array_sin_phi + # get nodes position in B frame + matrix_nodes[row_idx_start:row_idx_end, 0] = 0 + matrix_nodes[row_idx_start:row_idx_end, 1] = radius*array_cos_phi + matrix_nodes[row_idx_start:row_idx_end, 2] = radius*array_sin_phi + # convert position from B to A frame + psi_node = self.get_psi(i_surf, i_global_node, structure_tstep) + if not (psi_node == [0, 0, 0]).all(): + # just perform roation from B to A if psi not 0 + Cab = algebra.crv2rotation(psi_node) + for idx in range(row_idx_start, row_idx_end): + matrix_nodes[idx,:] = np.dot(Cab, matrix_nodes[idx,:]) + matrix_nodes[row_idx_start:row_idx_end,:] += structure_tstep.pos[i_global_node,:] row_idx_start += numb_radial_nodes phi_counter += 1 @@ -99,11 +101,9 @@ def get_collocation_point_pos(self, i_surf, numb_spanwise_elements, structure_ts matrix_nodes = self.get_nodes_position(i_surf, structure_tstep, numb_radial_nodes) counter, i_row = 0, 0 matrix_collocation = np.zeros(((numb_spanwise_elements)*(numb_radial_nodes-1),3)) - print(numb_spanwise_elements, numb_radial_nodes) for i in range(0, numb_spanwise_elements): if i == 0: for i_rad_node in range(0, numb_radial_nodes-1): - print(i_rad_node, counter) matrix_collocation[i_row] = self.get_triangle_center( matrix_nodes[0], matrix_nodes[i_rad_node+1], @@ -127,9 +127,17 @@ def get_collocation_point_pos(self, i_surf, numb_spanwise_elements, structure_ts counter+=1 i_row+=1 counter+=1 - np.savetxt("./collocation.csv", matrix_collocation, delimiter=",") return matrix_collocation + def get_psi(self, i_surf, i_global_node, structure_tstep): + # get beam elements of surface + idx_beam_elements_surface = np.where(self.surface_distribution == i_surf)[0] + # find element and local node of the global node and return psi + for i_elem in idx_beam_elements_surface: + if i_global_node in self.beam.elements[i_elem].reordered_global_connectivities: + i_local_node = np.where(self.beam.elements[i_elem].reordered_global_connectivities == i_global_node)[0][0] + return structure_tstep.psi[i_elem, i_local_node, :] + raise Exception("The global node %u could not be assigned to any element of surface %u." % (i_global_node, i_surf)) def find_intersection_points(self, array_points): sol_parameter = fsolve(self.intersection_point_equation, From 0d46ddaa663a7f4fa3d3a610fc5c2fce03ebe7c2 Mon Sep 17 00:00:00 2001 From: sduess Date: Mon, 7 Dec 2020 13:58:47 +0000 Subject: [PATCH 014/232] rename: Rename attribute to match parent class --- sharpy/aero/models/aerogrid.py | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/sharpy/aero/models/aerogrid.py b/sharpy/aero/models/aerogrid.py index 409c98133..8ac2161a6 100644 --- a/sharpy/aero/models/aerogrid.py +++ b/sharpy/aero/models/aerogrid.py @@ -89,7 +89,7 @@ def generate(self, data_dict, beam, settings, ts): raise TypeError('Control surface {:g} is defined as static but there is a control surface generator' 'associated with it'.format(i_cs)) generator_type = gen_interface.generator_from_string( - aero_settings['control_surface_deflection'][i_cs]) + settings['control_surface_deflection'][i_cs]) self.cs_generators.append(generator_type()) try: self.cs_generators[i_cs].initialise( @@ -105,9 +105,9 @@ def generate(self, data_dict, beam, settings, ts): self.add_timestep() self.generate_mapping() - self.generate_zeta(self.beam, self.settings, ts) + self.generate_zeta(self.beam, self.aero_settings, ts) - if 'polars' in self.data_dict: + if 'polars' in self.data_dict: import sharpy.aero.utils.airfoilpolars as ap self.polars = [] nairfoils = np.amax(self.data_dict['airfoil_distribution']) + 1 @@ -139,9 +139,9 @@ def calculate_dimensions(self): self.dimensions_star = self.dimensions.copy() for i_surf in range(self.n_surf): - self.dimensions_star[i_surf, 0] = self.settings['mstar'].value + self.dimensions_star[i_surf, 0] = self.aero_settings['mstar'].value - def generate_zeta_timestep_info(self, structure_tstep, aero_tstep, beam, aero_settings, it=None, dt=None): + def generate_zeta_timestep_info(self, structure_tstep, aero_tstep, beam, settings, it=None, dt=None): if it is None: it = len(beam.timestep_info) - 1 global_node_in_surface = [] @@ -277,8 +277,6 @@ def generate_zeta_timestep_info(self, structure_tstep, aero_tstep, beam, aero_se node_info['elem'] = beam.elements[i_elem] node_info['for_pos'] = structure_tstep.for_pos node_info['cga'] = structure_tstep.cga() - # print("PSI WING") - # print(structure_tstep.psi[i_elem]) if node_info['M_distribution'].lower() == 'user_defined': ielem_in_surf = i_elem - np.sum(self.surface_distribution < i_surf) node_info['user_defined_m_distribution'] = self.data_dict['user_defined_m_distribution'][str(i_surf)][:, ielem_in_surf, i_local_node] @@ -286,8 +284,8 @@ def generate_zeta_timestep_info(self, structure_tstep, aero_tstep, beam, aero_se aero_tstep.zeta_dot[i_surf][:, :, i_n]) = ( generate_strip(node_info, self.airfoil_db, - self.settings['aligned_grid'], - orientation_in=self.settings['freestream_dir'], + self.aero_settings['aligned_grid'], + orientation_in=self.aero_settings['freestream_dir'], calculate_zeta_dot=True)) From f1ead917662b32a177eeb4f065b06d20f29fa6cd Mon Sep 17 00:00:00 2001 From: sduess Date: Tue, 8 Dec 2020 12:25:37 +0000 Subject: [PATCH 015/232] add: Gridloader and Nonliftingbodygridloader classes - Gridloader acts as a parent class for nonliftingbodygridloader class - Nonliftingbodygridloader generates aerodynamic grid for nonlifitng bodies similiar to aerogrid --- sharpy/solvers/gridloader.py | 72 ++++++++++++++++++++++ sharpy/solvers/nonliftingbodygridloader.py | 62 +++++++++++++++++++ 2 files changed, 134 insertions(+) create mode 100644 sharpy/solvers/gridloader.py create mode 100644 sharpy/solvers/nonliftingbodygridloader.py diff --git a/sharpy/solvers/gridloader.py b/sharpy/solvers/gridloader.py new file mode 100644 index 000000000..448c534f2 --- /dev/null +++ b/sharpy/solvers/gridloader.py @@ -0,0 +1,72 @@ +import h5py as h5 +import numpy as np + +from sharpy.utils.solver_interface import solver, BaseSolver +import sharpy.utils.settings as settings_utils +import sharpy.utils.h5utils as h5utils + + +@solver +class GridLoader(BaseSolver): + """ + ``GridLoader`` class, inherited from ``BaseSolver`` + + Parent class for Aerogridloader and Nonliftingbodygridloader. Both classes + generate aerodynamic grids based on the input data + + Args: + data (PreSharpy): ``ProblemData`` class structure + + Attributes: + settings (dict): Name-value pair of the settings employed by the aerodynamic solver + settings_types (dict): Acceptable types for the values in ``settings`` + settings_default (dict): Name-value pair of default values for the aerodynamic settings + data (ProblemData): class structure + afile_name (str): name of the HDF5 file, e.g. ``.aero.h5`` + aero: empty attribute + data_dict (dict): key-value pairs of aerodynamic data + + """ + solver_id = 'GridLoader' + solver_classification = 'loader' + + settings_types = dict() + settings_default = dict() + settings_description = dict() + + def __init__(self): + self.data = None + self.settings = None + self.file_name = '' + # storage of file contents + self.data_dict = dict() + + # aero storage + #self.aero = None + + def initialise(self, data): + self.data = data + self.settings = data.settings[self.solver_id] + + # init settings + settings_utils.to_custom_types(self.settings, + self.settings_types, + self.settings_default) + + # read input file + self.read_files() + + + def read_files(self): + # first check that the file exists + self.file_name = (self.data.case_route + + '/' + + self.data.case_name + + self.file_name) + # first check that the file exists + h5utils.check_file_exists(self.file_name) + + # read and store the hdf5 file + with h5.File(self.file_name, 'r') as file_handle: + # store files in dictionary + self.data_dict = h5utils.load_h5_in_dict(file_handle) \ No newline at end of file diff --git a/sharpy/solvers/nonliftingbodygridloader.py b/sharpy/solvers/nonliftingbodygridloader.py new file mode 100644 index 000000000..a2e8667e3 --- /dev/null +++ b/sharpy/solvers/nonliftingbodygridloader.py @@ -0,0 +1,62 @@ +from sharpy.utils.solver_interface import solver +import sharpy.aero.models.nonlifting_body_grid as nonlifting_body_grid +import sharpy.utils.settings as settings_utils +from sharpy.solvers.gridloader import GridLoader + + +@solver +class NonliftingbodygridLoader(GridLoader): + """ + ``NonliftingbodygridLoader`` class, inherited from ``GridLoader`` + + Generates aerodynamic grid for nonlifting bodies based on the input data + + Args: + data (PreSharpy): ``ProblemData`` class structure + + Attributes: + settings (dict): Name-value pair of the settings employed by the aerodynamic solver + settings_types (dict): Acceptable types for the values in ``settings`` + settings_default (dict): Name-value pair of default values for the aerodynamic settings + data (ProblemData): class structure + file_name (str): name of the ``.nonlifting_body.h5`` HDF5 file + aero: empty attribute + aero_data_dict (dict): key-value pairs of aerodynamic data + + + """ + solver_id = 'NonliftingbodygridLoader' + solver_classification = 'loader' + + settings_types = dict() + settings_default = dict() + settings_description = dict() + + settings_types['unsteady'] = 'bool' + settings_default['unsteady'] = False + settings_description['unsteady'] = 'Unsteady effects' + + settings_types['aligned_grid'] = 'bool' + settings_default['aligned_grid'] = True + settings_description['aligned_grid'] = 'Align grid' + + settings_types['freestream_dir'] = 'list(float)' + settings_default['freestream_dir'] = [1.0, 0.0, 0.0] + settings_description['freestream_dir'] = 'Free stream flow direction' + + settings_table = settings_utils.SettingsTable() + __doc__ += settings_table.generate(settings_types, settings_default, settings_description) + def __init__(self): + super().__init__() + self.file_name = '.nonlifting_body.h5' + + # nonlifting_body storage + self.nonlifting_body = None + + def run(self): + self.data.nonlifting_body = nonlifting_body_grid.Nonlifting_body_grid() + self.data.nonlifting_body.generate(self.data_dict, + self.data.structure, + self.settings, + self.data.ts) + return self.data From d2592c5b60439ac45ea2e33f43e20de46e1f961c Mon Sep 17 00:00:00 2001 From: sduess Date: Tue, 8 Dec 2020 12:26:41 +0000 Subject: [PATCH 016/232] refactor: delete import of unused modules --- sharpy/aero/models/grid.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/sharpy/aero/models/grid.py b/sharpy/aero/models/grid.py index 47e1f328d..176a7a5e6 100644 --- a/sharpy/aero/models/grid.py +++ b/sharpy/aero/models/grid.py @@ -6,11 +6,8 @@ import warnings import numpy as np -import scipy.interpolate import sharpy.utils.algebra as algebra -import sharpy.utils.cout_utils as cout -from sharpy.utils.datastructures import AeroTimeStepInfo import sharpy.utils.generator_interface as gen_interface From d07c5008ff53c69025d5ed88fa28f3bf0009c3d4 Mon Sep 17 00:00:00 2001 From: sduess Date: Tue, 8 Dec 2020 12:32:36 +0000 Subject: [PATCH 017/232] refactor: Aerogridloader is now inherited from Gridloader --- sharpy/solvers/aerogridloader.py | 45 +++++++------------------------- 1 file changed, 9 insertions(+), 36 deletions(-) diff --git a/sharpy/solvers/aerogridloader.py b/sharpy/solvers/aerogridloader.py index 486ee70d7..20476df68 100644 --- a/sharpy/solvers/aerogridloader.py +++ b/sharpy/solvers/aerogridloader.py @@ -6,12 +6,13 @@ import sharpy.utils.settings as settings_utils import sharpy.utils.h5utils as h5utils import sharpy.utils.generator_interface as gen_interface +from sharpy.solves.gridloader import GridLoader @solver -class AerogridLoader(BaseSolver): +class AerogridLoader(GridLoader): """ - ``AerogridLoader`` class, inherited from ``BaseSolver`` + ``AerogridLoader`` class, inherited from ``GridLoader`` Generates aerodynamic grid based on the input data @@ -23,9 +24,9 @@ class AerogridLoader(BaseSolver): settings_types (dict): Acceptable types for the values in ``settings`` settings_default (dict): Name-value pair of default values for the aerodynamic settings data (ProblemData): class structure - aero_file_name (str): name of the ``.aero.h5`` HDF5 file + file_name (str): name of the ``.aero.h5`` HDF5 file aero: empty attribute - aero_data_dict (dict): key-value pairs of aerodynamic data + data_dict (dict): key-value pairs of aerodynamic data wake_shape_generator (class): Wake shape generator Notes: @@ -86,11 +87,8 @@ class AerogridLoader(BaseSolver): __doc__ += settings_table.generate(settings_types, settings_default, settings_description) def __init__(self): - self.data = None - self.settings = None - self.aero_file_name = '' - # storage of file contents - self.aero_data_dict = dict() + super().__init__ + self.file_name = '.aero.h5' # aero storage self.aero = None @@ -98,16 +96,7 @@ def __init__(self): self.wake_shape_generator = None def initialise(self, data): - self.data = data - self.settings = data.settings[self.solver_id] - - # init settings - settings_utils.to_custom_types(self.settings, - self.settings_types, - self.settings_default) - - # read input file (aero) - self.read_files() + super().initialise(data) wake_shape_generator_type = gen_interface.generator_from_string( self.settings['wake_shape_generator']) @@ -115,25 +104,9 @@ def initialise(self, data): self.wake_shape_generator.initialise(data, self.settings['wake_shape_generator_input']) - def read_files(self): - # open aero file - # first, file names - self.aero_file_name = (self.data.case_route + - '/' + - self.data.case_name + - '.aero.h5') - - # then check that the file exists - h5utils.check_file_exists(self.aero_file_name) - - # read and store the hdf5 file - with h5.File(self.aero_file_name, 'r') as aero_file_handle: - # store files in dictionary - self.aero_data_dict = h5utils.load_h5_in_dict(aero_file_handle) - def run(self): self.data.aero = aerogrid.Aerogrid() - self.data.aero.generate(self.aero_data_dict, + self.data.aero.generate(self.data_dict, self.data.structure, self.settings, self.data.ts) From fcbec9345a8345e9566a920dbaa715b07f6f86b8 Mon Sep 17 00:00:00 2001 From: sduess Date: Tue, 8 Dec 2020 16:05:20 +0000 Subject: [PATCH 018/232] fix: store nodes in zeta and not collocation points --- sharpy/aero/models/nonlifting_body_grid.py | 65 ++++++++++------------ 1 file changed, 30 insertions(+), 35 deletions(-) diff --git a/sharpy/aero/models/nonlifting_body_grid.py b/sharpy/aero/models/nonlifting_body_grid.py index cd67ffa91..ae6085076 100644 --- a/sharpy/aero/models/nonlifting_body_grid.py +++ b/sharpy/aero/models/nonlifting_body_grid.py @@ -36,11 +36,8 @@ def generate_zeta_timestep_info(self, structure_tstep, nonlifting_body_tstep, be super().generate_zeta_timestep_info(structure_tstep, nonlifting_body_tstep, beam, aero_settings, it, dt) for i_surf in range(self.n_surf): - # Get Zeta (Collocation point positions in A? frame) - # TO-DO: Consider fuselage deformation for node position calculations - nonlifting_body_tstep.zeta[i_surf] = self.get_collocation_point_pos(i_surf, self.dimensions[i_surf][-1], structure_tstep) - # TO-DO: Add Zeta Dot Calculation - # aero_tstep.zeta_dot[i_surf] + # Get Zeta and Zeta_dot (Panel node positions in G frame) + nonlifting_body_tstep.zeta[i_surf] = self.get_zeta_and_zeta_dot(i_surf, structure_tstep) def get_triangle_center(self, p1, p2, p3): return (p1+p2+p3)/3 @@ -59,40 +56,38 @@ def get_quadrilateral_center(self, list_points): centroid_quadrilateral = self.find_intersection_points(array_triangle_centroids) return centroid_quadrilateral - def get_nodes_position(self,i_surf, structure_tstep, numb_radial_nodes): - matrix_nodes = np.zeros(((self.dimensions[i_surf][1]-1) - *(numb_radial_nodes)+2, 3)) + def get_zeta_and_zeta_dot(self,i_surf, structure_tstep): + numb_radial_nodes = self.dimensions[i_surf][0] +1 + matrix_nodes = np.zeros((3, numb_radial_nodes, + self.dimensions[i_surf][1]+1)) array_phi_coordinates = np.linspace(0, 2*np.pi, numb_radial_nodes) # cache sin and cos values array_sin_phi = np.sin(array_phi_coordinates) array_cos_phi = np.cos(array_phi_coordinates) - phi_counter = 0 - row_idx_start, row_idx_end = 0, 0 - for i_global_node in self.aero2struct_mapping[i_surf]: - radius = self.data_dict["radius"][i_global_node] - if radius == 0.0: - # axissymmetric body has only one node at nose and tail (r = 0) - # position is already in A frame - matrix_nodes[row_idx_start, :] = structure_tstep.pos[i_global_node, :] - row_idx_start += 1 - else: - row_idx_end = row_idx_start + numb_radial_nodes - # get nodes position in B frame - matrix_nodes[row_idx_start:row_idx_end, 0] = 0 - matrix_nodes[row_idx_start:row_idx_end, 1] = radius*array_cos_phi - matrix_nodes[row_idx_start:row_idx_end, 2] = radius*array_sin_phi - # convert position from B to A frame - psi_node = self.get_psi(i_surf, i_global_node, structure_tstep) - if not (psi_node == [0, 0, 0]).all(): - # just perform roation from B to A if psi not 0 - Cab = algebra.crv2rotation(psi_node) - for idx in range(row_idx_start, row_idx_end): - matrix_nodes[idx,:] = np.dot(Cab, matrix_nodes[idx,:]) - matrix_nodes[row_idx_start:row_idx_end,:] += structure_tstep.pos[i_global_node,:] - row_idx_start += numb_radial_nodes - phi_counter += 1 + cga_rotation_matrix = structure_tstep.cga() + for node_counter, i_global_node in enumerate(self.aero2struct_mapping[i_surf]): + radius = self.data_dict["radius"][i_global_node] + # get nodes position in B frame + #matrix_nodes[0, :numb_radial_nodes, node_counter] = 0 + matrix_nodes[1, :numb_radial_nodes, node_counter] = radius*array_cos_phi + matrix_nodes[2, :numb_radial_nodes, node_counter] = radius*array_sin_phi + # convert position from B to A frame + i_elem, i_local_node = self.get_elment_and_local_node_id(i_surf, i_global_node) + psi_node = structure_tstep.psi[i_elem, i_local_node,:] + if not (psi_node == [0, 0, 0]).all(): + # just perform roation from B to A if psi not 0 + Cab = algebra.crv2rotation(psi_node) + for idx in range(numb_radial_nodes): + matrix_nodes[:, idx, node_counter] = np.dot(Cab, matrix_nodes[:, idx, node_counter]) + for dim in range(3): + matrix_nodes[dim, :, node_counter] += structure_tstep.pos[i_global_node,dim] + + for idx in range(numb_radial_nodes): + # convert position from A to G frame + matrix_nodes[:, idx, node_counter] = np.dot(cga_rotation_matrix, + matrix_nodes[:, idx, node_counter]) return matrix_nodes @@ -129,14 +124,14 @@ def get_collocation_point_pos(self, i_surf, numb_spanwise_elements, structure_ts counter+=1 return matrix_collocation - def get_psi(self, i_surf, i_global_node, structure_tstep): + def get_elment_and_local_node_id(self, i_surf, i_global_node): # get beam elements of surface idx_beam_elements_surface = np.where(self.surface_distribution == i_surf)[0] # find element and local node of the global node and return psi for i_elem in idx_beam_elements_surface: if i_global_node in self.beam.elements[i_elem].reordered_global_connectivities: i_local_node = np.where(self.beam.elements[i_elem].reordered_global_connectivities == i_global_node)[0][0] - return structure_tstep.psi[i_elem, i_local_node, :] + return i_elem, i_local_node raise Exception("The global node %u could not be assigned to any element of surface %u." % (i_global_node, i_surf)) def find_intersection_points(self, array_points): From d14d6522e9181390ccea28f2dced9fa7fe221142 Mon Sep 17 00:00:00 2001 From: sduess Date: Tue, 8 Dec 2020 16:06:12 +0000 Subject: [PATCH 019/232] add: calculate zeta dot and store it in NonliftingbodyTimeStepInfo --- sharpy/aero/models/nonlifting_body_grid.py | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/sharpy/aero/models/nonlifting_body_grid.py b/sharpy/aero/models/nonlifting_body_grid.py index ae6085076..1e20dbddc 100644 --- a/sharpy/aero/models/nonlifting_body_grid.py +++ b/sharpy/aero/models/nonlifting_body_grid.py @@ -37,7 +37,7 @@ def generate_zeta_timestep_info(self, structure_tstep, nonlifting_body_tstep, be for i_surf in range(self.n_surf): # Get Zeta and Zeta_dot (Panel node positions in G frame) - nonlifting_body_tstep.zeta[i_surf] = self.get_zeta_and_zeta_dot(i_surf, structure_tstep) + nonlifting_body_tstep.zeta[i_surf], nonlifting_body_tstep.zeta_dot[i_surf] = self.get_zeta_and_zeta_dot(i_surf, structure_tstep) def get_triangle_center(self, p1, p2, p3): return (p1+p2+p3)/3 @@ -60,6 +60,7 @@ def get_zeta_and_zeta_dot(self,i_surf, structure_tstep): numb_radial_nodes = self.dimensions[i_surf][0] +1 matrix_nodes = np.zeros((3, numb_radial_nodes, self.dimensions[i_surf][1]+1)) + matrix_nodes_dot = matrix_nodes.copy() array_phi_coordinates = np.linspace(0, 2*np.pi, numb_radial_nodes) # cache sin and cos values array_sin_phi = np.sin(array_phi_coordinates) @@ -76,6 +77,8 @@ def get_zeta_and_zeta_dot(self,i_surf, structure_tstep): # convert position from B to A frame i_elem, i_local_node = self.get_elment_and_local_node_id(i_surf, i_global_node) psi_node = structure_tstep.psi[i_elem, i_local_node,:] + psi_dot_node = structure_tstep.psi_dot[i_elem, i_local_node,:] + omega_a = algebra.crv_dot2omega(psi_node, psi_dot_node) if not (psi_node == [0, 0, 0]).all(): # just perform roation from B to A if psi not 0 Cab = algebra.crv2rotation(psi_node) @@ -84,11 +87,18 @@ def get_zeta_and_zeta_dot(self,i_surf, structure_tstep): for dim in range(3): matrix_nodes[dim, :, node_counter] += structure_tstep.pos[i_global_node,dim] + # get zeta dot in A frame (velocity due to pos_dot) + matrix_nodes_dot[dim, :numb_radial_nodes, node_counter] += structure_tstep.pos_dot[i_global_node, dim] + for idx in range(numb_radial_nodes): + # get zeta dot in A frame (velocity due to psi_dot)) + matrix_nodes_dot[:, idx, node_counter] += (np.dot(algebra.skew(omega_a), matrix_nodes[:, idx, node_counter])) # convert position from A to G frame matrix_nodes[:, idx, node_counter] = np.dot(cga_rotation_matrix, matrix_nodes[:, idx, node_counter]) - return matrix_nodes + matrix_nodes_dot[:, idx, node_counter] = np.dot(cga_rotation_matrix, + matrix_nodes_dot[:, idx, node_counter]) + return matrix_nodes, matrix_nodes_dot def get_collocation_point_pos(self, i_surf, numb_spanwise_elements, structure_tstep): From 2558b2440cf7fd854d38dd229a6cc54c7e284539 Mon Sep 17 00:00:00 2001 From: sduess Date: Tue, 8 Dec 2020 16:11:25 +0000 Subject: [PATCH 020/232] fix: update dimensions in datastructures - allows to simplify AerogridTimeStepInfo as a side effect since the array dimensions align with NonliftingBodyTimeStepInfo now --- sharpy/utils/datastructures.py | 70 ++++++++-------------------------- 1 file changed, 16 insertions(+), 54 deletions(-) diff --git a/sharpy/utils/datastructures.py b/sharpy/utils/datastructures.py index 682ab3880..ad89449f0 100644 --- a/sharpy/utils/datastructures.py +++ b/sharpy/utils/datastructures.py @@ -67,46 +67,45 @@ def __init__(self, dimensions): # generate placeholder for aero grid zeta coordinates self.zeta = [] for i_surf in range(self.n_surf): - self.zeta.append(np.zeros((3, #x,y,z - dimensions[i_surf, 0], - dimensions[i_surf, 1]), + self.zeta.append(np.zeros((3, + dimensions[i_surf, 0] + 1, + dimensions[i_surf, 1] + 1), dtype=ct.c_double)) - self.zeta_dot = [] for i_surf in range(self.n_surf): self.zeta_dot.append(np.zeros((3, - dimensions[i_surf, 0], - dimensions[i_surf, 1]), + dimensions[i_surf, 0] + 1, + dimensions[i_surf, 1] + 1), dtype=ct.c_double)) # panel normals self.normals = [] for i_surf in range(self.n_surf): - self.normals.append(np.zeros((3, - dimensions[i_surf, 0], - dimensions[i_surf, 1]), - dtype=ct.c_double)) - + self.normals.append(np.zeros((6, + dimensions[i_surf, 0], + dimensions[i_surf, 1]), + dtype=ct.c_double)) # panel forces self.forces = [] for i_surf in range(self.n_surf): self.forces.append(np.zeros((6, - dimensions[i_surf, 0], - dimensions[i_surf, 1]), + dimensions[i_surf, 0] + 1, + dimensions[i_surf, 1] + 1), dtype=ct.c_double)) # panel forces self.dynamic_forces = [] for i_surf in range(self.n_surf): self.dynamic_forces.append(np.zeros((6, - dimensions[i_surf, 0], - dimensions[i_surf, 1]), + dimensions[i_surf, 0] + 1, + dimensions[i_surf, 1] + 1), dtype=ct.c_double)) + # placeholder for external velocity self.u_ext = [] for i_surf in range(self.n_surf): self.u_ext.append(np.zeros((3, - dimensions[i_surf, 0], - dimensions[i_surf, 1]), + dimensions[i_surf, 0] + 1, + dimensions[i_surf, 1] + 1), dtype=ct.c_double)) # total forces @@ -394,7 +393,6 @@ def remove_ctypes_pointers(self): pass - class AeroTimeStepInfo(TimeStepInfo): """ Aerodynamic Time step class. @@ -461,34 +459,6 @@ def __init__(self, dimensions, dimensions_star): self.ct_dimensions_star = None self.dimensions_star = dimensions_star.copy() - # generate placeholder for aero grid zeta coordinates - self.zeta = [] - for i_surf in range(self.n_surf): - self.zeta.append(np.zeros((3, - dimensions[i_surf, 0] + 1, - dimensions[i_surf, 1] + 1), - dtype=ct.c_double)) - self.zeta_dot = [] - for i_surf in range(self.n_surf): - self.zeta_dot.append(np.zeros((3, - dimensions[i_surf, 0] + 1, - dimensions[i_surf, 1] + 1), - dtype=ct.c_double)) - - # panel forces - self.forces = [] - for i_surf in range(self.n_surf): - self.forces.append(np.zeros((6, - dimensions[i_surf, 0] + 1, - dimensions[i_surf, 1] + 1), - dtype=ct.c_double)) - # panel forces - self.dynamic_forces = [] - for i_surf in range(self.n_surf): - self.dynamic_forces.append(np.zeros((6, - dimensions[i_surf, 0] + 1, - dimensions[i_surf, 1] + 1), - dtype=ct.c_double)) # generate placeholder for aero grid zeta_star coordinates self.zeta_star = [] @@ -498,14 +468,6 @@ def __init__(self, dimensions, dimensions_star): dimensions_star[i_surf, 1] + 1), dtype=ct.c_double)) - # placeholder for external velocity - self.u_ext = [] - for i_surf in range(self.n_surf): - self.u_ext.append(np.zeros((3, - dimensions[i_surf, 0] + 1, - dimensions[i_surf, 1] + 1), - dtype=ct.c_double)) - self.u_ext_star = [] for i_surf in range(self.n_surf): self.u_ext_star.append(np.zeros((3, From 72d062ddefdacb2e180b31e6c080e0e4a39d0ab0 Mon Sep 17 00:00:00 2001 From: sduess Date: Tue, 8 Dec 2020 16:15:36 +0000 Subject: [PATCH 021/232] refactor: Delete unnecessary functions - Delete functions that are no longer needed since zeta contains only grid nodes instead of collocation points --- sharpy/aero/models/nonlifting_body_grid.py | 66 ---------------------- 1 file changed, 66 deletions(-) diff --git a/sharpy/aero/models/nonlifting_body_grid.py b/sharpy/aero/models/nonlifting_body_grid.py index 1e20dbddc..3ea1ff99e 100644 --- a/sharpy/aero/models/nonlifting_body_grid.py +++ b/sharpy/aero/models/nonlifting_body_grid.py @@ -39,23 +39,6 @@ def generate_zeta_timestep_info(self, structure_tstep, nonlifting_body_tstep, be # Get Zeta and Zeta_dot (Panel node positions in G frame) nonlifting_body_tstep.zeta[i_surf], nonlifting_body_tstep.zeta_dot[i_surf] = self.get_zeta_and_zeta_dot(i_surf, structure_tstep) - def get_triangle_center(self, p1, p2, p3): - return (p1+p2+p3)/3 - - def get_quadrilateral_center(self, list_points): - # based on http://jwilson.coe.uga.edu/EMT668/EMT668.Folders.F97/Patterson/EMT%20669/centroid%20of%20quad/Centroid.html - array_triangle_centroids = np.zeros((4,3)) - counter = 0 - triangle_combinations = [[0, 1, 2], [0, 2, 3], [0, 1, 3], [1, 2, 3]] - for triangle in triangle_combinations: #list(itertools.combinations([0, 1, 2, 3], 3)): - array_triangle_centroids[counter] = self.get_triangle_center( - list_points[triangle[0]], - list_points[triangle[1]], - list_points[triangle[2]]) - counter+=1 - centroid_quadrilateral = self.find_intersection_points(array_triangle_centroids) - return centroid_quadrilateral - def get_zeta_and_zeta_dot(self,i_surf, structure_tstep): numb_radial_nodes = self.dimensions[i_surf][0] +1 matrix_nodes = np.zeros((3, numb_radial_nodes, @@ -101,39 +84,6 @@ def get_zeta_and_zeta_dot(self,i_surf, structure_tstep): return matrix_nodes, matrix_nodes_dot - def get_collocation_point_pos(self, i_surf, numb_spanwise_elements, structure_tstep): - numb_radial_nodes = self.surface_m[i_surf]+1 - matrix_nodes = self.get_nodes_position(i_surf, structure_tstep, numb_radial_nodes) - counter, i_row = 0, 0 - matrix_collocation = np.zeros(((numb_spanwise_elements)*(numb_radial_nodes-1),3)) - for i in range(0, numb_spanwise_elements): - if i == 0: - for i_rad_node in range(0, numb_radial_nodes-1): - matrix_collocation[i_row] = self.get_triangle_center( - matrix_nodes[0], - matrix_nodes[i_rad_node+1], - matrix_nodes[i_rad_node+2]) - i_row += 1 - counter+=1 - elif i == numb_spanwise_elements-1: - for i_rad_node in range(0, numb_radial_nodes-1): - matrix_collocation[i_row] = self.get_triangle_center( - matrix_nodes[-1], - matrix_nodes[counter], - matrix_nodes[counter+1]) - i_row+=1 - counter+=1 - else: - for i_rad_node in range(0, numb_radial_nodes-1): - matrix_collocation[i_row] = self.get_quadrilateral_center( - matrix_nodes[[counter, counter +1, - counter+numb_radial_nodes+1, - counter+numb_radial_nodes]]) - counter+=1 - i_row+=1 - counter+=1 - return matrix_collocation - def get_elment_and_local_node_id(self, i_surf, i_global_node): # get beam elements of surface idx_beam_elements_surface = np.where(self.surface_distribution == i_surf)[0] @@ -144,22 +94,6 @@ def get_elment_and_local_node_id(self, i_surf, i_global_node): return i_elem, i_local_node raise Exception("The global node %u could not be assigned to any element of surface %u." % (i_global_node, i_surf)) - def find_intersection_points(self, array_points): - sol_parameter = fsolve(self.intersection_point_equation, - [0.3, 0.3, 0.0], args = tuple(array_points)) - return array_points[0]+sol_parameter[0]*(array_points[1]-array_points[0]) - - def intersection_point_equation(self,z, *data): - """ - Function to determine intersection point between 4 points for fsolve. - Math: A+t*(B-A) -C - u*(D-C) = 0 - TO-DO: Define function in utils as helper function (algebra??) - """ - point_A, point_B, point_C, point_D = data - t = z[0] - u = z[1] - F = point_A+t*(point_B-point_A) - point_C - u*(point_D-point_C) - return F From cd13073e30c48d875b9ef9daa009e77a40ff6acd Mon Sep 17 00:00:00 2001 From: sduess Date: Tue, 8 Dec 2020 16:22:33 +0000 Subject: [PATCH 022/232] add: solve VLM for nonlifting body interactions if settings say so --- sharpy/solvers/staticuvlm.py | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/sharpy/solvers/staticuvlm.py b/sharpy/solvers/staticuvlm.py index bddcc34a2..60a4c32d5 100644 --- a/sharpy/solvers/staticuvlm.py +++ b/sharpy/solvers/staticuvlm.py @@ -46,6 +46,10 @@ class StaticUvlm(BaseSolver): settings_default['horseshoe'] = False settings_description['horseshoe'] = 'Horseshoe wake modelling for steady simulations.' + settings_types['nonlifting_body_interactions'] = 'bool' + settings_default['nonlifting_body_interactions'] = False + settings_description['nonlifting_body_interactions'] = 'Consider nonlifting body interactions' + settings_types['num_cores'] = 'int' settings_default['num_cores'] = 0 settings_description['num_cores'] = 'Number of cores to use in the VLM lib' @@ -148,6 +152,16 @@ def run(self): 'override': True, 'for_pos': self.data.structure.timestep_info[self.data.ts].for_pos[0:3]}, self.data.aero.timestep_info[self.data.ts].u_ext) + + # check if nonlifting body interactions have to be considered + if self.settings['nonlifting_body_interactions']: + # generate uext + self.velocity_generator.generate({'zeta': self.data.nonlifting_body.timestep_info[self.data.ts].zeta, + 'override': True, + 'for_pos': self.data.structure.timestep_info[self.data.ts].for_pos[0:3]}, + self.data.nonlifting_body.timestep_info[self.data.ts].u_ext) + raise NotImplemented('VLM Solver for nonliftng body problems are not yet implemented!') + # grid orientation uvlmlib.vlm_solver(self.data.aero.timestep_info[self.data.ts], self.settings) @@ -158,6 +172,8 @@ def next_step(self): """ Updates de aerogrid based on the info of the step, and increases the self.ts counter """ self.data.aero.add_timestep() + if self.settings['nonlifting_body_interactions']: + self.data.nonlifting_body.add_timestep() self.update_step() def update_step(self): @@ -167,3 +183,7 @@ def update_step(self): # for i_surf in range(self.data.aero.timestep_info[self.data.ts].n_surf): # self.data.aero.timestep_info[self.data.ts].forces[i_surf].fill(0.0) # self.data.aero.timestep_info[self.data.ts].dynamic_forces[i_surf].fill(0.0) + if self.settings['nonlifting_body_interactions']: + self.data.nonlifting_body.generate_zeta(self.data.structure, + self.data.aero.aero_settings, + self.data.ts) From 631fef7ae294134f80e60e8f8600d6cbbf744d18 Mon Sep 17 00:00:00 2001 From: sduess Date: Mon, 28 Dec 2020 11:30:01 +0000 Subject: [PATCH 023/232] add: Missing ctype pointer for sigma and sigma dot --- sharpy/utils/datastructures.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/sharpy/utils/datastructures.py b/sharpy/utils/datastructures.py index ad89449f0..ca7081c75 100644 --- a/sharpy/utils/datastructures.py +++ b/sharpy/utils/datastructures.py @@ -371,6 +371,19 @@ def generate_ctypes_pointers(self): Generates the pointers to aerodynamic variables used to interface the C++ library ``uvlmlib`` """ super().generate_ctypes_points() + + from sharpy.utils.constants import NDIM + + self.ct_sigma_list = [] + for i_surf in range(self.n_surf): + for i_dim in range(NDIM): + self.ct_sigma_list.append(self.sigma[i_surf][:, :].reshape(-1)) + + self.ct_sigma_dot_list = [] + for i_surf in range(self.n_surf): + for i_dim in range(NDIM): + self.ct_sigma_dot_list.append(self.sigma_dot[i_surf][:, :].reshape(-1)) + self.ct_p_sigma = ((ct.POINTER(ct.c_double)*len(self.ct_sigma_list)) (* [np.ctypeslib.as_ctypes(array) for array in self.ct_sigma_list])) self.ct_p_sigma_dot = ((ct.POINTER(ct.c_double)*len(self.ct_sigma_dot_list)) From 0f500737b75132f30e6950c1d3bed63252ae22ca Mon Sep 17 00:00:00 2001 From: sduess Date: Mon, 28 Dec 2020 11:30:46 +0000 Subject: [PATCH 024/232] fix: correct naming of functions in TimeStepInfo classes --- sharpy/utils/datastructures.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sharpy/utils/datastructures.py b/sharpy/utils/datastructures.py index ca7081c75..c25315324 100644 --- a/sharpy/utils/datastructures.py +++ b/sharpy/utils/datastructures.py @@ -370,7 +370,7 @@ def generate_ctypes_pointers(self): """ Generates the pointers to aerodynamic variables used to interface the C++ library ``uvlmlib`` """ - super().generate_ctypes_points() + super().generate_ctypes_pointers() from sharpy.utils.constants import NDIM @@ -394,7 +394,7 @@ def remove_ctypes_pointers(self): """ Removes the pointers to aerodynamic variables used to interface the C++ library ``uvlmlib`` """ - super().remove_ctypes_points() + super().remove_ctypes_pointers() try: del self.ct_p_sigma except AttributeError: From b4ffdfe72b828cea8d829c67306520f3776072e5 Mon Sep 17 00:00:00 2001 From: sduess Date: Mon, 28 Dec 2020 17:01:18 +0000 Subject: [PATCH 025/232] add: vlm solver function for nonlifting bodies - new function starts different UVLM function to calculate nonlifting bodies --- sharpy/aero/utils/uvlmlib.py | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/sharpy/aero/utils/uvlmlib.py b/sharpy/aero/utils/uvlmlib.py index 1430357f9..6ed81c883 100644 --- a/sharpy/aero/utils/uvlmlib.py +++ b/sharpy/aero/utils/uvlmlib.py @@ -180,6 +180,41 @@ def vlm_solver(ts_info, options): p_rbm_vel_g) ts_info.remove_ctypes_pointers() +def vlm_solver_nonlifting_body(ts_info, options): + run_VLM_nonlifting = UvlmLib.run_VLM_nonlifting_body + run_VLM_nonliftingrestype = None + + + vmopts = VMopts() + vmopts.Steady = ct.c_bool(True) + vmopts.NumSurfaces = ct.c_uint(ts_info.n_surf) + vmopts.horseshoe = ct.c_bool(options['horseshoe'].value) + vmopts.dt = ct.c_double(options["rollup_dt"].value) + vmopts.n_rollup = ct.c_uint(options["n_rollup"].value) + vmopts.rollup_tolerance = ct.c_double(options["rollup_tolerance"].value) + vmopts.rollup_aic_refresh = ct.c_uint(options['rollup_aic_refresh'].value) + vmopts.NumCores = ct.c_uint(options['num_cores'].value) + vmopts.iterative_solver = ct.c_bool(options['iterative_solver'].value) + vmopts.iterative_tol = ct.c_double(options['iterative_tol'].value) + vmopts.iterative_precond = ct.c_bool(options['iterative_precond'].value) + + flightconditions = FlightConditions() + flightconditions.rho = options['rho'] + flightconditions.uinf = np.ctypeslib.as_ctypes(np.linalg.norm(ts_info.u_ext[0][:, 0, 0])) + flightconditions.uinf_direction = np.ctypeslib.as_ctypes(ts_info.u_ext[0][:, 0, 0]/flightconditions.uinf) + + ts_info.generate_ctypes_pointers() + + run_VLM_nonlifting(ct.byref(vmopts), + ct.byref(flightconditions), + ts_info.ct_p_dimensions, + ts_info.ct_p_zeta, + ts_info.ct_p_zeta_b_frame, + ts_info.ct_p_u_ext, + ts_info.ct_p_sigma, + ts_info.ct_p_forces) + ts_info.remove_ctypes_pointers() + def uvlm_init(ts_info, options): init_UVLM = UvlmLib.init_UVLM From 1fe2612405a98f173af966cc156d7f7129a637fc Mon Sep 17 00:00:00 2001 From: sduess Date: Mon, 28 Dec 2020 20:45:25 +0000 Subject: [PATCH 026/232] add: Start nonlifting body solver from staticuvlm solver --- sharpy/solvers/staticuvlm.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/sharpy/solvers/staticuvlm.py b/sharpy/solvers/staticuvlm.py index 60a4c32d5..a3ec311f5 100644 --- a/sharpy/solvers/staticuvlm.py +++ b/sharpy/solvers/staticuvlm.py @@ -160,7 +160,8 @@ def run(self): 'override': True, 'for_pos': self.data.structure.timestep_info[self.data.ts].for_pos[0:3]}, self.data.nonlifting_body.timestep_info[self.data.ts].u_ext) - raise NotImplemented('VLM Solver for nonliftng body problems are not yet implemented!') + uvlmlib.vlm_solver_nonlifting_body(self.data.nonlifting_body.timestep_info[self.data.ts], + self.settings) # grid orientation uvlmlib.vlm_solver(self.data.aero.timestep_info[self.data.ts], From 11bdc3124c41d015c70cf796b383f1e6ccd809ca Mon Sep 17 00:00:00 2001 From: sduess Date: Mon, 28 Dec 2020 20:47:14 +0000 Subject: [PATCH 027/232] fix: Typo in Aerogridloader --- sharpy/solvers/aerogridloader.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sharpy/solvers/aerogridloader.py b/sharpy/solvers/aerogridloader.py index 20476df68..bdaa45497 100644 --- a/sharpy/solvers/aerogridloader.py +++ b/sharpy/solvers/aerogridloader.py @@ -6,7 +6,7 @@ import sharpy.utils.settings as settings_utils import sharpy.utils.h5utils as h5utils import sharpy.utils.generator_interface as gen_interface -from sharpy.solves.gridloader import GridLoader +from sharpy.solvers.gridloader import GridLoader @solver From f878e3a2301487f0062d61f051d0ed93d304d239 Mon Sep 17 00:00:00 2001 From: sduess Date: Mon, 28 Dec 2020 20:51:56 +0000 Subject: [PATCH 028/232] fix: Delete previously deleted zeta b frame as uvlm input --- sharpy/aero/utils/uvlmlib.py | 1 - 1 file changed, 1 deletion(-) diff --git a/sharpy/aero/utils/uvlmlib.py b/sharpy/aero/utils/uvlmlib.py index 6ed81c883..0aa31bfec 100644 --- a/sharpy/aero/utils/uvlmlib.py +++ b/sharpy/aero/utils/uvlmlib.py @@ -209,7 +209,6 @@ def vlm_solver_nonlifting_body(ts_info, options): ct.byref(flightconditions), ts_info.ct_p_dimensions, ts_info.ct_p_zeta, - ts_info.ct_p_zeta_b_frame, ts_info.ct_p_u_ext, ts_info.ct_p_sigma, ts_info.ct_p_forces) From d6a5de60b5d14719bc1c08af2270acb8e4996ca2 Mon Sep 17 00:00:00 2001 From: sduess Date: Thu, 31 Dec 2020 09:54:43 +0000 Subject: [PATCH 029/232] fix(nlbodygrid): Set coordinates for all indices --- sharpy/aero/models/nonlifting_body_grid.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/sharpy/aero/models/nonlifting_body_grid.py b/sharpy/aero/models/nonlifting_body_grid.py index 3ea1ff99e..74d5455d1 100644 --- a/sharpy/aero/models/nonlifting_body_grid.py +++ b/sharpy/aero/models/nonlifting_body_grid.py @@ -55,8 +55,8 @@ def get_zeta_and_zeta_dot(self,i_surf, structure_tstep): radius = self.data_dict["radius"][i_global_node] # get nodes position in B frame #matrix_nodes[0, :numb_radial_nodes, node_counter] = 0 - matrix_nodes[1, :numb_radial_nodes, node_counter] = radius*array_cos_phi - matrix_nodes[2, :numb_radial_nodes, node_counter] = radius*array_sin_phi + matrix_nodes[1, :, node_counter] = radius*array_cos_phi + matrix_nodes[2, :, node_counter] = radius*array_sin_phi # convert position from B to A frame i_elem, i_local_node = self.get_elment_and_local_node_id(i_surf, i_global_node) psi_node = structure_tstep.psi[i_elem, i_local_node,:] @@ -71,7 +71,7 @@ def get_zeta_and_zeta_dot(self,i_surf, structure_tstep): matrix_nodes[dim, :, node_counter] += structure_tstep.pos[i_global_node,dim] # get zeta dot in A frame (velocity due to pos_dot) - matrix_nodes_dot[dim, :numb_radial_nodes, node_counter] += structure_tstep.pos_dot[i_global_node, dim] + matrix_nodes_dot[dim, :, node_counter] += structure_tstep.pos_dot[i_global_node, dim] for idx in range(numb_radial_nodes): # get zeta dot in A frame (velocity due to psi_dot)) From a48091c2f4e5c8ba8d6c3704900ab2f74a146323 Mon Sep 17 00:00:00 2001 From: sduess Date: Mon, 11 Jan 2021 10:57:25 +0000 Subject: [PATCH 030/232] fix(TimeStepInfo): Normal vector has 3 dimensions --- sharpy/utils/datastructures.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sharpy/utils/datastructures.py b/sharpy/utils/datastructures.py index c25315324..6c2ccf9ca 100644 --- a/sharpy/utils/datastructures.py +++ b/sharpy/utils/datastructures.py @@ -81,7 +81,7 @@ def __init__(self, dimensions): # panel normals self.normals = [] for i_surf in range(self.n_surf): - self.normals.append(np.zeros((6, + self.normals.append(np.zeros((3, dimensions[i_surf, 0], dimensions[i_surf, 1]), dtype=ct.c_double)) From 538f7c9a7c436a76d5ccf46c66aabced50c82ff3 Mon Sep 17 00:00:00 2001 From: sduess Date: Mon, 11 Jan 2021 11:08:09 +0000 Subject: [PATCH 031/232] fix(TimeStepInfo): Use self when call class function --- sharpy/utils/datastructures.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sharpy/utils/datastructures.py b/sharpy/utils/datastructures.py index 6c2ccf9ca..d3ebea80e 100644 --- a/sharpy/utils/datastructures.py +++ b/sharpy/utils/datastructures.py @@ -127,7 +127,7 @@ def copy(self): """ Returns a copy of a deepcopy of a :class:`~sharpy.utils.datastructures.TimeStepInfo` """ - return create_placeholder(TimeStepInfo(self.dimensions)) + return self.create_placeholder(TimeStepInfo(self.dimensions)) def create_placeholder(self, copied): # generate placeholder for aero grid zeta coordinates From 45d1017cc5af50b0cc5c9cbc46fdf993a0a2f847 Mon Sep 17 00:00:00 2001 From: sduess Date: Mon, 11 Jan 2021 11:12:26 +0000 Subject: [PATCH 032/232] fix(staticUVLM): reposition velocity generator - Velocity generator for Lifting surfaces is only called if no nonlifting bodies are analyzed. Otherwise it gives zero uext-arrays to the nonlifting body VLM solver. --- sharpy/solvers/staticuvlm.py | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/sharpy/solvers/staticuvlm.py b/sharpy/solvers/staticuvlm.py index a3ec311f5..f7aa2243c 100644 --- a/sharpy/solvers/staticuvlm.py +++ b/sharpy/solvers/staticuvlm.py @@ -147,12 +147,6 @@ def run(self): 'gamma_star': aero_tstep.gamma_star, 'dist_to_orig': aero_tstep.dist_to_orig}) - # generate uext - self.velocity_generator.generate({'zeta': self.data.aero.timestep_info[self.data.ts].zeta, - 'override': True, - 'for_pos': self.data.structure.timestep_info[self.data.ts].for_pos[0:3]}, - self.data.aero.timestep_info[self.data.ts].u_ext) - # check if nonlifting body interactions have to be considered if self.settings['nonlifting_body_interactions']: # generate uext @@ -162,10 +156,16 @@ def run(self): self.data.nonlifting_body.timestep_info[self.data.ts].u_ext) uvlmlib.vlm_solver_nonlifting_body(self.data.nonlifting_body.timestep_info[self.data.ts], self.settings) + else: + # generate uext + self.velocity_generator.generate({'zeta': self.data.aero.timestep_info[self.data.ts].zeta, + 'override': True, + 'for_pos': self.data.structure.timestep_info[self.data.ts].for_pos[0:3]}, + self.data.aero.timestep_info[self.data.ts].u_ext) - # grid orientation - uvlmlib.vlm_solver(self.data.aero.timestep_info[self.data.ts], - self.settings) + # grid orientation + uvlmlib.vlm_solver(self.data.aero.timestep_info[self.data.ts], + self.settings) return self.data From 3ec62e83e7629f85727a636edd552bdbb2fde6f9 Mon Sep 17 00:00:00 2001 From: sduess Date: Tue, 26 Jan 2021 17:02:38 +0000 Subject: [PATCH 033/232] update(submodule): Update UVLM lib --- lib/UVLM | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/UVLM b/lib/UVLM index dfcd946fd..a2b39541a 160000 --- a/lib/UVLM +++ b/lib/UVLM @@ -1 +1 @@ -Subproject commit dfcd946fd2aa46ea690ce1d71dd6edd38af23fc0 +Subproject commit a2b39541aa60e7d328819bac68dcee5c6003d7c3 From 4302b46e916be742afcb54841a3b2d91d043bb04 Mon Sep 17 00:00:00 2001 From: sduess Date: Wed, 27 Jan 2021 09:01:08 +0000 Subject: [PATCH 034/232] rename: aero_dict -> data_dict - necessary since the introduced gridloader class the attribute was renamed --- .../content/example_notebooks/wind_turbine.ipynb | 2 +- .../linear/assembler/lincontrolsurfacedeflector.py | 12 ++++++------ sharpy/postproc/stallcheck.py | 2 +- sharpy/solvers/dynamiccoupled.py | 4 ++-- sharpy/solvers/staticcoupled.py | 4 ++-- sharpy/solvers/staticcoupledrbm.py | 4 ++-- sharpy/solvers/trim.py | 2 +- sharpy/utils/correct_forces.py | 10 +++++----- sharpy/utils/h5utils.py | 2 +- 9 files changed, 21 insertions(+), 21 deletions(-) diff --git a/docs/source/content/example_notebooks/wind_turbine.ipynb b/docs/source/content/example_notebooks/wind_turbine.ipynb index 79895b940..61c9d1bc8 100644 --- a/docs/source/content/example_notebooks/wind_turbine.ipynb +++ b/docs/source/content/example_notebooks/wind_turbine.ipynb @@ -1040,7 +1040,7 @@ " inode_in_elem = sharpy_output.structure.node_master_elem[node_global_index, 1]\n", " CAB = algebra.crv2rotation(tstep.psi[ielem, inode_in_elem, :])\n", "\n", - " c[iblade][inode] = sharpy_output.aero.aero_dict['chord'][ielem,inode_in_elem]\n", + " c[iblade][inode] = sharpy_output.aero.data_dict['chord'][ielem,inode_in_elem]\n", "\n", " forces_AFoR = np.dot(CAB, forces[iblade][inode, 0:3])\n", "\n", diff --git a/sharpy/linear/assembler/lincontrolsurfacedeflector.py b/sharpy/linear/assembler/lincontrolsurfacedeflector.py index 169a37c9a..3f61c2634 100644 --- a/sharpy/linear/assembler/lincontrolsurfacedeflector.py +++ b/sharpy/linear/assembler/lincontrolsurfacedeflector.py @@ -71,7 +71,7 @@ def generate(self): tsstruct0 = self.tsstruct0 # Find the vertices corresponding to a control surface from beam coordinates to aerogrid - aero_dict = aero.aero_dict + data_dict = aero.data_dict n_surf = tsaero0.n_surf n_control_surfaces = self.n_control_surfaces @@ -109,7 +109,7 @@ def generate(self): # Although a node may be part of 2 aerodynamic surfaces, we need to ensure that the current # element for the given node is indeed part of that surface. - elems_in_surf = np.where(aero_dict['surface_distribution'] == i_surf)[0] + elems_in_surf = np.where(data_dict['surface_distribution'] == i_surf)[0] if i_elem not in elems_in_surf: continue @@ -120,18 +120,18 @@ def generate(self): K_zeta_start = 3 * sum(linuvlm.MS.KKzeta[:i_surf]) shape_zeta = (3, M + 1, N + 1) - i_control_surface = aero_dict['control_surface'][i_elem, i_local_node] + i_control_surface = data_dict['control_surface'][i_elem, i_local_node] if i_control_surface >= 0: if not with_control_surface: i_start_of_cs = i_node_span.copy() with_control_surface = True - control_surface_chord = aero_dict['control_surface_chord'][i_control_surface] + control_surface_chord = data_dict['control_surface_chord'][i_control_surface] try: control_surface_hinge_coord = \ - aero_dict['control_surface_hinge_coord'][i_control_surface] * \ - aero_dict['chord'][i_elem, i_local_node] + data_dict['control_surface_hinge_coord'][i_control_surface] * \ + data_dict['chord'][i_elem, i_local_node] except KeyError: control_surface_hinge_coord = None diff --git a/sharpy/postproc/stallcheck.py b/sharpy/postproc/stallcheck.py index b283dd4af..182ca327b 100644 --- a/sharpy/postproc/stallcheck.py +++ b/sharpy/postproc/stallcheck.py @@ -100,7 +100,7 @@ def check_stall(self): for i_elem in range(self.data.structure.num_elem): for i_local_node in range(self.data.structure.num_node_elem): - airfoil_id = self.data.aero.aero_dict['airfoil_distribution'][i_elem, i_local_node] + airfoil_id = self.data.aero.data_dict['airfoil_distribution'][i_elem, i_local_node] if self.settings['airfoil_stall_angles']: i_global_node = self.data.structure.connectivities[i_elem, i_local_node] for i_dict in self.data.aero.struct2aero_mapping[i_global_node]: diff --git a/sharpy/solvers/dynamiccoupled.py b/sharpy/solvers/dynamiccoupled.py index e222dd4a0..e54dbb5ac 100644 --- a/sharpy/solvers/dynamiccoupled.py +++ b/sharpy/solvers/dynamiccoupled.py @@ -707,7 +707,7 @@ def map_forces(self, aero_kstep, structural_kstep, unsteady_forces_coeff=1.0): self.data.structure.node_master_elem, self.data.structure.connectivities, structural_kstep.cag(), - self.data.aero.aero_dict) + self.data.aero.data_dict) dynamic_struct_forces = unsteady_forces_coeff*mapping.aero2struct_force_mapping( aero_kstep.dynamic_forces, self.data.aero.struct2aero_mapping, @@ -717,7 +717,7 @@ def map_forces(self, aero_kstep, structural_kstep, unsteady_forces_coeff=1.0): self.data.structure.node_master_elem, self.data.structure.connectivities, structural_kstep.cag(), - self.data.aero.aero_dict) + self.data.aero.data_dict) if self.correct_forces: struct_forces = self.correct_forces_function(self.data, diff --git a/sharpy/solvers/staticcoupled.py b/sharpy/solvers/staticcoupled.py index 81b2cca59..7b0252d44 100644 --- a/sharpy/solvers/staticcoupled.py +++ b/sharpy/solvers/staticcoupled.py @@ -160,7 +160,7 @@ def run(self): self.data.structure.node_master_elem, self.data.structure.connectivities, self.data.structure.timestep_info[self.data.ts].cag(), - self.data.aero.aero_dict) + self.data.aero.data_dict) if self.correct_forces: struct_forces = self.correct_forces_function(self.data, @@ -296,7 +296,7 @@ def change_trim(self, alpha, thrust, thrust_nodes, tail_deflection, tail_cs_inde # tail deflection try: - self.data.aero.aero_dict['control_surface_deflection'][tail_cs_index] = tail_deflection + self.data.aero.data_dict['control_surface_deflection'][tail_cs_index] = tail_deflection except KeyError: raise Exception('This model has no control surfaces') except IndexError: diff --git a/sharpy/solvers/staticcoupledrbm.py b/sharpy/solvers/staticcoupledrbm.py index d950205a7..3656f7e1d 100644 --- a/sharpy/solvers/staticcoupledrbm.py +++ b/sharpy/solvers/staticcoupledrbm.py @@ -161,7 +161,7 @@ def run(self): self.data.structure.node_master_elem, self.data.structure.connectivities, self.data.structure.timestep_info[self.data.ts].cag(), - self.data.aero.aero_dict) + self.data.aero.data_dict) if self.correct_forces: struct_forces = self.correct_forces_function(self.data, @@ -318,7 +318,7 @@ def change_trim(self, alpha, thrust, thrust_nodes, tail_deflection, tail_cs_inde # tail deflection try: - self.data.aero.aero_dict['control_surface_deflection'][tail_cs_index] = tail_deflection + self.data.aero.data_dict['control_surface_deflection'][tail_cs_index] = tail_deflection except KeyError: raise Exception('This model has no control surfaces') except IndexError: diff --git a/sharpy/solvers/trim.py b/sharpy/solvers/trim.py index 68bc221b4..f2ca8a484 100644 --- a/sharpy/solvers/trim.py +++ b/sharpy/solvers/trim.py @@ -294,7 +294,7 @@ def solver_wrapper(x, x_info, solver_data, i_dim=-1): tstep.quat[:] = orientation_quat # control surface deflection for i_cs in range(len(x_info['i_control_surfaces'])): - solver_data.data.aero.aero_dict['control_surface_deflection'][x_info['control_surfaces_id'][i_cs]] = x[x_info['i_control_surfaces'][i_cs]] + solver_data.data.aero.data_dict['control_surface_deflection'][x_info['control_surfaces_id'][i_cs]] = x[x_info['i_control_surfaces'][i_cs]] # thrust input tstep.steady_applied_forces[:] = 0.0 try: diff --git a/sharpy/utils/correct_forces.py b/sharpy/utils/correct_forces.py index 906447290..409285e16 100644 --- a/sharpy/utils/correct_forces.py +++ b/sharpy/utils/correct_forces.py @@ -54,11 +54,11 @@ def efficiency(data, aero_kstep, structural_kstep, struct_forces, **kwargs): n_node = data.structure.num_node n_elem = data.structure.num_elem - aero_dict = data.aero.aero_dict + data_dict = data.aero.data_dict new_struct_forces = np.zeros_like(struct_forces) # load airfoil efficiency (if it exists); else set to one (to avoid multiple ifs in the loops) - airfoil_efficiency = aero_dict['airfoil_efficiency'] + airfoil_efficiency = data_dict['airfoil_efficiency'] # force efficiency dimensions [n_elem, n_node_elem, 2, [fx, fy, fz]] - all defined in B frame force_efficiency = np.zeros((n_elem, 3, 2, 3)) force_efficiency[:, :, 0, :] = 1. @@ -110,7 +110,7 @@ def polars(data, aero_kstep, structural_kstep, struct_forces, **kwargs): rho = kwargs.get('rho', 1.225) correct_lift = kwargs.get('correct_lift', False) cd_from_cl = kwargs.get('cd_from_cl', False) - aero_dict = aerogrid.aero_dict + data_dict = aerogrid.data_dict if aerogrid.polars is None: return struct_forces new_struct_forces = np.zeros_like(struct_forces) @@ -118,10 +118,10 @@ def polars(data, aero_kstep, structural_kstep, struct_forces, **kwargs): nnode = struct_forces.shape[0] for inode in range(nnode): new_struct_forces[inode, :] = struct_forces[inode, :].copy() - if aero_dict['aero_node'][inode]: + if data_dict['aero_node'][inode]: ielem, inode_in_elem = beam.node_master_elem[inode] - iairfoil = aero_dict['airfoil_distribution'][ielem, inode_in_elem] + iairfoil = data_dict['airfoil_distribution'][ielem, inode_in_elem] isurf = aerogrid.struct2aero_mapping[inode][0]['i_surf'] i_n = aerogrid.struct2aero_mapping[inode][0]['i_n'] N = aerogrid.aero_dimensions[isurf, 1] diff --git a/sharpy/utils/h5utils.py b/sharpy/utils/h5utils.py index 2adbd0631..dee5561df 100644 --- a/sharpy/utils/h5utils.py +++ b/sharpy/utils/h5utils.py @@ -69,7 +69,7 @@ def check_fem_dict(fem_dict): print(' PASSED') -def check_aero_dict(aero_dict): +def check_data_dict(data_dict): pass From f588dab5eb79b4c0b62c8956761cb9df64cb7985 Mon Sep 17 00:00:00 2001 From: sduess Date: Tue, 2 Feb 2021 11:35:13 +0000 Subject: [PATCH 035/232] rename: aero_dict to data_dict --- sharpy/aero/utils/mapping.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sharpy/aero/utils/mapping.py b/sharpy/aero/utils/mapping.py index e3b359294..5dd8ce275 100644 --- a/sharpy/aero/utils/mapping.py +++ b/sharpy/aero/utils/mapping.py @@ -11,7 +11,7 @@ def aero2struct_force_mapping(aero_forces, master, conn, cag=np.eye(3), - aero_dict=None): + data_dict=None): r""" Maps the aerodynamic forces at the lattice to the structural nodes @@ -39,7 +39,7 @@ def aero2struct_force_mapping(aero_forces, master: Unused conn (np.ndarray): Connectivities matrix cag (np.ndarray): Transformation matrix between inertial and body-attached reference ``A`` - aero_dict (dict): Dictionary containing the grid's information. + data_dict (dict): Dictionary containing the grid's information. Returns: np.ndarray: structural forces in an ``n_node x 6`` vector From 3ec3ac7314ddde9decb730dc8b7e30247b94cd36 Mon Sep 17 00:00:00 2001 From: sduess Date: Tue, 2 Feb 2021 11:36:11 +0000 Subject: [PATCH 036/232] fix(NonliftingTimeStepInfo): Remove unnecessary loop --- sharpy/utils/datastructures.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/sharpy/utils/datastructures.py b/sharpy/utils/datastructures.py index d3ebea80e..a2b8d6f17 100644 --- a/sharpy/utils/datastructures.py +++ b/sharpy/utils/datastructures.py @@ -376,13 +376,11 @@ def generate_ctypes_pointers(self): self.ct_sigma_list = [] for i_surf in range(self.n_surf): - for i_dim in range(NDIM): - self.ct_sigma_list.append(self.sigma[i_surf][:, :].reshape(-1)) + self.ct_sigma_list.append(self.sigma[i_surf][:, :].reshape(-1)) self.ct_sigma_dot_list = [] for i_surf in range(self.n_surf): - for i_dim in range(NDIM): - self.ct_sigma_dot_list.append(self.sigma_dot[i_surf][:, :].reshape(-1)) + self.ct_sigma_dot_list.append(self.sigma_dot[i_surf][:, :].reshape(-1)) self.ct_p_sigma = ((ct.POINTER(ct.c_double)*len(self.ct_sigma_list)) (* [np.ctypeslib.as_ctypes(array) for array in self.ct_sigma_list])) From 9e87f1e52fa1588c61067c3ecdef30a54b877a0a Mon Sep 17 00:00:00 2001 From: sduess Date: Tue, 2 Feb 2021 11:38:24 +0000 Subject: [PATCH 037/232] update(submodule): update UVLM lib --- lib/UVLM | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/UVLM b/lib/UVLM index a2b39541a..dfcd946fd 160000 --- a/lib/UVLM +++ b/lib/UVLM @@ -1 +1 @@ -Subproject commit a2b39541aa60e7d328819bac68dcee5c6003d7c3 +Subproject commit dfcd946fd2aa46ea690ce1d71dd6edd38af23fc0 From 3b05e35f565306535501aaa56c391e4c9a7d71b7 Mon Sep 17 00:00:00 2001 From: sduess Date: Tue, 2 Feb 2021 15:14:07 +0000 Subject: [PATCH 038/232] update(submodule): Update UVLM dev_fuselage branch --- lib/UVLM | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/UVLM b/lib/UVLM index dfcd946fd..3cfdac61d 160000 --- a/lib/UVLM +++ b/lib/UVLM @@ -1 +1 @@ -Subproject commit dfcd946fd2aa46ea690ce1d71dd6edd38af23fc0 +Subproject commit 3cfdac61d5557f5f8394b702cb16e280a136828c From 0e4a89debfa5ebe07b15ca87ce209d38acaa96e0 Mon Sep 17 00:00:00 2001 From: sduess Date: Tue, 2 Feb 2021 17:01:08 +0000 Subject: [PATCH 039/232] add(UVLM_lib): Create function to start combined solver --- sharpy/aero/utils/uvlmlib.py | 53 ++++++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) diff --git a/sharpy/aero/utils/uvlmlib.py b/sharpy/aero/utils/uvlmlib.py index 0aa31bfec..ea95a0edc 100644 --- a/sharpy/aero/utils/uvlmlib.py +++ b/sharpy/aero/utils/uvlmlib.py @@ -214,6 +214,59 @@ def vlm_solver_nonlifting_body(ts_info, options): ts_info.ct_p_forces) ts_info.remove_ctypes_pointers() +def vlm_solver_lifting_and_nonlifting_bodies(ts_info_lifting, ts_info_nonlifting, options): + run_VLM_lifting_and_nonlifting = UvlmLib.run_VLM_lifting_and_nonlifting_bodies + run_VLM_lifting_and_nonlifting.restype = None + + vmopts = VMopts() + vmopts.Steady = ct.c_bool(True) + vmopts.NumSurfaces = ct.c_uint(ts_info_lifting.n_surf) + vmopts.horseshoe = ct.c_bool(options['horseshoe'].value) + vmopts.dt = ct.c_double(options["rollup_dt"].value) + vmopts.n_rollup = ct.c_uint(options["n_rollup"].value) + vmopts.rollup_tolerance = ct.c_double(options["rollup_tolerance"].value) + vmopts.rollup_aic_refresh = ct.c_uint(options['rollup_aic_refresh'].value) + vmopts.NumCores = ct.c_uint(options['num_cores'].value) + vmopts.iterative_solver = ct.c_bool(options['iterative_solver'].value) + vmopts.iterative_tol = ct.c_double(options['iterative_tol'].value) + vmopts.iterative_precond = ct.c_bool(options['iterative_precond'].value) + vmopts.cfl1 = ct.c_bool(options['cfl1']) + vmopts.vortex_radius = ct.c_double(options['vortex_radius'].value) + vmopts.vortex_radius_wake_ind = ct.c_double(options['vortex_radius_wake_ind'].value) + + vmopts_nonlifting = VMopts() + vmopts_nonlifting.Steady = ct.c_bool(True) + vmopts_nonlifting.NumSurfaces = ct.c_uint(ts_info_nonlifting.n_surf) + + flightconditions = FlightConditions() + flightconditions.rho = options['rho'] + flightconditions.uinf = np.ctypeslib.as_ctypes(np.linalg.norm(ts_info_lifting.u_ext[0][:, 0, 0])) + flightconditions.uinf_direction = np.ctypeslib.as_ctypes(ts_info_lifting.u_ext[0][:, 0, 0]/flightconditions.uinf) + + p_rbm_vel_g = options['rbm_vel_g'].ctypes.data_as(ct.POINTER(ct.c_double)) + ts_info_lifting.generate_ctypes_pointers() + ts_info_nonlifting.generate_ctypes_pointers() + run_VLM_lifting_and_nonlifting(ct.byref(vmopts), + ct.byref(flightconditions), + ts_info_lifting.ct_p_dimensions, + ts_info_lifting.ct_p_dimensions_star, + ts_info_lifting.ct_p_zeta, + ts_info_lifting.ct_p_zeta_star, + ts_info_lifting.ct_p_zeta_dot, + ts_info_lifting.ct_p_u_ext, + ts_info_lifting.ct_p_gamma, + ts_info_lifting.ct_p_gamma_star, + ts_info_lifting.ct_p_forces, + p_rbm_vel_g, + ct.byref(vmopts_nonlifting), + ts_info_nonlifting.ct_p_dimensions, + ts_info_nonlifting.ct_p_zeta, + ts_info_nonlifting.ct_p_u_ext, + ts_info_nonlifting.ct_p_sigma, + ts_info_nonlifting.ct_p_forces) + + ts_info_lifting.remove_ctypes_pointers() + ts_info_nonlifting.remove_ctypes_pointers() def uvlm_init(ts_info, options): init_UVLM = UvlmLib.init_UVLM From 7338914e22c6de136bfd81af8dd4ef05945e95a0 Mon Sep 17 00:00:00 2001 From: sduess Date: Thu, 11 Feb 2021 22:03:06 +0000 Subject: [PATCH 040/232] add(phantom cell): Junction BC for phantom cell generation in UVLM - junction boundary conditions are saved in aerotimestepinfo and passed to UVLM --- sharpy/aero/models/aerogrid.py | 21 +++++++++++++++++++++ sharpy/aero/utils/uvlmlib.py | 1 + sharpy/utils/datastructures.py | 25 ++++++++++++++++++++++++- 3 files changed, 46 insertions(+), 1 deletion(-) diff --git a/sharpy/aero/models/aerogrid.py b/sharpy/aero/models/aerogrid.py index 8ac2161a6..8d556ab33 100644 --- a/sharpy/aero/models/aerogrid.py +++ b/sharpy/aero/models/aerogrid.py @@ -287,6 +287,27 @@ def generate_zeta_timestep_info(self, structure_tstep, aero_tstep, beam, setting self.aero_settings['aligned_grid'], orientation_in=self.aero_settings['freestream_dir'], calculate_zeta_dot=True)) + # set junction boundary conditions for later phantom cell creation in UVLM + if sum(self.data_dict["junction_boundary_condition"])>0: + self.generate_phantom_panels_at_junction(aero_tstep) + + + def generate_phantom_panels_at_junction(self, aero_tstep): + # To-Do: Extent for general case + list_global_node_junction = np.where(self.data_dict["junction_boundary_condition"] == 1)[0] + list_local_nodes = [] + list_surfaces = [] + for i_global_node in list_global_node_junction.tolist(): + for i in range(len(self.struct2aero_mapping[i_global_node])): + list_local_nodes.append(self.struct2aero_mapping[i_global_node][i]['i_n']) + list_surfaces.append(self.struct2aero_mapping[i_global_node][i]['i_surf']) + for i_surf in range(self.n_surf): + zeta_phantom.append([]) + if i_surf in list_surfaces: + idx_local_node = list_surfaces.index(i_surf) + # assume that there is only one junction per surface yet + aero_tstep.flag_zeta_phantom[i_surf][list_local_nodes[idx_local_node]] = True + @staticmethod diff --git a/sharpy/aero/utils/uvlmlib.py b/sharpy/aero/utils/uvlmlib.py index ea95a0edc..11c1fc79c 100644 --- a/sharpy/aero/utils/uvlmlib.py +++ b/sharpy/aero/utils/uvlmlib.py @@ -257,6 +257,7 @@ def vlm_solver_lifting_and_nonlifting_bodies(ts_info_lifting, ts_info_nonlifting ts_info_lifting.ct_p_gamma, ts_info_lifting.ct_p_gamma_star, ts_info_lifting.ct_p_forces, + ts_info_lifting.ct_p_flag_zeta_phantom, p_rbm_vel_g, ct.byref(vmopts_nonlifting), ts_info_nonlifting.ct_p_dimensions, diff --git a/sharpy/utils/datastructures.py b/sharpy/utils/datastructures.py index a2b8d6f17..450868e66 100644 --- a/sharpy/utils/datastructures.py +++ b/sharpy/utils/datastructures.py @@ -518,7 +518,11 @@ def __init__(self, dimensions, dimensions_star): dimensions_star[i_surf, 1]), dtype=ct.c_double)) - + # Junction handling + self.flag_zeta_phantom = [] + for i_surf in range(self.n_surf): + self.flag_zeta_phantom.append(np.zeros((dimensions[i_surf, 1] + 1), + dtype=ct.c_bool)) self.control_surface_deflection = np.array([]) def copy(self): @@ -552,6 +556,10 @@ def create_placeholder(self, copied): copied.control_surface_deflection = self.control_surface_deflection.astype(dtype=ct.c_double, copy=True) + # phantom panel flags + for i_surf in range(copied.n_surf): + copied.flag_zeta_phantom[i_surf] = self.flag_zeta_phantom[i_surf].astype(dtype=ct.c_bool, copy=True, order='C') + return copied def generate_ctypes_pointers(self): @@ -590,6 +598,13 @@ def generate_ctypes_pointers(self): for i_surf in range(self.n_surf): self.ct_wake_conv_vel_list.append(self.wake_conv_vel[i_surf][:, :].reshape(-1)) + self.ct_flag_zeta_phantom_list = [] + for i_surf in range(self.n_surf): + self.ct_flag_zeta_phantom_list.append(self.flag_zeta_phantom[i_surf][:].reshape(-1)) + + + + self.ct_p_dimensions_star = ((ct.POINTER(ct.c_uint)*n_surf) (* np.ctypeslib.as_ctypes(self.ct_dimensions_star))) self.ct_p_zeta_star = ((ct.POINTER(ct.c_double)*len(self.ct_zeta_star_list)) @@ -606,6 +621,10 @@ def generate_ctypes_pointers(self): (* [np.ctypeslib.as_ctypes(array) for array in self.ct_dist_to_orig_list])) self.ct_p_wake_conv_vel = ((ct.POINTER(ct.c_double)*len(self.ct_wake_conv_vel_list)) (* [np.ctypeslib.as_ctypes(array) for array in self.ct_wake_conv_vel_list])) + self.ct_p_flag_zeta_phantom = ((ct.POINTER(ct.c_bool)*len(self.ct_flag_zeta_phantom_list)) + (* [np.ctypeslib.as_ctypes(array) for array in self.ct_flag_zeta_phantom_list])) + + def remove_ctypes_pointers(self): super().remove_ctypes_pointers() @@ -649,6 +668,10 @@ def remove_ctypes_pointers(self): except AttributeError: pass + try: + del self.ct_p_flag_zeta_phantom + except AttributeError: + pass def init_matrix_structure(dimensions, with_dim_dimension, added_size=0): From eccdc2406056a0d48beda196bfefcb40484c58f7 Mon Sep 17 00:00:00 2001 From: sduess Date: Mon, 1 Mar 2021 17:35:16 +0000 Subject: [PATCH 041/232] update(submodule): Update UVLM lib --- lib/UVLM | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/UVLM b/lib/UVLM index 3cfdac61d..53f5a2e11 160000 --- a/lib/UVLM +++ b/lib/UVLM @@ -1 +1 @@ -Subproject commit 3cfdac61d5557f5f8394b702cb16e280a136828c +Subproject commit 53f5a2e117bc99d0a94d31f242c4d573538a8bb7 From 81fee66fa372101f7c5c001d09f86a926ba9751d Mon Sep 17 00:00:00 2001 From: sduess Date: Mon, 1 Mar 2021 19:44:41 +0000 Subject: [PATCH 042/232] fix(aerogrid): delete not fully removed variable --- sharpy/aero/models/aerogrid.py | 1 - 1 file changed, 1 deletion(-) diff --git a/sharpy/aero/models/aerogrid.py b/sharpy/aero/models/aerogrid.py index 8d556ab33..ed044f8d8 100644 --- a/sharpy/aero/models/aerogrid.py +++ b/sharpy/aero/models/aerogrid.py @@ -302,7 +302,6 @@ def generate_phantom_panels_at_junction(self, aero_tstep): list_local_nodes.append(self.struct2aero_mapping[i_global_node][i]['i_n']) list_surfaces.append(self.struct2aero_mapping[i_global_node][i]['i_surf']) for i_surf in range(self.n_surf): - zeta_phantom.append([]) if i_surf in list_surfaces: idx_local_node = list_surfaces.index(i_surf) # assume that there is only one junction per surface yet From e1381e7f9ca3077de85360ea70ddae79b72d4b1d Mon Sep 17 00:00:00 2001 From: sduess Date: Mon, 12 Apr 2021 08:05:27 +0100 Subject: [PATCH 043/232] fix: adjust zeta phantom flag to int type --- sharpy/aero/models/aerogrid.py | 4 +++- sharpy/utils/datastructures.py | 23 +++++++---------------- 2 files changed, 10 insertions(+), 17 deletions(-) diff --git a/sharpy/aero/models/aerogrid.py b/sharpy/aero/models/aerogrid.py index ed044f8d8..9f668046b 100644 --- a/sharpy/aero/models/aerogrid.py +++ b/sharpy/aero/models/aerogrid.py @@ -288,6 +288,7 @@ def generate_zeta_timestep_info(self, structure_tstep, aero_tstep, beam, setting orientation_in=self.aero_settings['freestream_dir'], calculate_zeta_dot=True)) # set junction boundary conditions for later phantom cell creation in UVLM + if "junction_boundary_condition" in self.data_dict: if sum(self.data_dict["junction_boundary_condition"])>0: self.generate_phantom_panels_at_junction(aero_tstep) @@ -305,7 +306,7 @@ def generate_phantom_panels_at_junction(self, aero_tstep): if i_surf in list_surfaces: idx_local_node = list_surfaces.index(i_surf) # assume that there is only one junction per surface yet - aero_tstep.flag_zeta_phantom[i_surf][list_local_nodes[idx_local_node]] = True + aero_tstep.flag_zeta_phantom[list_local_nodes[idx_local_node], i_surf] = 1 @@ -393,6 +394,7 @@ def generate_strip(node_info, airfoil_db, aligned_grid, orientation_in=np.array( if node_info['M_distribution'] == 'uniform': strip_coordinates_b_frame[1, :] = np.linspace(0.0, 1.0, node_info['M'] + 1) elif node_info['M_distribution'] == '1-cos': + print("Distribution = ", node_info['M_distribution']) domain = np.linspace(0, 1.0, node_info['M'] + 1) strip_coordinates_b_frame[1, :] = 0.5*(1.0 - np.cos(domain*np.pi)) elif node_info['M_distribution'].lower() == 'user_defined': diff --git a/sharpy/utils/datastructures.py b/sharpy/utils/datastructures.py index 47f7ee059..8ef9bd1cf 100644 --- a/sharpy/utils/datastructures.py +++ b/sharpy/utils/datastructures.py @@ -519,10 +519,8 @@ def __init__(self, dimensions, dimensions_star): dtype=ct.c_double)) # Junction handling - self.flag_zeta_phantom = [] - for i_surf in range(self.n_surf): - self.flag_zeta_phantom.append(np.zeros((dimensions[i_surf, 1] + 1), - dtype=ct.c_bool)) + self.flag_zeta_phantom = np.zeros((dimensions[i_surf, 1] + 1, self.n_surf), + dtype=ct.c_int) self.control_surface_deflection = np.array([]) def copy(self): @@ -557,9 +555,8 @@ def create_placeholder(self, copied): copied.control_surface_deflection = self.control_surface_deflection.astype(dtype=ct.c_double, copy=True) # phantom panel flags - for i_surf in range(copied.n_surf): - copied.flag_zeta_phantom[i_surf] = self.flag_zeta_phantom[i_surf].astype(dtype=ct.c_bool, copy=True, order='C') - + copied.flag_zeta_phantom = self.flag_zeta_phantom.astype(dtype=ct.c_int, copy=True, order='C') + return copied def generate_ctypes_pointers(self): @@ -597,12 +594,8 @@ def generate_ctypes_pointers(self): self.ct_wake_conv_vel_list = [] for i_surf in range(self.n_surf): self.ct_wake_conv_vel_list.append(self.wake_conv_vel[i_surf][:, :].reshape(-1)) - - self.ct_flag_zeta_phantom_list = [] - for i_surf in range(self.n_surf): - self.ct_flag_zeta_phantom_list.append(self.flag_zeta_phantom[i_surf][:].reshape(-1)) - - + self.ct_flag_zeta_phantom_list = self.flag_zeta_phantom[:].reshape(-1) + self.ct_p_dimensions_star = ((ct.POINTER(ct.c_uint)*n_surf) @@ -621,11 +614,9 @@ def generate_ctypes_pointers(self): (* [np.ctypeslib.as_ctypes(array) for array in self.ct_dist_to_orig_list])) self.ct_p_wake_conv_vel = ((ct.POINTER(ct.c_double)*len(self.ct_wake_conv_vel_list)) (* [np.ctypeslib.as_ctypes(array) for array in self.ct_wake_conv_vel_list])) - self.ct_p_flag_zeta_phantom = ((ct.POINTER(ct.c_bool)*len(self.ct_flag_zeta_phantom_list)) - (* [np.ctypeslib.as_ctypes(array) for array in self.ct_flag_zeta_phantom_list])) + self.ct_p_flag_zeta_phantom = np.ctypeslib.as_ctypes(self.ct_flag_zeta_phantom_list) - def remove_ctypes_pointers(self): super().remove_ctypes_pointers() try: From ea13a5fdd87835bbfada75c362d079ecca3a3d0a Mon Sep 17 00:00:00 2001 From: sduess Date: Mon, 26 Apr 2021 13:19:10 +0100 Subject: [PATCH 044/232] fix: remove all .value - due to changes in SHARPy's develop branch, all .value have to be removed since settings are not saved in ctype anymore --- sharpy/aero/models/aerogrid.py | 2 +- sharpy/aero/utils/uvlmlib.py | 40 +++++++++++++++++----------------- 2 files changed, 21 insertions(+), 21 deletions(-) diff --git a/sharpy/aero/models/aerogrid.py b/sharpy/aero/models/aerogrid.py index 9f668046b..166e00808 100644 --- a/sharpy/aero/models/aerogrid.py +++ b/sharpy/aero/models/aerogrid.py @@ -139,7 +139,7 @@ def calculate_dimensions(self): self.dimensions_star = self.dimensions.copy() for i_surf in range(self.n_surf): - self.dimensions_star[i_surf, 0] = self.aero_settings['mstar'].value + self.dimensions_star[i_surf, 0] = self.aero_settings['mstar'] def generate_zeta_timestep_info(self, structure_tstep, aero_tstep, beam, settings, it=None, dt=None): if it is None: diff --git a/sharpy/aero/utils/uvlmlib.py b/sharpy/aero/utils/uvlmlib.py index 004322611..a0ba5e396 100644 --- a/sharpy/aero/utils/uvlmlib.py +++ b/sharpy/aero/utils/uvlmlib.py @@ -188,15 +188,15 @@ def vlm_solver_nonlifting_body(ts_info, options): vmopts = VMopts() vmopts.Steady = ct.c_bool(True) vmopts.NumSurfaces = ct.c_uint(ts_info.n_surf) - vmopts.horseshoe = ct.c_bool(options['horseshoe'].value) - vmopts.dt = ct.c_double(options["rollup_dt"].value) - vmopts.n_rollup = ct.c_uint(options["n_rollup"].value) - vmopts.rollup_tolerance = ct.c_double(options["rollup_tolerance"].value) - vmopts.rollup_aic_refresh = ct.c_uint(options['rollup_aic_refresh'].value) - vmopts.NumCores = ct.c_uint(options['num_cores'].value) - vmopts.iterative_solver = ct.c_bool(options['iterative_solver'].value) - vmopts.iterative_tol = ct.c_double(options['iterative_tol'].value) - vmopts.iterative_precond = ct.c_bool(options['iterative_precond'].value) + vmopts.horseshoe = ct.c_bool(options['horseshoe']) + vmopts.dt = ct.c_double(options["rollup_dt"]) + vmopts.n_rollup = ct.c_uint(options["n_rollup"]) + vmopts.rollup_tolerance = ct.c_double(options["rollup_tolerance"]) + vmopts.rollup_aic_refresh = ct.c_uint(options['rollup_aic_refresh']) + vmopts.NumCores = ct.c_uint(options['num_cores']) + vmopts.iterative_solver = ct.c_bool(options['iterative_solver']) + vmopts.iterative_tol = ct.c_double(options['iterative_tol']) + vmopts.iterative_precond = ct.c_bool(options['iterative_precond']) flightconditions = FlightConditions() flightconditions.rho = options['rho'] @@ -221,18 +221,18 @@ def vlm_solver_lifting_and_nonlifting_bodies(ts_info_lifting, ts_info_nonlifting vmopts = VMopts() vmopts.Steady = ct.c_bool(True) vmopts.NumSurfaces = ct.c_uint(ts_info_lifting.n_surf) - vmopts.horseshoe = ct.c_bool(options['horseshoe'].value) - vmopts.dt = ct.c_double(options["rollup_dt"].value) - vmopts.n_rollup = ct.c_uint(options["n_rollup"].value) - vmopts.rollup_tolerance = ct.c_double(options["rollup_tolerance"].value) - vmopts.rollup_aic_refresh = ct.c_uint(options['rollup_aic_refresh'].value) - vmopts.NumCores = ct.c_uint(options['num_cores'].value) - vmopts.iterative_solver = ct.c_bool(options['iterative_solver'].value) - vmopts.iterative_tol = ct.c_double(options['iterative_tol'].value) - vmopts.iterative_precond = ct.c_bool(options['iterative_precond'].value) + vmopts.horseshoe = ct.c_bool(options['horseshoe']) + vmopts.dt = ct.c_double(options["rollup_dt"]) + vmopts.n_rollup = ct.c_uint(options["n_rollup"]) + vmopts.rollup_tolerance = ct.c_double(options["rollup_tolerance"]) + vmopts.rollup_aic_refresh = ct.c_uint(options['rollup_aic_refresh']) + vmopts.NumCores = ct.c_uint(options['num_cores']) + vmopts.iterative_solver = ct.c_bool(options['iterative_solver']) + vmopts.iterative_tol = ct.c_double(options['iterative_tol']) + vmopts.iterative_precond = ct.c_bool(options['iterative_precond']) vmopts.cfl1 = ct.c_bool(options['cfl1']) - vmopts.vortex_radius = ct.c_double(options['vortex_radius'].value) - vmopts.vortex_radius_wake_ind = ct.c_double(options['vortex_radius_wake_ind'].value) + vmopts.vortex_radius = ct.c_double(options['vortex_radius']) + vmopts.vortex_radius_wake_ind = ct.c_double(options['vortex_radius_wake_ind']) vmopts_nonlifting = VMopts() vmopts_nonlifting.Steady = ct.c_bool(True) From d202d82828c697c6693cb0b2168799866a797e57 Mon Sep 17 00:00:00 2001 From: sduess Date: Mon, 26 Apr 2021 13:21:35 +0100 Subject: [PATCH 045/232] fix[aerogrid]: combines if statements - before format error due to intent --- sharpy/aero/models/aerogrid.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/sharpy/aero/models/aerogrid.py b/sharpy/aero/models/aerogrid.py index 166e00808..e4e6c405b 100644 --- a/sharpy/aero/models/aerogrid.py +++ b/sharpy/aero/models/aerogrid.py @@ -288,8 +288,7 @@ def generate_zeta_timestep_info(self, structure_tstep, aero_tstep, beam, setting orientation_in=self.aero_settings['freestream_dir'], calculate_zeta_dot=True)) # set junction boundary conditions for later phantom cell creation in UVLM - if "junction_boundary_condition" in self.data_dict: - if sum(self.data_dict["junction_boundary_condition"])>0: + if "junction_boundary_condition" in self.data_dict and sum(self.data_dict["junction_boundary_condition"])>0: self.generate_phantom_panels_at_junction(aero_tstep) From 76c6a1e94a12d4cff65396e61836125b7e084964 Mon Sep 17 00:00:00 2001 From: sduess Date: Mon, 26 Apr 2021 14:44:05 +0100 Subject: [PATCH 046/232] fix [liftdistribution]: adjust naming scheme to dev_fuselage --- sharpy/postproc/liftdistribution.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/sharpy/postproc/liftdistribution.py b/sharpy/postproc/liftdistribution.py index 9235bacca..98308066b 100644 --- a/sharpy/postproc/liftdistribution.py +++ b/sharpy/postproc/liftdistribution.py @@ -69,7 +69,7 @@ def lift_distribution(self, struct_tstep, aero_tstep): self.data.structure.node_master_elem, self.data.structure.connectivities, struct_tstep.cag(), - self.data.aero.aero_dict) + self.data.aero.data_dict) # Prepare output matrix and file N_nodes = self.data.structure.num_node numb_col = 4 @@ -77,7 +77,7 @@ def lift_distribution(self, struct_tstep, aero_tstep): # get aero forces lift_distribution = np.zeros((N_nodes, numb_col)) for inode in range(N_nodes): - if self.data.aero.aero_dict['aero_node'][inode]: + if self.data.aero.data_dict['aero_node'][inode]: # transform forces from B to A frame lift_distribution[inode,3]=np.dot(rot.T, forces[inode, :3])[2] # lift force lift_distribution[inode,2]=struct_tstep.pos[inode, 2] #z @@ -92,7 +92,7 @@ def lift_distribution(self, struct_tstep, aero_tstep): numb_col += 1 lift_distribution = np.concatenate((lift_distribution, np.zeros((N_nodes,1))), axis=1) for inode in range(N_nodes): - if self.data.aero.aero_dict['aero_node'][inode]: + if self.data.aero.data_dict['aero_node'][inode]: local_node = self.data.aero.struct2aero_mapping[inode][0]["i_n"] ielem, _ = self.data.structure.node_master_elem[inode] i_surf = int(self.data.aero.surface_distribution[ielem]) @@ -112,7 +112,7 @@ def calculate_strip_area(self, aero_tstep): # to trailing edge) connected to the beam node are accounted. strip_area = [] for i_surf in range(self.data.aero.n_surf): - N_panel = self.data.aero.aero_dimensions[i_surf][1] + N_panel = self.data.aero.dimensions[i_surf][1] array_panel_area = np.zeros((N_panel)) # the area is calculated for all chordwise panels together for i_panel in range(N_panel): From 7e49931c4c44f49b4361cd64e1d836eaa4f8021d Mon Sep 17 00:00:00 2001 From: sduess Date: Mon, 3 May 2021 10:11:59 +0100 Subject: [PATCH 047/232] add[staticcoupled]: consider nonlifting body effects --- sharpy/solvers/staticcoupled.py | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/sharpy/solvers/staticcoupled.py b/sharpy/solvers/staticcoupled.py index c058fd258..f3f2b92a2 100644 --- a/sharpy/solvers/staticcoupled.py +++ b/sharpy/solvers/staticcoupled.py @@ -71,6 +71,10 @@ class StaticCoupled(BaseSolver): settings_description['runtime_generators'] = 'The dictionary keys are the runtime generators to be used. ' \ 'The dictionary values are dictionaries with the settings ' \ 'needed by each generator.' + + settings_types['nonlifting_body_interaction'] = 'bool' + settings_default['nonlifting_body_interaction'] = False + settings_description['nonlifting_body_interaction'] = 'Consider forces induced by nonlifting bodies' settings_table = settings.SettingsTable() __doc__ += settings_table.generate(settings_types, settings_default, settings_description, settings_options) @@ -187,6 +191,18 @@ def run(self): self.data.structure.timestep_info[self.data.ts], struct_forces, rho=self.aero_solver.settings['rho']) + + if self.settings['nonlifting_body_interaction']: + struct_forces += mapping.aero2struct_force_mapping( + self.data.nonlifting_body.timestep_info[self.data.ts].forces, + self.data.nonlifting_body.struct2aero_mapping, + self.data.nonlifting_body.timestep_info[self.data.ts].zeta, + self.data.structure.timestep_info[self.data.ts].pos, + self.data.structure.timestep_info[self.data.ts].psi, + self.data.structure.node_master_elem, + self.data.structure.connectivities, + self.data.structure.timestep_info[self.data.ts].cag(), + self.data.nonlifting_body.data_dict) # Add external forces if self.with_runtime_generators: From 584afff3a80e723c5399458d3c98be7976cae2cb Mon Sep 17 00:00:00 2001 From: Unknown Date: Tue, 4 May 2021 11:18:40 +0100 Subject: [PATCH 048/232] no structural solver --- sharpy/solvers/nostructural.py | 72 ++++++++++++++++++++++++++++++++++ 1 file changed, 72 insertions(+) create mode 100644 sharpy/solvers/nostructural.py diff --git a/sharpy/solvers/nostructural.py b/sharpy/solvers/nostructural.py new file mode 100644 index 000000000..44628c2b2 --- /dev/null +++ b/sharpy/solvers/nostructural.py @@ -0,0 +1,72 @@ +import numpy as np + +import sharpy.utils.cout_utils as cout +import sharpy.utils.settings as settings +from sharpy.utils.solver_interface import solver, BaseSolver, solver_from_string + +_BaseStructural = solver_from_string('_BaseStructural') + +@solver +class NoStructural(_BaseStructural): + """ + Structural solver used for the static simulation of free-flying structures. + + This solver provides an interface to the structural library (``xbeam``) and updates the structural parameters + for every k-th step of the FSI iteration. + + This solver can be called as part of a standalone structural simulation or as the structural solver of a coupled + static aeroelastic simulation. + + """ + solver_id = 'NoStructural' + solver_classification = 'structural' + + # settings list + settings_types = _BaseStructural.settings_types.copy() + settings_default = _BaseStructural.settings_default.copy() + settings_description = _BaseStructural.settings_description.copy() + + settings_types['initial_position'] = 'list(float)' + settings_default['initial_position'] = np.array([0.0, 0.0, 0.0]) + + settings_table = settings.SettingsTable() + __doc__ += settings_table.generate(settings_types, settings_default, settings_description) + + def __init__(self): + self.data = None + self.settings = None + + def initialise(self, data, custom_settings=None): + self.data = data + if custom_settings is None: + self.settings = data.settings[self.solver_id] + else: + self.settings = custom_settings + settings.to_custom_types(self.settings, self.settings_types, self.settings_default) + + def run(self): + self.data.structure.timestep_info[self.data.ts].for_pos[0:3] = self.settings['initial_position'] + self.extract_resultants() + return self.data + + def next_step(self): + self.data.structure.next_step() + + def extract_resultants(self, tstep=None): + if tstep is None: + tstep = self.data.structure.timestep_info[self.data.ts] + steady, grav = tstep.extract_resultants(self.data.structure, force_type=['steady', 'grav']) + totals = steady + grav + return totals[0:3], totals[3:6] + + def update(self, tstep=None): + self.create_q_vector(tstep) + + def create_q_vector(self, tstep=None): + import sharpy.structure.utils.xbeamlib as xb + if tstep is None: + tstep = self.data.structure.timestep_info[-1] + + xb.xbeam_solv_disp2state(self.data.structure, tstep) + + From d0ba0f090055343bad67ba577bc0e44b3de745e1 Mon Sep 17 00:00:00 2001 From: sduess Date: Thu, 6 May 2021 21:10:42 +0100 Subject: [PATCH 049/232] add: more parameters are stored in VMopts struct --- sharpy/aero/utils/uvlmlib.py | 33 ++++++++++++++++++++++++++++++--- 1 file changed, 30 insertions(+), 3 deletions(-) diff --git a/sharpy/aero/utils/uvlmlib.py b/sharpy/aero/utils/uvlmlib.py index a5362c367..bef2ae6c3 100644 --- a/sharpy/aero/utils/uvlmlib.py +++ b/sharpy/aero/utils/uvlmlib.py @@ -2,6 +2,7 @@ import sharpy.utils.ctypes_utils as ct_utils import ctypes as ct +from ctypes import * import numpy as np import platform import os @@ -9,6 +10,8 @@ UvlmLib = ct_utils.import_ctypes_lib(SharpyDir + '/lib/UVLM/lib/', 'libuvlm') +# TODO: Combine VMOpts and UVMOpts (Class + inheritance)? +# TODO: Combine solver functions (e.g. vlm solver is able to start nonlifting only, lifitng only, nonlifting and lifting coupled) class VMopts(ct.Structure): """ctypes definition for VMopts class @@ -21,11 +24,16 @@ class VMopts(ct.Structure): bool NewAIC; double DelTime; bool Rollup; + bool only_lifting; + bool only_nonlifting; unsigned int NumCores; unsigned int NumSurfaces; + unsigned int NumSurfacesNonlifting; bool cfl1; double vortex_radius; double vortex_radius_wake_ind; + double* centre_rot_g[3]; + double* rbm_vel_g[6]; }; """ _fields_ = [("ImageMethod", ct.c_bool), @@ -35,8 +43,11 @@ class VMopts(ct.Structure): ("NewAIC", ct.c_bool), ("DelTime", ct.c_double), ("Rollup", ct.c_bool), + ("only_lifting", ct.c_bool), + ("only_nonlifting", ct.c_bool), ("NumCores", ct.c_uint), ("NumSurfaces", ct.c_uint), + ("NumSurfacesNonlifting", ct.c_uint), ("dt", ct.c_double), ("n_rollup", ct.c_uint), ("rollup_tolerance", ct.c_double), @@ -46,7 +57,11 @@ class VMopts(ct.Structure): ("iterative_precond", ct.c_bool), ("cfl1", ct.c_bool), ("vortex_radius", ct.c_double), - ("vortex_radius_wake_ind", ct.c_double)] + ("vortex_radius_wake_ind", ct.c_double), + ("centre_rot_g", ct.c_double * 3), + ("rbm_vel_g", ct.c_double * 6)] + + def __init__(self): ct.Structure.__init__(self) @@ -57,8 +72,11 @@ def __init__(self): self.NewAIC = ct.c_bool(False) # legacy var self.DelTime = ct.c_double(1.0) self.Rollup = ct.c_bool(False) + self.only_lifting = ct.c_bool(False) + self.only_nonlifting = ct.c_bool(False) self.NumCores = ct.c_uint(4) self.NumSurfaces = ct.c_uint(1) + self.NumSurfacesNonlifting = ct.c_uint(0) self.dt = ct.c_double(0.01) self.n_rollup = ct.c_uint(0) self.rollup_tolerance = ct.c_double(1e-5) @@ -69,6 +87,7 @@ def __init__(self): self.cfl1 = ct.c_bool(True) self.vortex_radius = ct.c_double(vortex_radius_def) self.vortex_radius_wake_ind = ct.c_double(vortex_radius_def) + self.centre_rot_g = np.ctypeslib.as_ctypes(np.zeros((3))) self.rbm_vel_g = np.ctypeslib.as_ctypes(np.zeros((6))) @@ -76,6 +95,7 @@ class UVMopts(ct.Structure): _fields_ = [("dt", ct.c_double), ("NumCores", ct.c_uint), ("NumSurfaces", ct.c_uint), + ("NumSurfacesNonlifting", ct.c_uint), # ("steady_n_rollup", ct.c_uint), # ("steady_rollup_tolerance", ct.c_double), # ("steady_rollup_aic_refresh", ct.c_uint), @@ -85,7 +105,9 @@ class UVMopts(ct.Structure): ("iterative_solver", ct.c_bool), ("iterative_tol", ct.c_double), ("iterative_precond", ct.c_bool), - ("convect_wake", ct.c_bool), + ("convect_wake", ct.c_bool), + ("only_lifting", ct.c_bool), + ("only_nonlifting", ct.c_bool), ("cfl1", ct.c_bool), ("vortex_radius", ct.c_double), ("vortex_radius_wake_ind", ct.c_double), @@ -93,13 +115,16 @@ class UVMopts(ct.Structure): ("filter_method", ct.c_uint), ("interp_method", ct.c_uint), ("yaw_slerp", ct.c_double), - ("quasi_steady", ct.c_bool),] + ("quasi_steady", ct.c_bool), + ("centre_rot_g", ct.c_double * 3), + ("rbm_vel_g", ct.c_double * 6)] def __init__(self): ct.Structure.__init__(self) self.dt = ct.c_double(0.01) self.NumCores = ct.c_uint(4) self.NumSurfaces = ct.c_uint(1) + self.NumSurfacesNonlifting = ct.c_uint(0) self.convection_scheme = ct.c_uint(2) # self.Mstar = ct.c_uint(10) self.ImageMethod = ct.c_bool(False) @@ -112,6 +137,8 @@ def __init__(self): self.vortex_radius_wake_ind = ct.c_double(vortex_radius_def) self.yaw_slerp = ct.c_double(0.) self.quasi_steady = ct.c_bool(False) + self.centre_rot_g = np.ctypeslib.as_ctypes(np.zeros((3))) + self.rbm_vel_g = np.ctypeslib.as_ctypes(np.zeros((6))) class FlightConditions(ct.Structure): From da1e19ab1c840e56859d2c92d0182fadcc7186f7 Mon Sep 17 00:00:00 2001 From: sduess Date: Thu, 6 May 2021 21:12:21 +0100 Subject: [PATCH 050/232] refactor: set options function for structs - introduce function to setup the option struct for the UVLM automatically --- sharpy/aero/utils/uvlmlib.py | 253 +++++++++++++++++++---------------- 1 file changed, 140 insertions(+), 113 deletions(-) diff --git a/sharpy/aero/utils/uvlmlib.py b/sharpy/aero/utils/uvlmlib.py index bef2ae6c3..4d64f1fde 100644 --- a/sharpy/aero/utils/uvlmlib.py +++ b/sharpy/aero/utils/uvlmlib.py @@ -89,9 +89,39 @@ def __init__(self): self.vortex_radius_wake_ind = ct.c_double(vortex_radius_def) self.centre_rot_g = np.ctypeslib.as_ctypes(np.zeros((3))) self.rbm_vel_g = np.ctypeslib.as_ctypes(np.zeros((6))) + print(self.rbm_vel_g) + def set_options(self, options, n_surfaces = 0, n_surfaces_nonlifting = 0, rbm_vel_g = np.zeros(6)): + self.Steady = ct.c_bool(True) + self.NumSurfaces = ct.c_uint(n_surfaces) + self.NumSurfacesNonlifting = ct.c_uint(n_surfaces_nonlifting) + self.horseshoe = ct.c_bool(options['horseshoe']) + self.dt = ct.c_double(options["rollup_dt"]) + self.n_rollup = ct.c_uint(options["n_rollup"]) + self.rollup_tolerance = ct.c_double(options["rollup_tolerance"]) + self.rollup_aic_refresh = ct.c_uint(options['rollup_aic_refresh']) + self.NumCores = ct.c_uint(options['num_cores']) + self.iterative_solver = ct.c_bool(options['iterative_solver']) + self.iterative_tol = ct.c_double(options['iterative_tol']) + self.iterative_precond = ct.c_bool(options['iterative_precond']) + self.cfl1 = ct.c_bool(options['cfl1']) + self.vortex_radius = ct.c_double(options['vortex_radius']) + self.vortex_radius_wake_ind = ct.c_double(options['vortex_radius_wake_ind']) + + self.only_nonlifting = ct.c_bool(options["only_nonlifting"]) + self.only_lifting = ct.c_bool(not options["nonlifting_body_interactions"]) + print("Centre rot g = ", options["centre_rot_g"]) + rbm_vel_g = [1, 2, 5] + print("RBM = ", rbm_vel_g) + for i in range(len(options["centre_rot_g"])): + self.centre_rot_g[i] = ct.c_double(options["centre_rot_g"][i]) + for i in range(len(rbm_vel_g)): + self.rbm_vel_g[i] = ct.c_double(rbm_vel_g[i]) + class UVMopts(ct.Structure): + # TODO: add set_options function + # TODO: possible to combine with VMopts? _fields_ = [("dt", ct.c_double), ("NumCores", ct.c_uint), ("NumSurfaces", ct.c_uint), @@ -140,6 +170,43 @@ def __init__(self): self.centre_rot_g = np.ctypeslib.as_ctypes(np.zeros((3))) self.rbm_vel_g = np.ctypeslib.as_ctypes(np.zeros((6))) + def set_options(self, + options, + n_surfaces = 0, + n_surfaces_nonlifting = 0, + dt = None, + convect_wake = False, + rbm_vel_g = np.zeros(6), + image_method = False): + if dt is None: + self.dt = ct.c_double(options["dt"]) + else: + self.dt = ct.c_double(dt) + self.NumCores = ct.c_uint(options["num_cores"]) + self.NumSurfaces = ct.c_uint(n_surfaces) + self.ImageMethod = ct.c_bool(False) + self.convection_scheme = ct.c_uint(options["convection_scheme"]) + self.iterative_solver = ct.c_bool(options['iterative_solver']) + self.iterative_tol = ct.c_double(options['iterative_tol']) + self.iterative_precond = ct.c_bool(options['iterative_precond']) + self.convect_wake = ct.c_bool(convect_wake) + self.cfl1 = ct.c_bool(options['cfl1']) + self.vortex_radius = ct.c_double(options['vortex_radius']) + self.vortex_radius_wake_ind = ct.c_double(options['vortex_radius_wake_ind']) + self.interp_coords = ct.c_uint(options["interp_coords"]) + self.filter_method = ct.c_uint(options["filter_method"]) + self.interp_method = ct.c_uint(options["interp_method"]) + self.yaw_slerp = ct.c_double(options["yaw_slerp"]) + self.quasi_steady = ct.c_bool(options['quasi_steady']) + + self.only_nonlifting = ct.c_bool(options["only_nonlifting"]) + self.only_lifting = ct.c_bool(not options["nonlifting_body_interactions"]) + print("Centre rot g = ", options["centre_rot_g"]) + print("RBM = ", rbm_vel_g) + for i in range(len(options["centre_rot_g"])): + self.centre_rot_g[i] = ct.c_double(options["centre_rot_g"][i]) + for i in range(len(rbm_vel_g)): + self.rbm_vel_g[i] = ct.c_double(rbm_vel_g[i]) class FlightConditions(ct.Structure): _fields_ = [("uinf", ct.c_double), @@ -164,26 +231,12 @@ def __init__(self): # type for 2d integer matrix t_2int = ct.POINTER(ct.c_int)*2 - def vlm_solver(ts_info, options): run_VLM = UvlmLib.run_VLM run_VLM.restype = None vmopts = VMopts() - vmopts.Steady = ct.c_bool(True) - vmopts.NumSurfaces = ct.c_uint(ts_info.n_surf) - vmopts.horseshoe = ct.c_bool(options['horseshoe']) - vmopts.dt = ct.c_double(options["rollup_dt"]) - vmopts.n_rollup = ct.c_uint(options["n_rollup"]) - vmopts.rollup_tolerance = ct.c_double(options["rollup_tolerance"]) - vmopts.rollup_aic_refresh = ct.c_uint(options['rollup_aic_refresh']) - vmopts.NumCores = ct.c_uint(options['num_cores']) - vmopts.iterative_solver = ct.c_bool(options['iterative_solver']) - vmopts.iterative_tol = ct.c_double(options['iterative_tol']) - vmopts.iterative_precond = ct.c_bool(options['iterative_precond']) - vmopts.cfl1 = ct.c_bool(options['cfl1']) - vmopts.vortex_radius = ct.c_double(options['vortex_radius']) - vmopts.vortex_radius_wake_ind = ct.c_double(options['vortex_radius_wake_ind']) + vmopts.set_options(options, n_surfaces = ts_info.n_surf) flightconditions = FlightConditions() flightconditions.rho = options['rho'] @@ -211,21 +264,9 @@ def vlm_solver(ts_info, options): def vlm_solver_nonlifting_body(ts_info, options): run_VLM_nonlifting = UvlmLib.run_VLM_nonlifting_body - run_VLM_nonliftingrestype = None - vmopts = VMopts() - vmopts.Steady = ct.c_bool(True) - vmopts.NumSurfaces = ct.c_uint(ts_info.n_surf) - vmopts.horseshoe = ct.c_bool(options['horseshoe']) - vmopts.dt = ct.c_double(options["rollup_dt"]) - vmopts.n_rollup = ct.c_uint(options["n_rollup"]) - vmopts.rollup_tolerance = ct.c_double(options["rollup_tolerance"]) - vmopts.rollup_aic_refresh = ct.c_uint(options['rollup_aic_refresh']) - vmopts.NumCores = ct.c_uint(options['num_cores']) - vmopts.iterative_solver = ct.c_bool(options['iterative_solver']) - vmopts.iterative_tol = ct.c_double(options['iterative_tol']) - vmopts.iterative_precond = ct.c_bool(options['iterative_precond']) + vmopts.set_options(options, n_surfaces_nonlifting = ts_info.n_surf) flightconditions = FlightConditions() flightconditions.rho = options['rho'] @@ -247,25 +288,8 @@ def vlm_solver_lifting_and_nonlifting_bodies(ts_info_lifting, ts_info_nonlifting run_VLM_lifting_and_nonlifting = UvlmLib.run_VLM_lifting_and_nonlifting_bodies run_VLM_lifting_and_nonlifting.restype = None - vmopts = VMopts() - vmopts.Steady = ct.c_bool(True) - vmopts.NumSurfaces = ct.c_uint(ts_info_lifting.n_surf) - vmopts.horseshoe = ct.c_bool(options['horseshoe']) - vmopts.dt = ct.c_double(options["rollup_dt"]) - vmopts.n_rollup = ct.c_uint(options["n_rollup"]) - vmopts.rollup_tolerance = ct.c_double(options["rollup_tolerance"]) - vmopts.rollup_aic_refresh = ct.c_uint(options['rollup_aic_refresh']) - vmopts.NumCores = ct.c_uint(options['num_cores']) - vmopts.iterative_solver = ct.c_bool(options['iterative_solver']) - vmopts.iterative_tol = ct.c_double(options['iterative_tol']) - vmopts.iterative_precond = ct.c_bool(options['iterative_precond']) - vmopts.cfl1 = ct.c_bool(options['cfl1']) - vmopts.vortex_radius = ct.c_double(options['vortex_radius']) - vmopts.vortex_radius_wake_ind = ct.c_double(options['vortex_radius_wake_ind']) - - vmopts_nonlifting = VMopts() - vmopts_nonlifting.Steady = ct.c_bool(True) - vmopts_nonlifting.NumSurfaces = ct.c_uint(ts_info_nonlifting.n_surf) + vmopts = VMopts() + vmopts.set_options(options, n_surfaces = ts_info_lifting.n_surf, n_surfaces_nonlifting = ts_info_nonlifting.n_surf) flightconditions = FlightConditions() flightconditions.rho = options['rho'] @@ -288,7 +312,6 @@ def vlm_solver_lifting_and_nonlifting_bodies(ts_info_lifting, ts_info_nonlifting ts_info_lifting.ct_p_forces, ts_info_lifting.ct_p_flag_zeta_phantom, p_rbm_vel_g, - ct.byref(vmopts_nonlifting), ts_info_nonlifting.ct_p_dimensions, ts_info_nonlifting.ct_p_zeta, ts_info_nonlifting.ct_p_u_ext, @@ -298,79 +321,78 @@ def vlm_solver_lifting_and_nonlifting_bodies(ts_info_lifting, ts_info_nonlifting ts_info_lifting.remove_ctypes_pointers() ts_info_nonlifting.remove_ctypes_pointers() -def uvlm_init(ts_info, options): - init_UVLM = UvlmLib.init_UVLM - init_UVLM.restype = None - vmopts = VMopts() - vmopts.Steady = ct.c_bool(True) - # vmopts.Mstar = ct.c_uint(options['mstar']) - vmopts.NumSurfaces = ct.c_uint(ts_info.n_surf) - vmopts.horseshoe = ct.c_bool(False) - vmopts.dt = options["dt"] - try: - vmopts.n_rollup = ct.c_uint(options["steady_n_rollup"]) - vmopts.rollup_tolerance = ct.c_double(options["steady_rollup_tolerance"]) - vmopts.rollup_aic_refresh = ct.c_uint(options['steady_rollup_aic_refresh']) - except KeyError: - pass - vmopts.NumCores = ct.c_uint(options['num_cores']) - vmopts.vortex_radius = ct.c_double(options['vortex_radius']) - vmopts.vortex_radius_wake_ind = ct.c_double(options['vortex_radius_wake_ind']) - vmopts.quasi_steady = ct.c_bool(options['quasi_steady']) +def uvlm_solver(i_iter, ts_info, struct_ts_info, options, convect_wake=True, dt=None): + rbm_vel = struct_ts_info.for_vel.copy() + rbm_vel[0:3] = np.dot(struct_ts_info.cga(), rbm_vel[0:3]) + rbm_vel[3:6] = np.dot(struct_ts_info.cga(), rbm_vel[3:6]) + p_rbm_vel = rbm_vel.ctypes.data_as(ct.POINTER(ct.c_double)) + p_centre_rot = options['centre_rot'].ctypes.data_as(ct.POINTER(ct.c_double)) + + + run_UVLM = UvlmLib.run_UVLM + run_UVLM.restype = None + + uvmopts = UVMopts() + uvmopts.set_options(options, + n_surfaces = ts_info.n_surf, + n_surfaces_nonlifting = 0, + dt = dt, + convect_wake = convect_wake, + rbm_vel_g = rbm_vel, + image_method = False) + flightconditions = FlightConditions() flightconditions.rho = options['rho'] flightconditions.uinf = np.ctypeslib.as_ctypes(np.linalg.norm(ts_info.u_ext[0][:, 0, 0])) + # direction = np.array([1.0, 0, 0]) flightconditions.uinf_direction = np.ctypeslib.as_ctypes(ts_info.u_ext[0][:, 0, 0]/flightconditions.uinf) + # flightconditions.uinf_direction = np.ctypeslib.as_ctypes(direction) - # rbm_vel[0:3] = np.dot(inertial2aero.transpose(), rbm_vel[0:3]) - # rbm_vel[3:6] = np.dot(inertial2aero.transpose(), rbm_vel[3:6]) - p_rbm_vel = np.zeros((6,)).ctypes.data_as(ct.POINTER(ct.c_double)) - + + i = ct.c_uint(i_iter) ts_info.generate_ctypes_pointers() - init_UVLM(ct.byref(vmopts), - ct.byref(flightconditions), - ts_info.ct_p_dimensions, - ts_info.ct_p_dimensions_star, - ts_info.ct_p_u_ext, - ts_info.ct_p_zeta, - ts_info.ct_p_zeta_star, - ts_info.ct_p_zeta_dot, - ts_info.ct_p_zeta_star_dot, - p_rbm_vel, - ts_info.ct_p_gamma, - ts_info.ct_p_gamma_star, - ts_info.ct_p_normals, - ts_info.ct_p_forces) + # previous_ts_info.generate_ctypes_pointers() + run_UVLM(ct.byref(uvmopts), + ct.byref(flightconditions), + ts_info.ct_p_dimensions, + ts_info.ct_p_dimensions_star, + ct.byref(i), + ts_info.ct_p_u_ext, + ts_info.ct_p_u_ext_star, + ts_info.ct_p_zeta, + ts_info.ct_p_zeta_star, + ts_info.ct_p_zeta_dot, + ts_info.ct_p_gamma, + ts_info.ct_p_gamma_star, + ts_info.ct_p_dist_to_orig, + # previous_ts_info.ct_p_gamma, + ts_info.ct_p_normals, + ts_info.ct_p_forces, + ts_info.ct_p_dynamic_forces) ts_info.remove_ctypes_pointers() + # previous_ts_info.remove_ctypes_pointers() +def uvlm_solver_lifting_and_nonlifting(i_iter, ts_info, ts_info_nonlifting, struct_ts_info, options, convect_wake=True, dt=None): + rbm_vel = struct_ts_info.for_vel.copy() + rbm_vel[0:3] = np.dot(struct_ts_info.cga(), rbm_vel[0:3]) + rbm_vel[3:6] = np.dot(struct_ts_info.cga(), rbm_vel[3:6]) + p_rbm_vel = rbm_vel.ctypes.data_as(ct.POINTER(ct.c_double)) + p_centre_rot = options['centre_rot'].ctypes.data_as(ct.POINTER(ct.c_double)) -def uvlm_solver(i_iter, ts_info, struct_ts_info, options, convect_wake=True, dt=None): - run_UVLM = UvlmLib.run_UVLM - run_UVLM.restype = None uvmopts = UVMopts() - if dt is None: - uvmopts.dt = ct.c_double(options["dt"]) - else: - uvmopts.dt = ct.c_double(dt) - uvmopts.NumCores = ct.c_uint(options["num_cores"]) - uvmopts.NumSurfaces = ct.c_uint(ts_info.n_surf) - uvmopts.ImageMethod = ct.c_bool(False) - uvmopts.convection_scheme = ct.c_uint(options["convection_scheme"]) - uvmopts.iterative_solver = ct.c_bool(options['iterative_solver']) - uvmopts.iterative_tol = ct.c_double(options['iterative_tol']) - uvmopts.iterative_precond = ct.c_bool(options['iterative_precond']) - uvmopts.convect_wake = ct.c_bool(convect_wake) - uvmopts.cfl1 = ct.c_bool(options['cfl1']) - uvmopts.vortex_radius = ct.c_double(options['vortex_radius']) - uvmopts.vortex_radius_wake_ind = ct.c_double(options['vortex_radius_wake_ind']) - uvmopts.interp_coords = ct.c_uint(options["interp_coords"]) - uvmopts.filter_method = ct.c_uint(options["filter_method"]) - uvmopts.interp_method = ct.c_uint(options["interp_method"]) - uvmopts.yaw_slerp = ct.c_double(options["yaw_slerp"]) - uvmopts.quasi_steady = ct.c_bool(options['quasi_steady']) + uvmopts.set_options(options, + n_surfaces = ts_info.n_surf, + n_surfaces_nonlifting = ts_info_nonlifting.n_surf, + dt = dt, + convect_wake = convect_wake, + rbm_vel_g = rbm_vel, + image_method = False) + run_UVLM = UvlmLib.run_UVLM_lifting_and_nonlifting + run_UVLM.restype = None + flightconditions = FlightConditions() flightconditions.rho = options['rho'] @@ -387,6 +409,7 @@ def uvlm_solver(i_iter, ts_info, struct_ts_info, options, convect_wake=True, dt= i = ct.c_uint(i_iter) ts_info.generate_ctypes_pointers() + ts_info_nonlifting.generate_ctypes_pointers() # previous_ts_info.generate_ctypes_pointers() run_UVLM(ct.byref(uvmopts), ct.byref(flightconditions), @@ -398,19 +421,23 @@ def uvlm_solver(i_iter, ts_info, struct_ts_info, options, convect_wake=True, dt= ts_info.ct_p_zeta, ts_info.ct_p_zeta_star, ts_info.ct_p_zeta_dot, - p_rbm_vel, - p_centre_rot, ts_info.ct_p_gamma, ts_info.ct_p_gamma_star, ts_info.ct_p_dist_to_orig, - # previous_ts_info.ct_p_gamma, ts_info.ct_p_normals, ts_info.ct_p_forces, - ts_info.ct_p_dynamic_forces) + ts_info.ct_p_dynamic_forces, + ts_info.ct_p_flag_zeta_phantom, + ts_info_nonlifting.ct_p_dimensions, + ts_info_nonlifting.ct_p_zeta, + ts_info_nonlifting.ct_p_u_ext, + ts_info_nonlifting.ct_p_sigma, + ts_info_nonlifting.ct_p_forces) + ts_info.remove_ctypes_pointers() + ts_info_nonlifting.remove_ctypes_pointers() # previous_ts_info.remove_ctypes_pointers() - def uvlm_calculate_unsteady_forces(ts_info, struct_ts_info, options, From 15abd70ce5bbadee06547c7ca2edae96e9e33d9f Mon Sep 17 00:00:00 2001 From: sduess Date: Thu, 6 May 2021 21:13:15 +0100 Subject: [PATCH 051/232] remove: debug print --- sharpy/aero/models/aerogrid.py | 1 - 1 file changed, 1 deletion(-) diff --git a/sharpy/aero/models/aerogrid.py b/sharpy/aero/models/aerogrid.py index e4e6c405b..c1f814135 100644 --- a/sharpy/aero/models/aerogrid.py +++ b/sharpy/aero/models/aerogrid.py @@ -393,7 +393,6 @@ def generate_strip(node_info, airfoil_db, aligned_grid, orientation_in=np.array( if node_info['M_distribution'] == 'uniform': strip_coordinates_b_frame[1, :] = np.linspace(0.0, 1.0, node_info['M'] + 1) elif node_info['M_distribution'] == '1-cos': - print("Distribution = ", node_info['M_distribution']) domain = np.linspace(0, 1.0, node_info['M'] + 1) strip_coordinates_b_frame[1, :] = 0.5*(1.0 - np.cos(domain*np.pi)) elif node_info['M_distribution'].lower() == 'user_defined': From c064a54b0a21fe69fa2dc6dfff68ebf10dcfcb49 Mon Sep 17 00:00:00 2001 From: sduess Date: Thu, 6 May 2021 21:15:40 +0100 Subject: [PATCH 052/232] add [Dynamiccoupling]: nonlifting body effects --- sharpy/solvers/dynamiccoupled.py | 58 ++++++++++++++++++++++++++------ 1 file changed, 48 insertions(+), 10 deletions(-) diff --git a/sharpy/solvers/dynamiccoupled.py b/sharpy/solvers/dynamiccoupled.py index 701439cf4..f9d897a0d 100644 --- a/sharpy/solvers/dynamiccoupled.py +++ b/sharpy/solvers/dynamiccoupled.py @@ -175,6 +175,11 @@ class DynamicCoupled(BaseSolver): 'The dictionary values are dictionaries with the settings ' \ 'needed by each generator.' + + settings_types['nonlifting_body_interaction'] = 'bool' + settings_default['nonlifting_body_interaction'] = True #False + settings_description['nonlifting_body_interaction'] = 'Effect of Nonlifting Bodies on Lifting bodies are considered' + settings_table = settings.SettingsTable() __doc__ += settings_table.generate(settings_types, settings_default, settings_description, settings_options) @@ -481,6 +486,8 @@ def time_loop(self, in_queue=None, out_queue=None, finish_event=None): structural_kstep = self.data.structure.timestep_info[-1].copy() aero_kstep = self.data.aero.timestep_info[-1].copy() + if self.settings['nonlifting_body_interaction']: + nl_body_kstep = self.data.nonlifting_body.timestep_info[-1].copy() self.logger.debug('Time step {}'.format(self.data.ts)) # Add the controller here @@ -567,10 +574,18 @@ def time_loop(self, in_queue=None, out_queue=None, finish_event=None): # run the solver ini_time_aero = time.perf_counter() - self.data = self.aero_solver.run(aero_kstep, - structural_kstep, - convect_wake=True, - unsteady_contribution=unsteady_contribution) + if self.settings['nonlifting_body_interaction']: + self.data = self.aero_solver.run(aero_kstep, + structural_kstep, + convect_wake=True, + unsteady_contribution=unsteady_contribution, + nl_body_tstep = nl_body_kstep) + + else: + self.data = self.aero_solver.run(aero_kstep, + structural_kstep, + convect_wake=True, + unsteady_contribution=unsteady_contribution) self.time_aero += time.perf_counter() - ini_time_aero previous_kstep = structural_kstep.copy() @@ -579,12 +594,24 @@ def time_loop(self, in_queue=None, out_queue=None, finish_event=None): previous_kstep.runtime_generated_forces = previous_runtime_generated_forces.astype(dtype=ct.c_double, order='F', copy=True) # move the aerodynamic surface according the the structural one - self.aero_solver.update_custom_grid(structural_kstep, - aero_kstep) - - self.map_forces(aero_kstep, + + if self.settings['nonlifting_body_interaction']: + self.aero_solver.update_custom_grid(structural_kstep, + aero_kstep, + nl_body_tstep = nl_body_kstep) + else: + self.aero_solver.update_custom_grid(structural_kstep, + aero_kstep, + nl_body_tstep = nl_body_kstep) + if self.settings['nonlifting_body_interaction']: + self.map_forces(aero_kstep, structural_kstep, - force_coeff) + nl_body_kstep = nl_body_kstep, + unsteady_forces_coeff = force_coeff) + else: + self.map_forces(aero_kstep, + structural_kstep, + unsteady_forces_coeff = force_coeff) # relaxation relax_factor = self.relaxation_factor(k) @@ -738,7 +765,7 @@ def convergence(self, k, tstep, previous_tstep, all(x < self.settings['fsi_tolerance'] for x in (self.res, self.res_dqdt, res_forces)) - def map_forces(self, aero_kstep, structural_kstep, unsteady_forces_coeff=1.0): + def map_forces(self, aero_kstep, structural_kstep, nl_body_kstep = None, unsteady_forces_coeff=1.0): # set all forces to 0 structural_kstep.steady_applied_forces.fill(0.0) structural_kstep.unsteady_applied_forces.fill(0.0) @@ -776,6 +803,17 @@ def map_forces(self, aero_kstep, structural_kstep, unsteady_forces_coeff=1.0): # structural_kstep, # dynamic_struct_forces) + if self.settings['nonlifting_body_interaction']: + struct_forces += mapping.aero2struct_force_mapping( + nl_body_kstep.forces, + self.data.nonlifting_body.struct2aero_mapping, + nl_body_kstep.zeta, + structural_kstep.pos, + structural_kstep.psi, + self.data.structure.node_master_elem, + self.data.structure.connectivities, + structural_kstep.cag(), + self.data.nonlifting_body.data_dict) # prescribed forces + aero forces structural_kstep.steady_applied_forces = ( (struct_forces + self.data.structure.ini_info.steady_applied_forces). From f99e7cfc863c6198783bcf9ea0a43d29fa57f4f7 Mon Sep 17 00:00:00 2001 From: sduess Date: Thu, 6 May 2021 21:17:07 +0100 Subject: [PATCH 053/232] add: new options to staticuvlm for nonlifting effects --- sharpy/solvers/staticuvlm.py | 33 +++++++++++++++++++++++++-------- 1 file changed, 25 insertions(+), 8 deletions(-) diff --git a/sharpy/solvers/staticuvlm.py b/sharpy/solvers/staticuvlm.py index 5b1861a0c..281621e3c 100644 --- a/sharpy/solvers/staticuvlm.py +++ b/sharpy/solvers/staticuvlm.py @@ -50,6 +50,9 @@ class StaticUvlm(BaseSolver): settings_default['nonlifting_body_interactions'] = False settings_description['nonlifting_body_interactions'] = 'Consider nonlifting body interactions' + settings_types['only_nonlifting'] = 'bool' + settings_default['only_nonlifting'] = False + settings_description['only_nonlifting'] = 'Consider only nonlifting bodies' settings_types['num_cores'] = 'int' settings_default['num_cores'] = 0 settings_description['num_cores'] = 'Number of cores to use in the VLM lib' @@ -151,16 +154,30 @@ def run(self): 'gamma_star': aero_tstep.gamma_star, 'dist_to_orig': aero_tstep.dist_to_orig}) - # check if nonlifting body interactions have to be considered - if self.settings['nonlifting_body_interactions']: - # generate uext + # check if nonlifting body interactions have to be considered + if not self.settings['nonlifting_body_interactions']: self.velocity_generator.generate({'zeta': self.data.nonlifting_body.timestep_info[self.data.ts].zeta, - 'override': True, - 'for_pos': self.data.structure.timestep_info[self.data.ts].for_pos[0:3]}, - self.data.nonlifting_body.timestep_info[self.data.ts].u_ext) + 'override': True, + 'for_pos': self.data.structure.timestep_info[self.data.ts].for_pos[0:3]}, + self.data.nonlifting_body.timestep_info[self.data.ts].u_ext) uvlmlib.vlm_solver_nonlifting_body(self.data.nonlifting_body.timestep_info[self.data.ts], - self.settings) - else: + self.settings) + elif self.settings['nonlifting_body_interactions']: + # generate uext + self.velocity_generator.generate({'zeta': self.data.nonlifting_body.timestep_info[self.data.ts].zeta, + 'override': True, + 'for_pos': self.data.structure.timestep_info[self.data.ts].for_pos[0:3]}, + self.data.nonlifting_body.timestep_info[self.data.ts].u_ext) + # generate uext + self.velocity_generator.generate({'zeta': self.data.aero.timestep_info[self.data.ts].zeta, + 'override': True, + 'for_pos': self.data.structure.timestep_info[self.data.ts].for_pos[0:3]}, + self.data.aero.timestep_info[self.data.ts].u_ext) + # grid orientation + uvlmlib.vlm_solver_lifting_and_nonlifting_bodies(self.data.aero.timestep_info[self.data.ts], + self.data.nonlifting_body.timestep_info[self.data.ts], + self.settings) + else: # generate uext self.velocity_generator.generate({'zeta': self.data.aero.timestep_info[self.data.ts].zeta, 'override': True, From cccab29d9cc74910dbfb1967cf2e496330cb7e94 Mon Sep 17 00:00:00 2001 From: sduess Date: Thu, 6 May 2021 21:17:45 +0100 Subject: [PATCH 054/232] add[stepuvlm] nonlifting body effects --- sharpy/solvers/stepuvlm.py | 62 +++++++++++++++++++++++++++++++++++--- 1 file changed, 57 insertions(+), 5 deletions(-) diff --git a/sharpy/solvers/stepuvlm.py b/sharpy/solvers/stepuvlm.py index 566cc8325..5ee463f4a 100644 --- a/sharpy/solvers/stepuvlm.py +++ b/sharpy/solvers/stepuvlm.py @@ -132,6 +132,18 @@ class StepUvlm(BaseSolver): settings_types['quasi_steady'] = 'bool' settings_default['quasi_steady'] = False settings_description['quasi_steady'] = 'Use quasi-steady approximation in UVLM' + + settings_types['nonlifting_body_interactions'] = 'bool' + settings_default['nonlifting_body_interactions'] = True #False + settings_description['nonlifting_body_interactions'] = 'Consider nonlifting body interactions' + + settings_types['only_nonlifting'] = 'bool' + settings_default['only_nonlifting'] = False + settings_description['only_nonlifting'] = 'Consider only nonlifting bodies' + + settings_types['centre_rot_g'] = 'list(float)' + settings_default['centre_rot_g'] = [0., 0., 0.] + settings_description['centre_rot_g'] = 'Centre of rotation in G FoR around which ``rbm_vel_g`` is applied' settings_table = settings.SettingsTable() __doc__ += settings_table.generate(settings_types, settings_default, settings_description, settings_options) @@ -226,10 +238,35 @@ def run(self, 'for_pos': structure_tstep.for_pos, 'is_wake': True}, aero_tstep.u_ext_star) + if self.settings['nonlifting_body_interactions']: + if nl_body_tstep is None: + nl_body_tstep = self.data.nonlifting_body.timestep_info[-1] + # TODO: Extent velocity field generators + self.velocity_generator.generate({'zeta': nl_body_tstep.zeta, + 'override': True, + 'ts': self.data.ts, + 'dt': dt, + 't': t, + 'for_pos': structure_tstep.for_pos, + 'is_wake': False}, + nl_body_tstep.u_ext) + + print("convection scheme = ", self.settings['convection_scheme']) + uvlmlib.uvlm_solver_lifting_and_nonlifting(self.data.ts, + aero_tstep, + nl_body_tstep, + structure_tstep, + self.settings, + convect_wake=convect_wake, + dt=dt) + + # print("UNSTEADY UVLM finsihed in StepUVLM!") - uvlmlib.uvlm_solver(self.data.ts, - aero_tstep, - structure_tstep, + else: + + uvlmlib.uvlm_solver(self.data.ts, + aero_tstep, + structure_tstep, self.settings, convect_wake=convect_wake, dt=dt) @@ -256,24 +293,39 @@ def run(self, else: for i_surf in range(len(aero_tstep.gamma)): aero_tstep.gamma_dot[i_surf][:] = 0.0 - + # print("Step UVLM finsihed!") return self.data def add_step(self): self.data.aero.add_timestep() + if self.settings['nonlifting_body_interactions']: + self.data.nonlifting_body.add_timestep() def update_grid(self, beam): self.data.aero.generate_zeta(beam, self.data.aero.aero_settings, -1, beam_ts=-1) + if self.settings['nonlifting_body_interactions']: + self.data.nonlifting_body.generate_zeta(beam, + self.data.aero.aero_settings, + -1, + beam_ts=-1) - def update_custom_grid(self, structure_tstep, aero_tstep): + def update_custom_grid(self, structure_tstep, aero_tstep, nl_body_tstep = None): self.data.aero.generate_zeta_timestep_info(structure_tstep, aero_tstep, self.data.structure, self.data.aero.aero_settings, dt=self.settings['dt']) + if self.settings['nonlifting_body_interactions']: + if nl_body_tstep is None: + nl_body_tstep = self.data.nonlifting_body.timestep_info[-1] + self.data.nonlifting_body.generate_zeta_timestep_info(structure_tstep, + nl_body_tstep, + self.data.structure, + self.data.aero.aero_settings, + dt = self.settings['dt']) @staticmethod def filter_gamma_dot(tstep, history, filter_param): From fff49ea266d8bf72f44135812e185d78aa00999d Mon Sep 17 00:00:00 2001 From: sduess Date: Thu, 6 May 2021 21:20:46 +0100 Subject: [PATCH 055/232] fix: convergence criteria for NoStructural solver --- sharpy/solvers/dynamiccoupled.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/sharpy/solvers/dynamiccoupled.py b/sharpy/solvers/dynamiccoupled.py index f9d897a0d..57c79c2b3 100644 --- a/sharpy/solvers/dynamiccoupled.py +++ b/sharpy/solvers/dynamiccoupled.py @@ -736,7 +736,9 @@ def convergence(self, k, tstep, previous_tstep, return False # Check the special case of no aero and no runtime generators - if aero_solver.lower() == "noaero" and not with_runtime_generators: + if (aero_solver.lower() == "noaero"\ + or struct_solver.lower() == "nostructural")\ + and not with_runtime_generators: return True # relative residuals From ae4fd3595488e44b986c68c3a893d1bee30ee087 Mon Sep 17 00:00:00 2001 From: sduess Date: Mon, 10 May 2021 07:29:57 +0100 Subject: [PATCH 056/232] update: submodule UVLM to latest dev fuselage changes --- lib/UVLM | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/UVLM b/lib/UVLM index 53f5a2e11..80feb4948 160000 --- a/lib/UVLM +++ b/lib/UVLM @@ -1 +1 @@ -Subproject commit 53f5a2e117bc99d0a94d31f242c4d573538a8bb7 +Subproject commit 80feb4948f2445183377537acc5ce0e51822f3cd From 92fcf6e2dcac8e07302878098260f5875f4c3c70 Mon Sep 17 00:00:00 2001 From: sduess Date: Wed, 7 Jul 2021 12:04:27 +0100 Subject: [PATCH 057/232] fix: change datatype of phantom flag - allows to use the flag for more surfaces with different sizes --- sharpy/aero/models/aerogrid.py | 4 +- sharpy/utils/datastructures.py | 74 +++++++++++++++++++--------------- 2 files changed, 43 insertions(+), 35 deletions(-) diff --git a/sharpy/aero/models/aerogrid.py b/sharpy/aero/models/aerogrid.py index c1f814135..aa4b78ff7 100644 --- a/sharpy/aero/models/aerogrid.py +++ b/sharpy/aero/models/aerogrid.py @@ -305,9 +305,9 @@ def generate_phantom_panels_at_junction(self, aero_tstep): if i_surf in list_surfaces: idx_local_node = list_surfaces.index(i_surf) # assume that there is only one junction per surface yet - aero_tstep.flag_zeta_phantom[list_local_nodes[idx_local_node], i_surf] = 1 - + aero_tstep.flag_zeta_phantom[i_surf][list_local_nodes[idx_local_node], 0] = 1 + print(aero_tstep.flag_zeta_phantom) @staticmethod def compute_gamma_dot(dt, tstep, previous_tsteps): diff --git a/sharpy/utils/datastructures.py b/sharpy/utils/datastructures.py index 2d7710651..db6db9cfc 100644 --- a/sharpy/utils/datastructures.py +++ b/sharpy/utils/datastructures.py @@ -516,14 +516,16 @@ def __init__(self, dimensions, dimensions_star): for i_surf in range(self.n_surf): self.wake_conv_vel.append(np.zeros((dimensions_star[i_surf, 0], dimensions_star[i_surf, 1]), - dtype=ct.c_double)) - - # Junction handling - self.flag_zeta_phantom = np.zeros((dimensions[i_surf, 1] + 1, self.n_surf), - dtype=ct.c_int) - self.control_surface_deflection = np.array([]) - - def copy(self): + dtype=ct.c_double)) + + # Junction handling + self.flag_zeta_phantom = [] + for i_surf in range(self.n_surf): + self.flag_zeta_phantom.append(np.zeros((dimensions[i_surf, 1], 1), + dtype=ct.c_int)) + self.control_surface_deflection = np.array([]) + + def copy(self): return self.create_placeholder(AeroTimeStepInfo(self.dimensions, self.dimensions_star)) def create_placeholder(self, copied): @@ -552,14 +554,16 @@ def create_placeholder(self, copied): for i_surf in range(copied.n_surf): copied.wake_conv_vel[i_surf] = self.wake_conv_vel[i_surf].astype(dtype=ct.c_double, copy=True, order='C') - copied.control_surface_deflection = self.control_surface_deflection.astype(dtype=ct.c_double, copy=True) - - # phantom panel flags - copied.flag_zeta_phantom = self.flag_zeta_phantom.astype(dtype=ct.c_int, copy=True, order='C') - - return copied - - def generate_ctypes_pointers(self): + copied.control_surface_deflection = self.control_surface_deflection.astype(dtype=ct.c_double, copy=True) + + # phantom panel flags + + for i_surf in range(copied.n_surf): + copied.flag_zeta_phantom[i_surf] = self.flag_zeta_phantom[i_surf] .astype(dtype=ct.c_int, copy=True, order='C') + + return copied + + def generate_ctypes_pointers(self): from sharpy.utils.constants import NDIM n_surf = len(self.dimensions) super().generate_ctypes_pointers() @@ -591,16 +595,19 @@ def generate_ctypes_pointers(self): for i_surf in range(self.n_surf): self.ct_dist_to_orig_list.append(self.dist_to_orig[i_surf][:, :].reshape(-1)) - self.ct_wake_conv_vel_list = [] - for i_surf in range(self.n_surf): - self.ct_wake_conv_vel_list.append(self.wake_conv_vel[i_surf][:, :].reshape(-1)) - self.ct_flag_zeta_phantom_list = self.flag_zeta_phantom[:].reshape(-1) - - - - self.ct_p_dimensions_star = ((ct.POINTER(ct.c_uint)*n_surf) - (* np.ctypeslib.as_ctypes(self.ct_dimensions_star))) - self.ct_p_zeta_star = ((ct.POINTER(ct.c_double)*len(self.ct_zeta_star_list)) + self.ct_wake_conv_vel_list = [] + for i_surf in range(self.n_surf): + self.ct_wake_conv_vel_list.append(self.wake_conv_vel[i_surf][:, :].reshape(-1)) + + self.ct_flag_zeta_phantom_list = [] + for i_surf in range(self.n_surf): + self.ct_flag_zeta_phantom_list.append(self.flag_zeta_phantom[i_surf][:, :].reshape(-1)) + + + + self.ct_p_dimensions_star = ((ct.POINTER(ct.c_uint)*n_surf) + (* np.ctypeslib.as_ctypes(self.ct_dimensions_star))) + self.ct_p_zeta_star = ((ct.POINTER(ct.c_double)*len(self.ct_zeta_star_list)) (* [np.ctypeslib.as_ctypes(array) for array in self.ct_zeta_star_list])) self.ct_p_u_ext_star = ((ct.POINTER(ct.c_double)*len(self.ct_u_ext_star_list)) (* [np.ctypeslib.as_ctypes(array) for array in self.ct_u_ext_star_list])) @@ -611,13 +618,14 @@ def generate_ctypes_pointers(self): self.ct_p_gamma_star = ((ct.POINTER(ct.c_double)*len(self.ct_gamma_star_list)) (* [np.ctypeslib.as_ctypes(array) for array in self.ct_gamma_star_list])) self.ct_p_dist_to_orig = ((ct.POINTER(ct.c_double)*len(self.ct_dist_to_orig_list)) - (* [np.ctypeslib.as_ctypes(array) for array in self.ct_dist_to_orig_list])) - self.ct_p_wake_conv_vel = ((ct.POINTER(ct.c_double)*len(self.ct_wake_conv_vel_list)) - (* [np.ctypeslib.as_ctypes(array) for array in self.ct_wake_conv_vel_list])) - self.ct_p_flag_zeta_phantom = np.ctypeslib.as_ctypes(self.ct_flag_zeta_phantom_list) - - - def remove_ctypes_pointers(self): + (* [np.ctypeslib.as_ctypes(array) for array in self.ct_dist_to_orig_list])) + self.ct_p_wake_conv_vel = ((ct.POINTER(ct.c_double)*len(self.ct_wake_conv_vel_list)) + (* [np.ctypeslib.as_ctypes(array) for array in self.ct_wake_conv_vel_list])) + self.ct_p_flag_zeta_phantom = ((ct.POINTER(ct.c_int)*len(self.ct_flag_zeta_phantom_list)) + (* [np.ctypeslib.as_ctypes(array) for array in self.ct_flag_zeta_phantom_list])) + + + def remove_ctypes_pointers(self): super().remove_ctypes_pointers() try: del self.ct_p_dimensions_star From bae939d2b22c845a7b45d549a55a8126479a15c4 Mon Sep 17 00:00:00 2001 From: sduess Date: Wed, 14 Jul 2021 12:11:56 +0100 Subject: [PATCH 058/232] fix: allow possibility of multiple phantom surfaces - junction boundary condition contains now the surface id of the partner junction surface - allows the use of multiple phantom surfaces --- sharpy/aero/models/aerogrid.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/sharpy/aero/models/aerogrid.py b/sharpy/aero/models/aerogrid.py index aa4b78ff7..d8a178bd3 100644 --- a/sharpy/aero/models/aerogrid.py +++ b/sharpy/aero/models/aerogrid.py @@ -294,20 +294,21 @@ def generate_zeta_timestep_info(self, structure_tstep, aero_tstep, beam, setting def generate_phantom_panels_at_junction(self, aero_tstep): # To-Do: Extent for general case - list_global_node_junction = np.where(self.data_dict["junction_boundary_condition"] == 1)[0] + list_global_node_junction = (np.where(self.data_dict["junction_boundary_condition"] > 0)[0]).tolist() list_local_nodes = [] list_surfaces = [] - for i_global_node in list_global_node_junction.tolist(): + for i_global_node in list_global_node_junction: for i in range(len(self.struct2aero_mapping[i_global_node])): list_local_nodes.append(self.struct2aero_mapping[i_global_node][i]['i_n']) list_surfaces.append(self.struct2aero_mapping[i_global_node][i]['i_surf']) + for i_surf in range(self.n_surf): if i_surf in list_surfaces: - idx_local_node = list_surfaces.index(i_surf) + idx = list_surfaces.index(i_surf) # assume that there is only one junction per surface yet - aero_tstep.flag_zeta_phantom[i_surf][list_local_nodes[idx_local_node], 0] = 1 + aero_tstep.flag_zeta_phantom[i_surf][list_local_nodes[idx], 0] = \ + self.data_dict["junction_boundary_condition"][list_global_node_junction[idx]] - print(aero_tstep.flag_zeta_phantom) @staticmethod def compute_gamma_dot(dt, tstep, previous_tsteps): From e0052386bd7de08f52d3507ea2b35fcab3d1dece Mon Sep 17 00:00:00 2001 From: sduess Date: Mon, 9 Aug 2021 16:33:34 +0100 Subject: [PATCH 059/232] fix [uvlmlib]: Correct options for UVLM input --- sharpy/aero/utils/uvlmlib.py | 32 ++++++++------------------------ 1 file changed, 8 insertions(+), 24 deletions(-) diff --git a/sharpy/aero/utils/uvlmlib.py b/sharpy/aero/utils/uvlmlib.py index 4d64f1fde..fcf227087 100644 --- a/sharpy/aero/utils/uvlmlib.py +++ b/sharpy/aero/utils/uvlmlib.py @@ -89,7 +89,6 @@ def __init__(self): self.vortex_radius_wake_ind = ct.c_double(vortex_radius_def) self.centre_rot_g = np.ctypeslib.as_ctypes(np.zeros((3))) self.rbm_vel_g = np.ctypeslib.as_ctypes(np.zeros((6))) - print(self.rbm_vel_g) def set_options(self, options, n_surfaces = 0, n_surfaces_nonlifting = 0, rbm_vel_g = np.zeros(6)): @@ -111,9 +110,7 @@ def set_options(self, options, n_surfaces = 0, n_surfaces_nonlifting = 0, rbm_ve self.only_nonlifting = ct.c_bool(options["only_nonlifting"]) self.only_lifting = ct.c_bool(not options["nonlifting_body_interactions"]) - print("Centre rot g = ", options["centre_rot_g"]) - rbm_vel_g = [1, 2, 5] - print("RBM = ", rbm_vel_g) + for i in range(len(options["centre_rot_g"])): self.centre_rot_g[i] = ct.c_double(options["centre_rot_g"][i]) for i in range(len(rbm_vel_g)): @@ -135,9 +132,7 @@ class UVMopts(ct.Structure): ("iterative_solver", ct.c_bool), ("iterative_tol", ct.c_double), ("iterative_precond", ct.c_bool), - ("convect_wake", ct.c_bool), - ("only_lifting", ct.c_bool), - ("only_nonlifting", ct.c_bool), + ("convect_wake", ct.c_bool), ("cfl1", ct.c_bool), ("vortex_radius", ct.c_double), ("vortex_radius_wake_ind", ct.c_double), @@ -147,7 +142,8 @@ class UVMopts(ct.Structure): ("yaw_slerp", ct.c_double), ("quasi_steady", ct.c_bool), ("centre_rot_g", ct.c_double * 3), - ("rbm_vel_g", ct.c_double * 6)] + ("rbm_vel_g", ct.c_double * 6), + ("only_lifting", ct.c_bool)] def __init__(self): ct.Structure.__init__(self) @@ -169,6 +165,7 @@ def __init__(self): self.quasi_steady = ct.c_bool(False) self.centre_rot_g = np.ctypeslib.as_ctypes(np.zeros((3))) self.rbm_vel_g = np.ctypeslib.as_ctypes(np.zeros((6))) + self.only_lifting = ct.c_bool(True) def set_options(self, options, @@ -184,6 +181,7 @@ def set_options(self, self.dt = ct.c_double(dt) self.NumCores = ct.c_uint(options["num_cores"]) self.NumSurfaces = ct.c_uint(n_surfaces) + self.NumSurfacesNonlifting = ct.c_uint(n_surfaces_nonlifting) self.ImageMethod = ct.c_bool(False) self.convection_scheme = ct.c_uint(options["convection_scheme"]) self.iterative_solver = ct.c_bool(options['iterative_solver']) @@ -199,10 +197,8 @@ def set_options(self, self.yaw_slerp = ct.c_double(options["yaw_slerp"]) self.quasi_steady = ct.c_bool(options['quasi_steady']) - self.only_nonlifting = ct.c_bool(options["only_nonlifting"]) self.only_lifting = ct.c_bool(not options["nonlifting_body_interactions"]) - print("Centre rot g = ", options["centre_rot_g"]) - print("RBM = ", rbm_vel_g) + for i in range(len(options["centre_rot_g"])): self.centre_rot_g[i] = ct.c_double(options["centre_rot_g"][i]) for i in range(len(rbm_vel_g)): @@ -274,7 +270,6 @@ def vlm_solver_nonlifting_body(ts_info, options): flightconditions.uinf_direction = np.ctypeslib.as_ctypes(ts_info.u_ext[0][:, 0, 0]/flightconditions.uinf) ts_info.generate_ctypes_pointers() - run_VLM_nonlifting(ct.byref(vmopts), ct.byref(flightconditions), ts_info.ct_p_dimensions, @@ -296,7 +291,6 @@ def vlm_solver_lifting_and_nonlifting_bodies(ts_info_lifting, ts_info_nonlifting flightconditions.uinf = np.ctypeslib.as_ctypes(np.linalg.norm(ts_info_lifting.u_ext[0][:, 0, 0])) flightconditions.uinf_direction = np.ctypeslib.as_ctypes(ts_info_lifting.u_ext[0][:, 0, 0]/flightconditions.uinf) - p_rbm_vel_g = options['rbm_vel_g'].ctypes.data_as(ct.POINTER(ct.c_double)) ts_info_lifting.generate_ctypes_pointers() ts_info_nonlifting.generate_ctypes_pointers() run_VLM_lifting_and_nonlifting(ct.byref(vmopts), @@ -311,7 +305,6 @@ def vlm_solver_lifting_and_nonlifting_bodies(ts_info_lifting, ts_info_nonlifting ts_info_lifting.ct_p_gamma_star, ts_info_lifting.ct_p_forces, ts_info_lifting.ct_p_flag_zeta_phantom, - p_rbm_vel_g, ts_info_nonlifting.ct_p_dimensions, ts_info_nonlifting.ct_p_zeta, ts_info_nonlifting.ct_p_u_ext, @@ -378,9 +371,6 @@ def uvlm_solver_lifting_and_nonlifting(i_iter, ts_info, ts_info_nonlifting, stru rbm_vel = struct_ts_info.for_vel.copy() rbm_vel[0:3] = np.dot(struct_ts_info.cga(), rbm_vel[0:3]) rbm_vel[3:6] = np.dot(struct_ts_info.cga(), rbm_vel[3:6]) - p_rbm_vel = rbm_vel.ctypes.data_as(ct.POINTER(ct.c_double)) - p_centre_rot = options['centre_rot'].ctypes.data_as(ct.POINTER(ct.c_double)) - uvmopts = UVMopts() uvmopts.set_options(options, @@ -393,7 +383,6 @@ def uvlm_solver_lifting_and_nonlifting(i_iter, ts_info, ts_info_nonlifting, stru run_UVLM = UvlmLib.run_UVLM_lifting_and_nonlifting run_UVLM.restype = None - flightconditions = FlightConditions() flightconditions.rho = options['rho'] flightconditions.uinf = np.ctypeslib.as_ctypes(np.linalg.norm(ts_info.u_ext[0][:, 0, 0])) @@ -401,13 +390,8 @@ def uvlm_solver_lifting_and_nonlifting(i_iter, ts_info, ts_info_nonlifting, stru flightconditions.uinf_direction = np.ctypeslib.as_ctypes(ts_info.u_ext[0][:, 0, 0]/flightconditions.uinf) # flightconditions.uinf_direction = np.ctypeslib.as_ctypes(direction) - rbm_vel = struct_ts_info.for_vel.copy() - rbm_vel[0:3] = np.dot(struct_ts_info.cga(), rbm_vel[0:3]) - rbm_vel[3:6] = np.dot(struct_ts_info.cga(), rbm_vel[3:6]) - p_rbm_vel = rbm_vel.ctypes.data_as(ct.POINTER(ct.c_double)) - p_centre_rot = options['centre_rot'].ctypes.data_as(ct.POINTER(ct.c_double)) - i = ct.c_uint(i_iter) + ts_info.generate_ctypes_pointers() ts_info_nonlifting.generate_ctypes_pointers() # previous_ts_info.generate_ctypes_pointers() From 302c34445a58d00c23c12d28564db45e7153cabf Mon Sep 17 00:00:00 2001 From: sduess Date: Wed, 10 Nov 2021 13:27:43 +0000 Subject: [PATCH 060/232] refactor: simplify junction boundary conditions - for each lifting surface the partner junction surface is stored as an int in a matrix (1, n_surf) - no junction --> -1 --- sharpy/aero/models/aerogrid.py | 25 +++++++++++-------------- sharpy/utils/datastructures.py | 18 +++++------------- 2 files changed, 16 insertions(+), 27 deletions(-) diff --git a/sharpy/aero/models/aerogrid.py b/sharpy/aero/models/aerogrid.py index d8a178bd3..de9d69a48 100644 --- a/sharpy/aero/models/aerogrid.py +++ b/sharpy/aero/models/aerogrid.py @@ -288,26 +288,23 @@ def generate_zeta_timestep_info(self, structure_tstep, aero_tstep, beam, setting orientation_in=self.aero_settings['freestream_dir'], calculate_zeta_dot=True)) # set junction boundary conditions for later phantom cell creation in UVLM - if "junction_boundary_condition" in self.data_dict and sum(self.data_dict["junction_boundary_condition"])>0: + if "junction_boundary_condition" in self.data_dict: + if np.any(self.data_dict["junction_boundary_condition"] >= 0): self.generate_phantom_panels_at_junction(aero_tstep) def generate_phantom_panels_at_junction(self, aero_tstep): # To-Do: Extent for general case - list_global_node_junction = (np.where(self.data_dict["junction_boundary_condition"] > 0)[0]).tolist() - list_local_nodes = [] - list_surfaces = [] - for i_global_node in list_global_node_junction: - for i in range(len(self.struct2aero_mapping[i_global_node])): - list_local_nodes.append(self.struct2aero_mapping[i_global_node][i]['i_n']) - list_surfaces.append(self.struct2aero_mapping[i_global_node][i]['i_surf']) - + # list_global_node_junction = np.where(self.data_dict["junction_boundary_condition"] >= 0)[0] + # list_local_nodes = [] + # list_surfaces = [] + # list_partner_surface = [] + # for i_global_node in list_global_node_junction.tolist(): + # list_partner_surface.append(self.data_dict["junction_boundary_condition"][i_global_node]) + # for i in range(len(self.struct2aero_mapping[i_global_node])): for i_surf in range(self.n_surf): - if i_surf in list_surfaces: - idx = list_surfaces.index(i_surf) - # assume that there is only one junction per surface yet - aero_tstep.flag_zeta_phantom[i_surf][list_local_nodes[idx], 0] = \ - self.data_dict["junction_boundary_condition"][list_global_node_junction[idx]] + aero_tstep.flag_zeta_phantom[0, i_surf] = self.data_dict["junction_boundary_condition"][0,i_surf] + @staticmethod diff --git a/sharpy/utils/datastructures.py b/sharpy/utils/datastructures.py index db6db9cfc..9348fcb07 100644 --- a/sharpy/utils/datastructures.py +++ b/sharpy/utils/datastructures.py @@ -519,10 +519,8 @@ def __init__(self, dimensions, dimensions_star): dtype=ct.c_double)) # Junction handling - self.flag_zeta_phantom = [] - for i_surf in range(self.n_surf): - self.flag_zeta_phantom.append(np.zeros((dimensions[i_surf, 1], 1), - dtype=ct.c_int)) + self.flag_zeta_phantom = np.zeros((1, self.n_surf), + dtype=ct.c_int) self.control_surface_deflection = np.array([]) def copy(self): @@ -557,9 +555,7 @@ def create_placeholder(self, copied): copied.control_surface_deflection = self.control_surface_deflection.astype(dtype=ct.c_double, copy=True) # phantom panel flags - - for i_surf in range(copied.n_surf): - copied.flag_zeta_phantom[i_surf] = self.flag_zeta_phantom[i_surf] .astype(dtype=ct.c_int, copy=True, order='C') + copied.flag_zeta_phantom = self.flag_zeta_phantom.astype(dtype=ct.c_int, copy=True, order='C') return copied @@ -598,10 +594,7 @@ def generate_ctypes_pointers(self): self.ct_wake_conv_vel_list = [] for i_surf in range(self.n_surf): self.ct_wake_conv_vel_list.append(self.wake_conv_vel[i_surf][:, :].reshape(-1)) - - self.ct_flag_zeta_phantom_list = [] - for i_surf in range(self.n_surf): - self.ct_flag_zeta_phantom_list.append(self.flag_zeta_phantom[i_surf][:, :].reshape(-1)) + self.ct_flag_zeta_phantom_list = self.flag_zeta_phantom[:].reshape(-1) @@ -621,8 +614,7 @@ def generate_ctypes_pointers(self): (* [np.ctypeslib.as_ctypes(array) for array in self.ct_dist_to_orig_list])) self.ct_p_wake_conv_vel = ((ct.POINTER(ct.c_double)*len(self.ct_wake_conv_vel_list)) (* [np.ctypeslib.as_ctypes(array) for array in self.ct_wake_conv_vel_list])) - self.ct_p_flag_zeta_phantom = ((ct.POINTER(ct.c_int)*len(self.ct_flag_zeta_phantom_list)) - (* [np.ctypeslib.as_ctypes(array) for array in self.ct_flag_zeta_phantom_list])) + self.ct_p_flag_zeta_phantom = np.ctypeslib.as_ctypes(self.ct_flag_zeta_phantom_list) def remove_ctypes_pointers(self): From 35979df62f854ecd9e1d356f58a744f8aa17070e Mon Sep 17 00:00:00 2001 From: sduess Date: Wed, 10 Nov 2021 13:29:45 +0000 Subject: [PATCH 061/232] update [uvlmlib]: adjust types and inputs to UVLM --- sharpy/aero/utils/uvlmlib.py | 38 +++++++++++++++++++++++++----------- 1 file changed, 27 insertions(+), 11 deletions(-) diff --git a/sharpy/aero/utils/uvlmlib.py b/sharpy/aero/utils/uvlmlib.py index fcf227087..5f2777d92 100644 --- a/sharpy/aero/utils/uvlmlib.py +++ b/sharpy/aero/utils/uvlmlib.py @@ -122,7 +122,9 @@ class UVMopts(ct.Structure): _fields_ = [("dt", ct.c_double), ("NumCores", ct.c_uint), ("NumSurfaces", ct.c_uint), - ("NumSurfacesNonlifting", ct.c_uint), + ("NumSurfacesNonlifting", ct.c_uint), + ("only_lifting", ct.c_bool), + ("only_nonlifting", ct.c_bool), # ("steady_n_rollup", ct.c_uint), # ("steady_rollup_tolerance", ct.c_double), # ("steady_rollup_aic_refresh", ct.c_uint), @@ -132,7 +134,7 @@ class UVMopts(ct.Structure): ("iterative_solver", ct.c_bool), ("iterative_tol", ct.c_double), ("iterative_precond", ct.c_bool), - ("convect_wake", ct.c_bool), + ("convect_wake", ct.c_bool), ("cfl1", ct.c_bool), ("vortex_radius", ct.c_double), ("vortex_radius_wake_ind", ct.c_double), @@ -142,15 +144,15 @@ class UVMopts(ct.Structure): ("yaw_slerp", ct.c_double), ("quasi_steady", ct.c_bool), ("centre_rot_g", ct.c_double * 3), - ("rbm_vel_g", ct.c_double * 6), - ("only_lifting", ct.c_bool)] + ("rbm_vel_g", ct.c_double * 6), + ("num_spanwise_panels_wo_induced_velocity", ct.c_uint)] def __init__(self): ct.Structure.__init__(self) self.dt = ct.c_double(0.01) self.NumCores = ct.c_uint(4) self.NumSurfaces = ct.c_uint(1) - self.NumSurfacesNonlifting = ct.c_uint(0) + self.NumSurfacesNonlifting = ct.c_uint(1) self.convection_scheme = ct.c_uint(2) # self.Mstar = ct.c_uint(10) self.ImageMethod = ct.c_bool(False) @@ -165,7 +167,7 @@ def __init__(self): self.quasi_steady = ct.c_bool(False) self.centre_rot_g = np.ctypeslib.as_ctypes(np.zeros((3))) self.rbm_vel_g = np.ctypeslib.as_ctypes(np.zeros((6))) - self.only_lifting = ct.c_bool(True) + self.num_spanwise_panels_wo_induced_velocity = ct.c_uint(0) def set_options(self, options, @@ -174,7 +176,8 @@ def set_options(self, dt = None, convect_wake = False, rbm_vel_g = np.zeros(6), - image_method = False): + image_method = False, + n_span_panels_wo_u_ind = 0): if dt is None: self.dt = ct.c_double(options["dt"]) else: @@ -197,7 +200,9 @@ def set_options(self, self.yaw_slerp = ct.c_double(options["yaw_slerp"]) self.quasi_steady = ct.c_bool(options['quasi_steady']) + self.only_nonlifting = ct.c_bool(options["only_nonlifting"]) self.only_lifting = ct.c_bool(not options["nonlifting_body_interactions"]) + self.num_spanwise_panels_wo_induced_velocity = n_span_panels_wo_u_ind for i in range(len(options["centre_rot_g"])): self.centre_rot_g[i] = ct.c_double(options["centre_rot_g"][i]) @@ -291,6 +296,7 @@ def vlm_solver_lifting_and_nonlifting_bodies(ts_info_lifting, ts_info_nonlifting flightconditions.uinf = np.ctypeslib.as_ctypes(np.linalg.norm(ts_info_lifting.u_ext[0][:, 0, 0])) flightconditions.uinf_direction = np.ctypeslib.as_ctypes(ts_info_lifting.u_ext[0][:, 0, 0]/flightconditions.uinf) + p_rbm_vel_g = options['rbm_vel_g'].ctypes.data_as(ct.POINTER(ct.c_double)) ts_info_lifting.generate_ctypes_pointers() ts_info_nonlifting.generate_ctypes_pointers() run_VLM_lifting_and_nonlifting(ct.byref(vmopts), @@ -305,6 +311,7 @@ def vlm_solver_lifting_and_nonlifting_bodies(ts_info_lifting, ts_info_nonlifting ts_info_lifting.ct_p_gamma_star, ts_info_lifting.ct_p_forces, ts_info_lifting.ct_p_flag_zeta_phantom, + p_rbm_vel_g, ts_info_nonlifting.ct_p_dimensions, ts_info_nonlifting.ct_p_zeta, ts_info_nonlifting.ct_p_u_ext, @@ -333,7 +340,8 @@ def uvlm_solver(i_iter, ts_info, struct_ts_info, options, convect_wake=True, dt= dt = dt, convect_wake = convect_wake, rbm_vel_g = rbm_vel, - image_method = False) + image_method = False, + n_span_panels_wo_u_ind=0) flightconditions = FlightConditions() @@ -371,6 +379,8 @@ def uvlm_solver_lifting_and_nonlifting(i_iter, ts_info, ts_info_nonlifting, stru rbm_vel = struct_ts_info.for_vel.copy() rbm_vel[0:3] = np.dot(struct_ts_info.cga(), rbm_vel[0:3]) rbm_vel[3:6] = np.dot(struct_ts_info.cga(), rbm_vel[3:6]) + p_rbm_vel = rbm_vel.ctypes.data_as(ct.POINTER(ct.c_double)) + p_centre_rot = options['centre_rot'].ctypes.data_as(ct.POINTER(ct.c_double)) uvmopts = UVMopts() uvmopts.set_options(options, @@ -379,7 +389,9 @@ def uvlm_solver_lifting_and_nonlifting(i_iter, ts_info, ts_info_nonlifting, stru dt = dt, convect_wake = convect_wake, rbm_vel_g = rbm_vel, - image_method = False) + image_method = False, + n_span_panels_wo_u_ind=4) + uvmopts.only_lifting = ct.c_bool(False) run_UVLM = UvlmLib.run_UVLM_lifting_and_nonlifting run_UVLM.restype = None @@ -390,8 +402,13 @@ def uvlm_solver_lifting_and_nonlifting(i_iter, ts_info, ts_info_nonlifting, stru flightconditions.uinf_direction = np.ctypeslib.as_ctypes(ts_info.u_ext[0][:, 0, 0]/flightconditions.uinf) # flightconditions.uinf_direction = np.ctypeslib.as_ctypes(direction) - i = ct.c_uint(i_iter) + rbm_vel = struct_ts_info.for_vel.copy() + rbm_vel[0:3] = np.dot(struct_ts_info.cga(), rbm_vel[0:3]) + rbm_vel[3:6] = np.dot(struct_ts_info.cga(), rbm_vel[3:6]) + p_rbm_vel = rbm_vel.ctypes.data_as(ct.POINTER(ct.c_double)) + p_centre_rot = options['centre_rot'].ctypes.data_as(ct.POINTER(ct.c_double)) + i = ct.c_uint(i_iter) ts_info.generate_ctypes_pointers() ts_info_nonlifting.generate_ctypes_pointers() # previous_ts_info.generate_ctypes_pointers() @@ -420,7 +437,6 @@ def uvlm_solver_lifting_and_nonlifting(i_iter, ts_info, ts_info_nonlifting, stru ts_info.remove_ctypes_pointers() ts_info_nonlifting.remove_ctypes_pointers() - # previous_ts_info.remove_ctypes_pointers() def uvlm_calculate_unsteady_forces(ts_info, struct_ts_info, From f08e7b02240b28d5ce8937cec7c5104569b6a4e8 Mon Sep 17 00:00:00 2001 From: sduess Date: Wed, 10 Nov 2021 13:31:55 +0000 Subject: [PATCH 062/232] add [aerogridplot]: plot nonlifting surfaces --- sharpy/postproc/aerogridplot.py | 120 ++++++++++++++++++++++++++++++++ 1 file changed, 120 insertions(+) diff --git a/sharpy/postproc/aerogridplot.py b/sharpy/postproc/aerogridplot.py index a22eedb87..cf99d1ba2 100644 --- a/sharpy/postproc/aerogridplot.py +++ b/sharpy/postproc/aerogridplot.py @@ -65,6 +65,14 @@ class AerogridPlot(BaseSolver): settings_default['include_incidence_angle'] = False settings_description['include_incidence_angle'] = 'Include panel incidence angle' + settings_types['plot_nonlifting_surfaces'] = 'bool' + settings_default['plot_nonlifting_surfaces'] = False + settings_description['plot_nonlifting_surfaces'] = 'Plot nonlifting surfaces' + + settings_types['plot_lifting_surfaces'] = 'bool' + settings_default['plot_lifting_surfaces'] = False + settings_description['plot_lifting_surfaces'] = 'Plot nonlifting surfaces' + settings_types['num_cores'] = 'int' settings_default['num_cores'] = 1 settings_description['num_cores'] = 'Number of cores used to compute velocities/angles' @@ -83,6 +91,7 @@ def __init__(self): self.folder = None self.body_filename = '' self.wake_filename = '' + self.nonlifting_filename = '' self.ts_max = 0 self.caller = None @@ -106,6 +115,10 @@ def initialise(self, data, custom_settings=None, caller=None): self.settings['name_prefix'] + 'wake_' + self.data.settings['SHARPy']['case']) + self.nonlifting_filename = (self.folder + + self.settings['name_prefix'] + + 'nonlifting_' + + self.data.settings['SHARPy']['case']) self.caller = caller def run(self, online=False): @@ -115,6 +128,9 @@ def run(self, online=False): if self.data.structure.timestep_info[self.ts] is not None: self.plot_body() self.plot_wake() + if self.settings['plot_nonlifting_surfaces']: + print("Plot Nonlifting Surface") + self.plot_nonlifting_surfaces() cout.cout_wrap('...Finished', 1) else: aero_tsteps = len(self.data.aero.timestep_info) - 1 @@ -122,6 +138,9 @@ def run(self, online=False): self.ts = np.max((aero_tsteps, struct_tsteps)) self.plot_body() self.plot_wake() + if self.settings['plot_nonlifting_surfaces']: + print("Plot Nonlifting Surface") + self.plot_nonlifting_surfaces() return self.data def plot_body(self): @@ -318,3 +337,104 @@ def plot_wake(self): ug.point_data.scalars = np.arange(0, coords.shape[0]) ug.point_data.scalars.name = 'n_id' write_data(ug, filename) + + def plot_nonlifting_surfaces(self): + nonlifting_tstep = self.data.nonlifting_body.timestep_info[self.ts] + struct_tstep = self.data.structure.timestep_info[self.ts] + + for i_surf in range(nonlifting_tstep.n_surf): + filename = (self.nonlifting_filename + + '_' + + '%02u_' % i_surf + + '%06u' % self.ts) + + dims = nonlifting_tstep.dimensions[i_surf, :] + point_data_dim = (dims[0]+1)*(dims[1]+1) # + (dims_star[0]+1)*(dims_star[1]+1) + panel_data_dim = (dims[0])*(dims[1]) # + (dims_star[0])*(dims_star[1]) + + coords = np.zeros((point_data_dim, 3)) + conn = [] + panel_id = np.zeros((panel_data_dim,), dtype=int) + panel_surf_id = np.zeros((panel_data_dim,), dtype=int) + panel_sigma = np.zeros((panel_data_dim,)) + normal = np.zeros((panel_data_dim, 3)) + point_struct_id = np.zeros((point_data_dim,), dtype=int) + point_cf = np.zeros((point_data_dim, 3)) + u_inf = np.zeros((point_data_dim, 3)) + counter = -1 + + # coordinates of corners + for i_n in range(dims[1]+1): + for i_m in range(dims[0]+1): + counter += 1 + coords[counter, :] = nonlifting_tstep.zeta[i_surf][:, i_m, i_n] + # TODO: include those for nonlifting body (are they different for nonlifting coordinates?) + if self.settings['include_rbm']: + coords[counter, :] += struct_tstep.for_pos[0:3] + if self.settings['include_forward_motion']: + coords[counter, 0] -= self.settings['dt']*self.ts*self.settings['u_inf'] + counter = -1 + node_counter = -1 + for i_n in range(dims[1] + 1): + global_counter = self.data.nonlifting_body.aero2struct_mapping[i_surf][i_n] + for i_m in range(dims[0] + 1): + node_counter += 1 + # point data + point_struct_id[node_counter] = global_counter + point_cf[node_counter, :] = nonlifting_tstep.forces[i_surf][0:3, i_m, i_n] + try: + u_inf[node_counter, :] = nonlifting_tstep.u_ext[i_surf][0:3, i_m, i_n] + except AttributeError: + pass + if i_n < dims[1] and i_m < dims[0]: + counter += 1 + else: + continue + + conn.append([node_counter + 0, + node_counter + 1, + node_counter + dims[0]+2, + node_counter + dims[0]+1]) + # cell data + normal[counter, :] = nonlifting_tstep.normals[i_surf][:, i_m, i_n] + panel_id[counter] = counter + panel_surf_id[counter] = i_surf + panel_sigma[counter] = nonlifting_tstep.sigma[i_surf][i_m, i_n] + + ug = tvtk.UnstructuredGrid(points=coords) + ug.set_cells(tvtk.Quad().cell_type, conn) + ug.cell_data.scalars = panel_id + ug.cell_data.scalars.name = 'panel_n_id' + ug.cell_data.add_array(panel_surf_id) + ug.cell_data.get_array(1).name = 'panel_surface_id' + ug.cell_data.add_array(panel_sigma) + ug.cell_data.get_array(2).name = 'panel_sigma' + ug.cell_data.vectors = normal + ug.cell_data.vectors.name = 'panel_normal' + ug.point_data.scalars = np.arange(0, coords.shape[0]) + ug.point_data.scalars.name = 'n_id' + ug.point_data.add_array(point_struct_id) + ug.point_data.get_array(1).name = 'point_struct_id' + ug.point_data.add_array(point_cf) + ug.point_data.get_array(2).name = 'point_steady_force' + ug.point_data.add_array(u_inf) + ug.point_data.get_array(3).name = 'u_inf' + write_data(ug, filename) + + def write_paraview_data(self, coords, conn, panel_id, list_cell_parameters, list_cell_names, list_point_parameters, list_point_names, filename): + ug = tvtk.UnstructuredGrid(points=coords) + + ug.set_cells(tvtk.Quad().cell_type, conn) + ug.cell_data.scalars = panel_id + ug.cell_data.scalars.name = 'panel_n_id' + for counter in range(len(list_cell_parameters)): + ug.cell_data.add_array(list_cell_parameters[counter]) + ug.cell_data.get_array(counter+1).name = list_cell_names[counter] + + ug.point_data.scalars = np.arange(0, coords.shape[0]) + ug.point_data.scalars.name = 'n_id' + for counter in range(len(list_point_parameters)): + ug.point_data.add_array(list_point_parameters[counter]) + ug.point_data.get_array(counter+1).name = list_point_names[counter] + + write_data(ug, filename) From 8374fcb4fa606086e00f577c1ae4f8e191eadd51 Mon Sep 17 00:00:00 2001 From: sduess Date: Wed, 10 Nov 2021 14:43:12 +0000 Subject: [PATCH 063/232] add [nonliftinggrid]: more general fuselage shape generation - Allows to generate the surface of bodies with changing elliptical cross sections --- sharpy/aero/models/nonlifting_body_grid.py | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/sharpy/aero/models/nonlifting_body_grid.py b/sharpy/aero/models/nonlifting_body_grid.py index 74d5455d1..2808d995d 100644 --- a/sharpy/aero/models/nonlifting_body_grid.py +++ b/sharpy/aero/models/nonlifting_body_grid.py @@ -52,11 +52,26 @@ def get_zeta_and_zeta_dot(self,i_surf, structure_tstep): cga_rotation_matrix = structure_tstep.cga() for node_counter, i_global_node in enumerate(self.aero2struct_mapping[i_surf]): - radius = self.data_dict["radius"][i_global_node] + # TODO: Adjust for ellipse input + if self.data_dict["shape"] == 'specific': + a_ellipse = self.data_dict["a_ellipse"][i_global_node] + b_ellipse = self.data_dict["b_ellipse"][i_global_node] + z_0 =self.data_dict["z_0_ellipse"][i_global_node] + if a_ellipse == 0. or b_ellipse == 0.: + radius = 0 + else: + radius = a_ellipse*b_ellipse/np.sqrt( + (b_ellipse*array_cos_phi)**2 + +(a_ellipse*array_sin_phi)**2) + else: + radius = self.data_dict["radius"][i_global_node] + z_0 = 0 + + # get nodes position in B frame - #matrix_nodes[0, :numb_radial_nodes, node_counter] = 0 matrix_nodes[1, :, node_counter] = radius*array_cos_phi - matrix_nodes[2, :, node_counter] = radius*array_sin_phi + matrix_nodes[2, :, node_counter] = radius*array_sin_phi + z_0 + # convert position from B to A frame i_elem, i_local_node = self.get_elment_and_local_node_id(i_surf, i_global_node) psi_node = structure_tstep.psi[i_elem, i_local_node,:] From 8bdab9ee455f535927ac617e143de3a5b48243b1 Mon Sep 17 00:00:00 2001 From: sduess Date: Wed, 10 Nov 2021 14:44:23 +0000 Subject: [PATCH 064/232] remove: old notes --- sharpy/aero/models/aerogrid.py | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/sharpy/aero/models/aerogrid.py b/sharpy/aero/models/aerogrid.py index de9d69a48..15ae16eec 100644 --- a/sharpy/aero/models/aerogrid.py +++ b/sharpy/aero/models/aerogrid.py @@ -294,14 +294,6 @@ def generate_zeta_timestep_info(self, structure_tstep, aero_tstep, beam, setting def generate_phantom_panels_at_junction(self, aero_tstep): - # To-Do: Extent for general case - # list_global_node_junction = np.where(self.data_dict["junction_boundary_condition"] >= 0)[0] - # list_local_nodes = [] - # list_surfaces = [] - # list_partner_surface = [] - # for i_global_node in list_global_node_junction.tolist(): - # list_partner_surface.append(self.data_dict["junction_boundary_condition"][i_global_node]) - # for i in range(len(self.struct2aero_mapping[i_global_node])): for i_surf in range(self.n_surf): aero_tstep.flag_zeta_phantom[0, i_surf] = self.data_dict["junction_boundary_condition"][0,i_surf] @@ -429,7 +421,7 @@ def generate_strip(node_info, airfoil_db, aligned_grid, orientation_in=np.array( # deflection velocity try: cs_velocity[:, i_M] += np.cross(np.array([-node_info['control_surface']['deflection_dot'], 0.0, 0.0]), - relative_coords) + relative_coords) except KeyError: pass From 005143a81907c5264c1f463f180c32d3a5a4bee6 Mon Sep 17 00:00:00 2001 From: sduess Date: Wed, 10 Nov 2021 14:46:41 +0000 Subject: [PATCH 065/232] fix [dynamiccoupled]: allow nonlifting bodies to be included --- sharpy/solvers/dynamiccoupled.py | 73 +++++++++++++++++--------------- 1 file changed, 38 insertions(+), 35 deletions(-) diff --git a/sharpy/solvers/dynamiccoupled.py b/sharpy/solvers/dynamiccoupled.py index 57c79c2b3..6718c0098 100644 --- a/sharpy/solvers/dynamiccoupled.py +++ b/sharpy/solvers/dynamiccoupled.py @@ -485,12 +485,14 @@ def time_loop(self, in_queue=None, out_queue=None, finish_event=None): self.set_of_variables.update_timestep(self.data, values) structural_kstep = self.data.structure.timestep_info[-1].copy() - aero_kstep = self.data.aero.timestep_info[-1].copy() - if self.settings['nonlifting_body_interaction']: - nl_body_kstep = self.data.nonlifting_body.timestep_info[-1].copy() - self.logger.debug('Time step {}'.format(self.data.ts)) - - # Add the controller here + aero_kstep = self.data.aero.timestep_info[-1].copy() + if self.settings['nonlifting_body_interaction']: + nl_body_kstep = self.data.nonlifting_body.timestep_info[-1].copy() + else: + nl_body_kstep = None + self.logger.debug('Time step {}'.format(self.data.ts)) + + # Add the controller here if self.with_controllers: state = {'structural': structural_kstep, 'aero': aero_kstep} @@ -536,20 +538,23 @@ def time_loop(self, in_queue=None, out_queue=None, finish_event=None): self.settings['fsi_substeps']): print_res = 0 if self.res == 0. else np.log10(self.res) print_res_dqdt = 0 if self.res_dqdt == 0. else np.log10(self.res_dqdt) - cout.cout_wrap(("The FSI solver did not converge!!! residuals: %f %f" % (print_res, print_res_dqdt))) - self.aero_solver.update_custom_grid( - structural_kstep, - aero_kstep) - break - - # generate new grid (already rotated) - aero_kstep = controlled_aero_kstep.copy() - self.aero_solver.update_custom_grid( - structural_kstep, - aero_kstep) - - # compute unsteady contribution - force_coeff = 0.0 + cout.cout_wrap(("The FSI solver did not converge!!! residuals: %f %f" % (print_res, print_res_dqdt))) + self.aero_solver.update_custom_grid( + structural_kstep, + aero_kstep, + nl_body_kstep) + break + + # generate new grid (already rotated) + aero_kstep = controlled_aero_kstep.copy() + + self.aero_solver.update_custom_grid( + structural_kstep, + aero_kstep, + nl_body_kstep) + + # compute unsteady contribution + force_coeff = 0.0 unsteady_contribution = False if self.settings['include_unsteady_force_contribution']: if self.data.ts > self.settings['steps_without_unsteady_force']: @@ -594,16 +599,11 @@ def time_loop(self, in_queue=None, out_queue=None, finish_event=None): previous_kstep.runtime_generated_forces = previous_runtime_generated_forces.astype(dtype=ct.c_double, order='F', copy=True) # move the aerodynamic surface according the the structural one - - if self.settings['nonlifting_body_interaction']: - self.aero_solver.update_custom_grid(structural_kstep, + self.aero_solver.update_custom_grid( + structural_kstep, aero_kstep, - nl_body_tstep = nl_body_kstep) - else: - self.aero_solver.update_custom_grid(structural_kstep, - aero_kstep, - nl_body_tstep = nl_body_kstep) - if self.settings['nonlifting_body_interaction']: + nl_body_kstep) + if self.settings['nonlifting_body_interaction']: self.map_forces(aero_kstep, structural_kstep, nl_body_kstep = nl_body_kstep, @@ -642,8 +642,8 @@ def time_loop(self, in_queue=None, out_queue=None, finish_event=None): coeff=coeff) self.data = self.structural_solver.run( - structural_step=structural_kstep, - dt=self.substep_dt) + structural_step=structural_kstep) #, + # dt=self.substep_dt) self.time_struc += time.perf_counter() - ini_time_struc @@ -655,16 +655,19 @@ def time_loop(self, in_queue=None, out_queue=None, finish_event=None): self.settings['aero_solver'].lower(), self.with_runtime_generators): # move the aerodynamic surface according to the structural one - self.aero_solver.update_custom_grid( - structural_kstep, - aero_kstep) + self.aero_solver.update_custom_grid(structural_kstep, + aero_kstep, + nl_body_tstep = nl_body_kstep) break # move the aerodynamic surface according the the structural one - self.aero_solver.update_custom_grid(structural_kstep, aero_kstep) + self.aero_solver.update_custom_grid(structural_kstep, + aero_kstep, + nl_body_tstep = nl_body_kstep) self.aero_solver.add_step() self.data.aero.timestep_info[-1] = aero_kstep.copy() + self.data.nonlifting_body.timestep_info[-1] = nl_body_kstep.copy() self.structural_solver.add_step() self.data.structure.timestep_info[-1] = structural_kstep.copy() From c5170201e441be208d3550a69cdf3ac407c0a52d Mon Sep 17 00:00:00 2001 From: sduess Date: Wed, 10 Nov 2021 14:56:09 +0000 Subject: [PATCH 066/232] fix: exclude forces on nonlifting body in static coupled solver - not important - include it later when everything is working correctly --- sharpy/solvers/staticcoupled.py | 39 +++++++++++++++++---------------- 1 file changed, 20 insertions(+), 19 deletions(-) diff --git a/sharpy/solvers/staticcoupled.py b/sharpy/solvers/staticcoupled.py index f3f2b92a2..6f3d1d1db 100644 --- a/sharpy/solvers/staticcoupled.py +++ b/sharpy/solvers/staticcoupled.py @@ -181,31 +181,32 @@ def run(self): self.data.structure.timestep_info[self.data.ts].pos, self.data.structure.timestep_info[self.data.ts].psi, self.data.structure.node_master_elem, - self.data.structure.connectivities, - self.data.structure.timestep_info[self.data.ts].cag(), - self.data.aero.data_dict) + self.data.structure.connectivities, + self.data.structure.timestep_info[self.data.ts].cag(), + self.data.aero.data_dict) - if self.correct_forces: + if self.correct_forces: struct_forces = self.correct_forces_function(self.data, self.data.aero.timestep_info[self.data.ts], self.data.structure.timestep_info[self.data.ts], struct_forces, rho=self.aero_solver.settings['rho']) - - if self.settings['nonlifting_body_interaction']: - struct_forces += mapping.aero2struct_force_mapping( - self.data.nonlifting_body.timestep_info[self.data.ts].forces, - self.data.nonlifting_body.struct2aero_mapping, - self.data.nonlifting_body.timestep_info[self.data.ts].zeta, - self.data.structure.timestep_info[self.data.ts].pos, - self.data.structure.timestep_info[self.data.ts].psi, - self.data.structure.node_master_elem, - self.data.structure.connectivities, - self.data.structure.timestep_info[self.data.ts].cag(), - self.data.nonlifting_body.data_dict) - - # Add external forces - if self.with_runtime_generators: + + + # if self.settings['nonlifting_body_interaction']: + # struct_forces += mapping.aero2struct_force_mapping( + # self.data.nonlifting_body.timestep_info[self.data.ts].forces, + # self.data.nonlifting_body.struct2aero_mapping, + # self.data.nonlifting_body.timestep_info[self.data.ts].zeta, + # self.data.structure.timestep_info[self.data.ts].pos, + # self.data.structure.timestep_info[self.data.ts].psi, + # self.data.structure.node_master_elem, + # self.data.structure.connectivities, + # self.data.structure.timestep_info[self.data.ts].cag(), + # self.data.nonlifting_body.data_dict) + + # Add external forces + if self.with_runtime_generators: self.data.structure.timestep_info[self.data.ts].runtime_generated_forces.fill(0.) params = dict() params['data'] = self.data From b4f7ab2e4732f13946978084a3de185bdabf4a06 Mon Sep 17 00:00:00 2001 From: sduess Date: Wed, 10 Nov 2021 14:57:23 +0000 Subject: [PATCH 067/232] update: staticcoupled solver - use newest force correction generator --- sharpy/solvers/staticcoupled.py | 55 +++++++++++++++++++-------------- 1 file changed, 31 insertions(+), 24 deletions(-) diff --git a/sharpy/solvers/staticcoupled.py b/sharpy/solvers/staticcoupled.py index 6f3d1d1db..17174d6a1 100644 --- a/sharpy/solvers/staticcoupled.py +++ b/sharpy/solvers/staticcoupled.py @@ -88,13 +88,13 @@ def __init__(self): self.previous_force = None - self.residual_table = None - - self.correct_forces = False - self.correct_forces_function = None - - self.runtime_generators = dict() - self.with_runtime_generators = False + self.residual_table = None + + self.correct_forces = False + self.correct_forces_generator = None + + self.runtime_generators = dict() + self.with_runtime_generators = False def initialise(self, data, input_dict=None): self.data = data @@ -122,13 +122,18 @@ def initialise(self, data, input_dict=None): self.residual_table.field_length[2] = 10 self.residual_table.print_header(['iter', 'step', 'log10(res)', 'Fx', 'Fy', 'Fz', 'Mx', 'My', 'Mz']) - # Define the function to correct aerodynamic forces - if self.settings['correct_forces_method'] is not '': - self.correct_forces = True - self.correct_forces_function = cf.dict_of_corrections[self.settings['correct_forces_method']] - - # initialise runtime generators - self.runtime_generators = dict() + # Define the function to correct aerodynamic forces + if self.settings['correct_forces_method'] is not '': + self.correct_forces = True + self.correct_forces_generator = gen_interface.generator_from_string(self.settings['correct_forces_method'])() + self.correct_forces_generator.initialise(in_dict=self.settings['correct_forces_settings'], + aero=self.data.aero, + structure=self.data.structure, + rho=self.settings['aero_solver_settings']['rho'], + vortex_radius=self.settings['aero_solver_settings']['vortex_radius']) + + # initialise runtime generators + self.runtime_generators = dict() if self.settings['runtime_generators']: self.with_runtime_generators = True for id, param in self.settings['runtime_generators'].items(): @@ -171,12 +176,13 @@ def run(self): for i_iter in range(self.settings['max_iter']): # run aero - self.data = self.aero_solver.run() - - # map force - struct_forces = mapping.aero2struct_force_mapping( - self.data.aero.timestep_info[self.data.ts].forces, - self.data.aero.struct2aero_mapping, + self.data = self.aero_solver.run() + + # map force + # TODO: case if nonlifting body only (coupling solvers do not make sense anyway for this case) + struct_forces = mapping.aero2struct_force_mapping( + self.data.aero.timestep_info[self.data.ts].forces, + self.data.aero.struct2aero_mapping, self.data.aero.timestep_info[self.data.ts].zeta, self.data.structure.timestep_info[self.data.ts].pos, self.data.structure.timestep_info[self.data.ts].psi, @@ -186,10 +192,10 @@ def run(self): self.data.aero.data_dict) if self.correct_forces: - struct_forces = self.correct_forces_function(self.data, - self.data.aero.timestep_info[self.data.ts], - self.data.structure.timestep_info[self.data.ts], - struct_forces, + struct_forces = \ + self.correct_forces_generator.generate(aero_kstep=self.data.aero.timestep_info[self.data.ts], + structural_kstep=self.data.structure.timestep_info[self.data.ts], + struct_forces=struct_forces) rho=self.aero_solver.settings['rho']) @@ -243,6 +249,7 @@ def run(self): # update grid self.aero_solver.update_step() + self.structural_solver.update(self.data.structure.timestep_info[self.data.ts]) # convergence if self.convergence(i_iter, i_step): # create q and dqdt vectors From 9b185106f7c85c510ada4e013d322781b945d9c0 Mon Sep 17 00:00:00 2001 From: sduess Date: Wed, 10 Nov 2021 14:59:14 +0000 Subject: [PATCH 068/232] fix outdated naming - aero_dimensions of aerogrid where changed to dimensions when indtroducing nonliftingbodygrid and grid classes --- sharpy/utils/correct_forces.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sharpy/utils/correct_forces.py b/sharpy/utils/correct_forces.py index 409285e16..2acc8428b 100644 --- a/sharpy/utils/correct_forces.py +++ b/sharpy/utils/correct_forces.py @@ -124,7 +124,7 @@ def polars(data, aero_kstep, structural_kstep, struct_forces, **kwargs): iairfoil = data_dict['airfoil_distribution'][ielem, inode_in_elem] isurf = aerogrid.struct2aero_mapping[inode][0]['i_surf'] i_n = aerogrid.struct2aero_mapping[inode][0]['i_n'] - N = aerogrid.aero_dimensions[isurf, 1] + N = aerogrid.dimensions[isurf, 1] polar = aerogrid.polars[iairfoil] cab = algebra.crv2rotation(structural_kstep.psi[ielem, inode_in_elem, :]) cga = algebra.quat2rotation(structural_kstep.quat) From b1d272843c5f8d7f8beba813c7c8e5ebfcf3b5ab Mon Sep 17 00:00:00 2001 From: sduess Date: Wed, 10 Nov 2021 15:01:09 +0000 Subject: [PATCH 069/232] update [stepuvlm]: update nonlifting surfaces when existent --- sharpy/solvers/stepuvlm.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/sharpy/solvers/stepuvlm.py b/sharpy/solvers/stepuvlm.py index 5ee463f4a..de6452e64 100644 --- a/sharpy/solvers/stepuvlm.py +++ b/sharpy/solvers/stepuvlm.py @@ -201,7 +201,8 @@ def run(self, convect_wake=True, dt=None, t=None, - unsteady_contribution=False): + unsteady_contribution=False, + nl_body_tstep = None): """ Runs a step of the aerodynamics as implemented in UVLM. """ From 35ca66f1b065261775aabcfd45d4eb8890165aee Mon Sep 17 00:00:00 2001 From: sduess Date: Wed, 10 Nov 2021 15:02:24 +0000 Subject: [PATCH 070/232] update [staticuvlm]: allow for nonlifting surfaces when existent --- sharpy/solvers/staticuvlm.py | 88 ++++++++++++++++++------------------ 1 file changed, 43 insertions(+), 45 deletions(-) diff --git a/sharpy/solvers/staticuvlm.py b/sharpy/solvers/staticuvlm.py index 281621e3c..1fdb984c1 100644 --- a/sharpy/solvers/staticuvlm.py +++ b/sharpy/solvers/staticuvlm.py @@ -143,50 +143,49 @@ def initialise(self, data, custom_settings=None): self.velocity_generator.initialise(self.settings['velocity_field_input']) def run(self): - if not self.data.aero.timestep_info[self.data.ts].zeta: - return self.data - - # generate the wake because the solid shape might change - aero_tstep = self.data.aero.timestep_info[self.data.ts] - self.data.aero.wake_shape_generator.generate({'zeta': aero_tstep.zeta, - 'zeta_star': aero_tstep.zeta_star, - 'gamma': aero_tstep.gamma, - 'gamma_star': aero_tstep.gamma_star, - 'dist_to_orig': aero_tstep.dist_to_orig}) - - # check if nonlifting body interactions have to be considered - if not self.settings['nonlifting_body_interactions']: - self.velocity_generator.generate({'zeta': self.data.nonlifting_body.timestep_info[self.data.ts].zeta, - 'override': True, - 'for_pos': self.data.structure.timestep_info[self.data.ts].for_pos[0:3]}, - self.data.nonlifting_body.timestep_info[self.data.ts].u_ext) - uvlmlib.vlm_solver_nonlifting_body(self.data.nonlifting_body.timestep_info[self.data.ts], - self.settings) - elif self.settings['nonlifting_body_interactions']: - # generate uext - self.velocity_generator.generate({'zeta': self.data.nonlifting_body.timestep_info[self.data.ts].zeta, - 'override': True, - 'for_pos': self.data.structure.timestep_info[self.data.ts].for_pos[0:3]}, - self.data.nonlifting_body.timestep_info[self.data.ts].u_ext) - # generate uext - self.velocity_generator.generate({'zeta': self.data.aero.timestep_info[self.data.ts].zeta, - 'override': True, - 'for_pos': self.data.structure.timestep_info[self.data.ts].for_pos[0:3]}, - self.data.aero.timestep_info[self.data.ts].u_ext) - # grid orientation - uvlmlib.vlm_solver_lifting_and_nonlifting_bodies(self.data.aero.timestep_info[self.data.ts], + if not self.settings['only_nonlifting']: + if not self.data.aero.timestep_info[self.data.ts].zeta: + return self.data + + # generate the wake because the solid shape might change + aero_tstep = self.data.aero.timestep_info[self.data.ts] + self.data.aero.wake_shape_generator.generate({'zeta': aero_tstep.zeta, + 'zeta_star': aero_tstep.zeta_star, + 'gamma': aero_tstep.gamma, + 'gamma_star': aero_tstep.gamma_star, + 'dist_to_orig': aero_tstep.dist_to_orig}) + if self.settings['nonlifting_body_interactions']: + # generate uext + self.velocity_generator.generate({'zeta': self.data.nonlifting_body.timestep_info[self.data.ts].zeta, + 'override': True, + 'for_pos': self.data.structure.timestep_info[self.data.ts].for_pos[0:3]}, + self.data.nonlifting_body.timestep_info[self.data.ts].u_ext) + # generate uext + self.velocity_generator.generate({'zeta': self.data.aero.timestep_info[self.data.ts].zeta, + 'override': True, + 'for_pos': self.data.structure.timestep_info[self.data.ts].for_pos[0:3]}, + self.data.aero.timestep_info[self.data.ts].u_ext) + # grid orientation + uvlmlib.vlm_solver_lifting_and_nonlifting_bodies(self.data.aero.timestep_info[self.data.ts], self.data.nonlifting_body.timestep_info[self.data.ts], self.settings) else: # generate uext self.velocity_generator.generate({'zeta': self.data.aero.timestep_info[self.data.ts].zeta, 'override': True, - 'for_pos': self.data.structure.timestep_info[self.data.ts].for_pos[0:3]}, - self.data.aero.timestep_info[self.data.ts].u_ext) + 'for_pos': self.data.structure.timestep_info[self.data.ts].for_pos[0:3]}, + self.data.aero.timestep_info[self.data.ts].u_ext) - # grid orientation - uvlmlib.vlm_solver(self.data.aero.timestep_info[self.data.ts], - self.settings) + # grid orientation + uvlmlib.vlm_solver(self.data.aero.timestep_info[self.data.ts], + self.settings) + else: + self.velocity_generator.generate({'zeta': self.data.nonlifting_body.timestep_info[self.data.ts].zeta, + 'override': True, + 'for_pos': self.data.structure.timestep_info[self.data.ts].for_pos[0:3]}, + self.data.nonlifting_body.timestep_info[self.data.ts].u_ext) + uvlmlib.vlm_solver_nonlifting_body(self.data.nonlifting_body.timestep_info[self.data.ts], + self.settings) return self.data @@ -199,13 +198,12 @@ def next_step(self): self.update_step() def update_step(self): - self.data.aero.generate_zeta(self.data.structure, - self.data.aero.aero_settings, - self.data.ts) - # for i_surf in range(self.data.aero.timestep_info[self.data.ts].n_surf): - # self.data.aero.timestep_info[self.data.ts].forces[i_surf].fill(0.0) - # self.data.aero.timestep_info[self.data.ts].dynamic_forces[i_surf].fill(0.0) - if self.settings['nonlifting_body_interactions']: + if not self.settings['only_nonlifting']: + self.data.aero.generate_zeta(self.data.structure, + self.data.aero.aero_settings, + self.data.ts) + if self.settings['nonlifting_body_interactions'] or self.settings['only_nonlifting']: self.data.nonlifting_body.generate_zeta(self.data.structure, - self.data.aero.aero_settings, + self.data.nonlifting_body.aero_settings, self.data.ts) + From 2a5de98e16a395cd39d4618b5a4c64bc9644ed87 Mon Sep 17 00:00:00 2001 From: sduess Date: Wed, 10 Nov 2021 15:03:02 +0000 Subject: [PATCH 071/232] update [staticcoupled]: include latest force correction method --- sharpy/solvers/staticcoupled.py | 26 ++++++++++++++++---------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/sharpy/solvers/staticcoupled.py b/sharpy/solvers/staticcoupled.py index 17174d6a1..c2007bb84 100644 --- a/sharpy/solvers/staticcoupled.py +++ b/sharpy/solvers/staticcoupled.py @@ -58,16 +58,22 @@ class StaticCoupled(BaseSolver): settings_description['tolerance'] = 'Convergence threshold for the FSI loop' settings_types['relaxation_factor'] = 'float' - settings_default['relaxation_factor'] = 0. - settings_description['relaxation_factor'] = 'Relaxation parameter in the FSI iteration. 0 is no relaxation and -> 1 is very relaxed' - - settings_types['correct_forces_method'] = 'str' - settings_default['correct_forces_method'] = '' # 'efficiency' - settings_description['correct_forces_method'] = 'Function used to correct aerodynamic forces. Check :py:mod:`sharpy.utils.correct_forces`' - settings_options['correct_forces_method'] = ['efficiency', 'polars'] - - settings_types['runtime_generators'] = 'dict' - settings_default['runtime_generators'] = dict() + settings_default['relaxation_factor'] = 0. + settings_description['relaxation_factor'] = 'Relaxation parameter in the FSI iteration. 0 is no relaxation and -> 1 is very relaxed' + + + settings_types['correct_forces_method'] = 'str' + settings_default['correct_forces_method'] = '' + settings_description['correct_forces_method'] = 'Function used to correct aerodynamic forces. ' \ + 'See :py:mod:`sharpy.generators.polaraeroforces`' + settings_options['correct_forces_method'] = ['EfficiencyCorrection', 'PolarCorrection'] + + settings_types['correct_forces_settings'] = 'dict' + settings_default['correct_forces_settings'] = {} + settings_description['correct_forces_settings'] = 'Settings for corrected forces evaluation' + + settings_types['runtime_generators'] = 'dict' + settings_default['runtime_generators'] = dict() settings_description['runtime_generators'] = 'The dictionary keys are the runtime generators to be used. ' \ 'The dictionary values are dictionaries with the settings ' \ 'needed by each generator.' From 41f8a6f3df1399e4c54f96c067a0cba4a0082639 Mon Sep 17 00:00:00 2001 From: sduess Date: Wed, 10 Nov 2021 15:17:53 +0000 Subject: [PATCH 072/232] fix lift distribution postprocessor - just quick temporarly fix - I will update the file appropriately like a real software engineer later on when I am not stressed anymore because of a deadline --- sharpy/postproc/liftdistribution.py | 168 +++++++++++++++++++++++----- 1 file changed, 139 insertions(+), 29 deletions(-) diff --git a/sharpy/postproc/liftdistribution.py b/sharpy/postproc/liftdistribution.py index 98308066b..86579614d 100644 --- a/sharpy/postproc/liftdistribution.py +++ b/sharpy/postproc/liftdistribution.py @@ -24,7 +24,7 @@ class LiftDistribution(BaseSolver): settings_description = dict() settings_types['text_file_name'] = 'str' - settings_default['text_file_name'] = 'lift_distribution.csv' + settings_default['text_file_name'] = 'lift_distribution' settings_description['text_file_name'] = 'Text file name' settings_default['coefficients'] = True @@ -34,6 +34,9 @@ class LiftDistribution(BaseSolver): settings_types['q_ref'] = 'float' settings_default['q_ref'] = 1 settings_description['q_ref'] = 'Reference dynamic pressure' + settings_types['rho'] = 'float' + settings_default['rho'] = 1.225 + settings_description['rho'] = 'Reference density [kg/m³]' settings_table = settings.SettingsTable() __doc__ += settings_table.generate(settings_types, settings_default, settings_description) @@ -59,7 +62,6 @@ def run(self, online=False): def lift_distribution(self, struct_tstep, aero_tstep): # Force mapping - rot = algebra.quat2rotation(struct_tstep.quat) forces = mapping.aero2struct_force_mapping( aero_tstep.forces + aero_tstep.dynamic_forces, self.data.aero.struct2aero_mapping, @@ -73,38 +75,57 @@ def lift_distribution(self, struct_tstep, aero_tstep): # Prepare output matrix and file N_nodes = self.data.structure.num_node numb_col = 4 - header= "x,y,z,fz" + header = "x,y,z,fz" # get aero forces lift_distribution = np.zeros((N_nodes, numb_col)) + # get rotation matrix + cga = algebra.quat2rotation(struct_tstep.quat) + if self.settings["coefficients"]: + # TODO: add nondimensional spanwise column y/s + header += ", y/s ,cl" + numb_col += 2 + lift_distribution = np.concatenate((lift_distribution, np.zeros((N_nodes, 2))), axis=1) + for inode in range(N_nodes): + + lift_distribution[inode, 2] = struct_tstep.pos[inode, 2] # z + lift_distribution[inode, 1] = struct_tstep.pos[inode, 1] # y + lift_distribution[inode, 0] = struct_tstep.pos[inode, 0] # x if self.data.aero.data_dict['aero_node'][inode]: - # transform forces from B to A frame - lift_distribution[inode,3]=np.dot(rot.T, forces[inode, :3])[2] # lift force - lift_distribution[inode,2]=struct_tstep.pos[inode, 2] #z - lift_distribution[inode,1]=struct_tstep.pos[inode, 1] #y - lift_distribution[inode,0]=struct_tstep.pos[inode, 0] #x - - if self.settings["coefficients"]: - # get lift coefficient - strip_area = self.calculate_strip_area(aero_tstep) - # TODO: add nondimensional spanwise column y/s - header += ",cl" - numb_col += 1 - lift_distribution = np.concatenate((lift_distribution, np.zeros((N_nodes,1))), axis=1) - for inode in range(N_nodes): - if self.data.aero.data_dict['aero_node'][inode]: - local_node = self.data.aero.struct2aero_mapping[inode][0]["i_n"] - ielem, _ = self.data.structure.node_master_elem[inode] + if len(self.data.aero.struct2aero_mapping[inode]) > 0: + # print("local node = ", inode, ", ", self.data.aero.struct2aero_mapping[inode]) + local_node = self.data.aero.struct2aero_mapping[inode][0]["i_n"] + ielem, inode_in_elem = self.data.structure.node_master_elem[inode] i_surf = int(self.data.aero.surface_distribution[ielem]) - lift_distribution[inode,4] = lift_distribution[inode,3]/(self.settings['q_ref']*\ - strip_area[i_surf][local_node]) # cl - - # Check if shared nodes from different surfaces exist (e.g. two wings joining at symmetry plane) - # Leads to error since panel area just donates for half the panel size while lift forces is summed up - lift_distribution[inode,4] /= len(self.data.aero.struct2aero_mapping[inode]) - + # get c_gb + cab = algebra.crv2rotation(struct_tstep.psi[ielem, inode_in_elem, :]) + cgb = np.dot(cga, cab) + # Get c_bs + urel, dir_urel = self.magnitude_and_direction_of_relative_velocity(struct_tstep.pos[inode, :], + struct_tstep.pos_dot[inode, :], + struct_tstep.for_vel[:], + cga, + aero_tstep.u_ext[i_surf][:, :, + local_node]) + dir_span, span, dir_chord, chord = self.span_chord(local_node, aero_tstep.zeta[i_surf]) + # Stability axes - projects forces in B onto S + c_bs = self.local_stability_axes(cgb.T.dot(dir_urel), cgb.T.dot(dir_chord)) + lift_force = c_bs.T.dot(forces[inode, :3])[2] + # Store data in export matrix + lift_distribution[inode, 3] = lift_force + if self.settings["coefficients"]: + # Get non-dimensional spanwise coordinate y/s + lift_distribution[inode, 4] = lift_distribution[inode, 1]/max(abs(aero_tstep.zeta[i_surf][1,0,:])) + # Get lift coefficient + lift_distribution[inode, 5] = np.sign(lift_force) * np.linalg.norm(lift_force) \ + / (0.5 * self.settings['rho'] \ + * np.linalg.norm(urel) ** 2 * span * chord) + # Check if shared nodes from different surfaces exist (e.g. two wings joining at symmetry plane) + # Leads to error since panel area just donates for half the panel size while lift forces is summed up + lift_distribution[inode, 5] /= len(self.data.aero.struct2aero_mapping[inode]) + # Export lift distribution data - np.savetxt(os.path.join(self.folder,self.settings['text_file_name']), lift_distribution, fmt='%10e,'*(numb_col-1)+'%10e', delimiter = ", ", header= header) + np.savetxt(os.path.join(self.folder,self.settings['text_file_name']+'_' + str(self.data.ts) + '.csv'), lift_distribution, fmt='%10e,'*(numb_col-1)+'%10e', delimiter = ", ", header= header) def calculate_strip_area(self, aero_tstep): # Function to get the area of a strip, which has half of the panel area @@ -128,4 +149,93 @@ def calculate_strip_area(self, aero_tstep): strip_area[i_surf][-1] = abs(array_panel_area[-1]) strip_area[i_surf][:] /= 2 - return strip_area \ No newline at end of file + return strip_area + + def magnitude_and_direction_of_relative_velocity(self, displacement, displacement_vel, for_vel, cga, uext): + """ + Calculates the magnitude and direction of the relative velocity ``u_rel`` + + Args: + displacement (np.array): Unit vector in the direction of the free stream velocity expressed in B frame. + displacement_vel (np.array): Unit vector in the direction of the local chord expressed in B frame. + for_vel + cga + uext + Returns: + tuple: ``u_rel``, ``dir_u_rel`` + """ + urel = (displacement_vel+ + for_vel[0:3] + + algebra.cross3(for_vel[3:6], displacement)) + urel = -np.dot(cga, urel) + urel += np.average(uext, axis=1) + + dir_urel = algebra.unit_vector(urel) + return urel, dir_urel + + def local_stability_axes(self, dir_urel, dir_chord): + """ + Rotates the body axes onto stability axes. This rotation is equivalent to the projection of a vector in S onto B. + + The stability axes are defined as: + + * ``x_s``: parallel to the free stream + + * ``z_s``: perpendicular to the free stream and part of the plane formed by the local chord and the vertical + body axis ``z_b``. + + * ``y_s``: completes the set + + Args: + dir_urel (np.array): Unit vector in the direction of the free stream velocity expressed in B frame. + dir_chord (np.array): Unit vector in the direction of the local chord expressed in B frame. + + Returns: + np.array: Rotation matrix from B to S, equivalent to the projection matrix :math:`C^{BS}` that projects a + vector from S onto B. + """ + xs = dir_urel + + zb = np.array([0, 0, 1.]) + zs = algebra.cross3(algebra.cross3(dir_chord, zb), dir_urel) + + ys = -algebra.cross3(xs, zs) + + return algebra.triad2rotation(xs, ys, zs) + + def span_chord(self, i_node_surf, zeta): + """ + Retrieve the local span and local chord + + Args: + i_node_surf (int): Node index in aerodynamic surface + zeta (np.array): Aerodynamic surface coordinates ``(3 x n_chord x m_span)`` + + Returns: + tuple: ``dir_span``, ``span``, ``dir_chord``, ``chord`` + """ + N = zeta.shape[2] - 1 # spanwise vertices in surface (-1 for index) + + # Deal with the extremes + if i_node_surf == 0: + node_p = 1 + node_m = 0 + elif i_node_surf == N: + node_p = N + node_m = N - 1 + else: + node_p = i_node_surf + 1 + node_m = i_node_surf - 1 + + # Define the span and the span direction + dir_span = 0.5 * (zeta[:, 0, node_p] - zeta[:, 0, node_m]) + + span = np.linalg.norm(dir_span) + dir_span = algebra.unit_vector(dir_span) + + # Define the chord and the chord direction + dir_chord = zeta[:, -1, i_node_surf] - zeta[:, 0, i_node_surf] + chord = np.linalg.norm(dir_chord) + dir_chord = algebra.unit_vector(dir_chord) + + return dir_span, span, dir_chord, chord \ No newline at end of file From 2cf1f49842ef3dfb23f26ce7e06cbadb5d7ad840 Mon Sep 17 00:00:00 2001 From: sduess Date: Wed, 10 Nov 2021 16:17:45 +0000 Subject: [PATCH 073/232] add [utils]: function to get correct force vector - used for polar corrections --- sharpy/aero/utils/utils.py | 155 +++++++++++++++++++++++++++++++++++++ 1 file changed, 155 insertions(+) diff --git a/sharpy/aero/utils/utils.py b/sharpy/aero/utils/utils.py index 04c03dbc0..c1f842884 100644 --- a/sharpy/aero/utils/utils.py +++ b/sharpy/aero/utils/utils.py @@ -1,5 +1,7 @@ +"""Aero utilities functions""" import numpy as np import sharpy.utils.algebra as algebra +from sharpy.utils import algebra as algebra def flightcon_file_parser(fc_dict): @@ -18,3 +20,156 @@ def alpha_beta_to_direction(alpha, beta): beta_rot = algebra.rotation3d_z(beta) direction = np.dot(beta_rot, np.dot(alpha_rot, direction)) return direction + + +def magnitude_and_direction_of_relative_velocity(displacement, displacement_vel, for_vel, cga, uext): + r""" + Calculates the magnitude and direction of the relative velocity ``u_rel`` at a local section of the wing. + + .. math:: + + u_{rel, i}^G = \bar{U}_{\infty, i}^G - C^{GA}(\chi)(\dot{\eta}_i^A + v^A + \tilde{\omega}^A\eta_i^A + + where :math:`\bar{U}_{\infty, i}^G` is the average external velocity across all aerodynamic nodes at the + relevant cross section. + + Args: + displacement (np.array): Unit vector in the direction of the free stream velocity expressed in A frame. + displacement_vel (np.array): Unit vector in the direction of the local chord expressed in A frame. + for_vel (np.array): ``A`` frame of reference (FoR) velocity. Expressed in A FoR + cga (np.array): Rotation vector from FoR ``G`` to FoR ``A`` + uext (np.array): Background flow velocity on solid grid nodes + + Returns: + tuple: ``u_rel``, ``dir_u_rel`` expressed in the inertial, ``G`` frame. + """ + urel = (displacement_vel + + for_vel[0:3] + + algebra.cross3(for_vel[3:6], displacement)) + urel = -np.dot(cga, urel) + urel += np.average(uext, axis=1) + + dir_urel = algebra.unit_vector(urel) + + return urel, dir_urel + + +def local_stability_axes(dir_urel, dir_chord): + """ + Rotates the body axes onto stability axes. This rotation is equivalent to the projection of a vector in S onto B. + + The stability axes are defined as: + + * ``x_s``: parallel to the free stream + + * ``z_s``: perpendicular to the free stream and part of the plane formed by the local chord and the vertical + body axis ``z_b``. + + * ``y_s``: completes the set + + Args: + dir_urel (np.array): Unit vector in the direction of the free stream velocity expressed in B frame. + dir_chord (np.array): Unit vector in the direction of the local chord expressed in B frame. + + Returns: + np.array: Rotation matrix from B to S, equivalent to the projection matrix :math:`C^{BS}` that projects a + vector from S onto B. + """ + xs = dir_urel + + zb = np.array([0, 0, 1.]) + zs = algebra.cross3(algebra.cross3(dir_chord, zb), dir_urel) + + ys = -algebra.cross3(xs, zs) + + return algebra.triad2rotation(xs, ys, zs) + + +def span_chord(i_node_surf, zeta): + """ + Retrieve the local span and local chord + + Args: + i_node_surf (int): Node index in aerodynamic surface + zeta (np.array): Aerodynamic surface coordinates ``(3 x n_chord x m_span)`` + + Returns: + tuple: ``dir_span``, ``span``, ``dir_chord``, ``chord`` + """ + N = zeta.shape[2] - 1 # spanwise vertices in surface (-1 for index) + + # Deal with the extremes + if i_node_surf == 0: + node_p = 1 + node_m = 0 + elif i_node_surf == N: + node_p = N + node_m = N - 1 + else: + node_p = i_node_surf + 1 + node_m = i_node_surf - 1 + + # Define the span and the span direction + dir_span = 0.5 * (zeta[:, 0, node_p] - zeta[:, 0, node_m]) + + span = np.linalg.norm(dir_span) + dir_span = algebra.unit_vector(dir_span) + + # Define the chord and the chord direction + dir_chord = zeta[:, -1, i_node_surf] - zeta[:, 0, i_node_surf] + chord = np.linalg.norm(dir_chord) + dir_chord = algebra.unit_vector(dir_chord) + + return dir_span, span, dir_chord, chord + + +def find_aerodynamic_solver(settings): + """ + Retrieves the name and settings of the first aerodynamic solver used in the solution ``flow``. + + Args: + settings (dict): SHARPy settings (usually found in ``data.settings`` ) + + Returns: + tuple: Aerodynamic solver name and solver settings + """ + flow = settings['SHARPy']['flow'] + # Look for the aerodynamic solver + if 'StaticUvlm' in flow: + aero_solver_name = 'StaticUvlm' + aero_solver_settings = settings['StaticUvlm'] + elif 'StaticCoupled' in flow: + aero_solver_name = settings['StaticCoupled']['aero_solver'] + aero_solver_settings = settings['StaticCoupled']['aero_solver_settings'] + elif 'StaticCoupledRBM' in flow: + aero_solver_name = settings['StaticCoupledRBM']['aero_solver'] + aero_solver_settings = settings['StaticCoupledRBM']['aero_solver_settings'] + elif 'DynamicCoupled' in flow: + aero_solver_name = settings['DynamicCoupled']['aero_solver'] + aero_solver_settings = settings['DynamicCoupled']['aero_solver_settings'] + elif 'StepUvlm' in flow: + aero_solver_name = 'StepUvlm' + aero_solver_settings = settings['StepUvlm'] + else: + raise KeyError("ERROR: aerodynamic solver not found") + + return aero_solver_name, aero_solver_settings + + +def find_velocity_generator(settings): + """ + Retrieves the name and settings of the fluid velocity generator in the first aerodynamic solver used in the + solution ``flow``. + + Args: + settings (dict): SHARPy settings (usually found in ``data.settings`` ) + + Returns: + tuple: velocity generator name and velocity generator settings + """ + aero_solver_name, aero_solver_settings = find_aerodynamic_solver(settings) + + vel_gen_name = aero_solver_settings['velocity_field_generator'] + vel_gen_settings = aero_solver_settings['velocity_field_input'] + + return vel_gen_name, vel_gen_settings From 17136d9bba3c92c5aad3b0529aa262a9ff03eacf Mon Sep 17 00:00:00 2001 From: sduess Date: Wed, 10 Nov 2021 16:18:32 +0000 Subject: [PATCH 074/232] fix typos --- sharpy/solvers/staticcoupled.py | 136 ++++++++++++++++---------------- sharpy/solvers/stepuvlm.py | 1 - 2 files changed, 67 insertions(+), 70 deletions(-) diff --git a/sharpy/solvers/staticcoupled.py b/sharpy/solvers/staticcoupled.py index c2007bb84..4461aa38d 100644 --- a/sharpy/solvers/staticcoupled.py +++ b/sharpy/solvers/staticcoupled.py @@ -58,22 +58,22 @@ class StaticCoupled(BaseSolver): settings_description['tolerance'] = 'Convergence threshold for the FSI loop' settings_types['relaxation_factor'] = 'float' - settings_default['relaxation_factor'] = 0. - settings_description['relaxation_factor'] = 'Relaxation parameter in the FSI iteration. 0 is no relaxation and -> 1 is very relaxed' - - - settings_types['correct_forces_method'] = 'str' - settings_default['correct_forces_method'] = '' - settings_description['correct_forces_method'] = 'Function used to correct aerodynamic forces. ' \ - 'See :py:mod:`sharpy.generators.polaraeroforces`' - settings_options['correct_forces_method'] = ['EfficiencyCorrection', 'PolarCorrection'] - - settings_types['correct_forces_settings'] = 'dict' - settings_default['correct_forces_settings'] = {} - settings_description['correct_forces_settings'] = 'Settings for corrected forces evaluation' - - settings_types['runtime_generators'] = 'dict' - settings_default['runtime_generators'] = dict() + settings_default['relaxation_factor'] = 0. + settings_description['relaxation_factor'] = 'Relaxation parameter in the FSI iteration. 0 is no relaxation and -> 1 is very relaxed' + + + settings_types['correct_forces_method'] = 'str' + settings_default['correct_forces_method'] = '' + settings_description['correct_forces_method'] = 'Function used to correct aerodynamic forces. ' \ + 'See :py:mod:`sharpy.generators.polaraeroforces`' + settings_options['correct_forces_method'] = ['EfficiencyCorrection', 'PolarCorrection'] + + settings_types['correct_forces_settings'] = 'dict' + settings_default['correct_forces_settings'] = {} + settings_description['correct_forces_settings'] = 'Settings for corrected forces evaluation' + + settings_types['runtime_generators'] = 'dict' + settings_default['runtime_generators'] = dict() settings_description['runtime_generators'] = 'The dictionary keys are the runtime generators to be used. ' \ 'The dictionary values are dictionaries with the settings ' \ 'needed by each generator.' @@ -94,13 +94,13 @@ def __init__(self): self.previous_force = None - self.residual_table = None - - self.correct_forces = False - self.correct_forces_generator = None - - self.runtime_generators = dict() - self.with_runtime_generators = False + self.residual_table = None + + self.correct_forces = False + self.correct_forces_generator = None + + self.runtime_generators = dict() + self.with_runtime_generators = False def initialise(self, data, input_dict=None): self.data = data @@ -128,18 +128,18 @@ def initialise(self, data, input_dict=None): self.residual_table.field_length[2] = 10 self.residual_table.print_header(['iter', 'step', 'log10(res)', 'Fx', 'Fy', 'Fz', 'Mx', 'My', 'Mz']) - # Define the function to correct aerodynamic forces - if self.settings['correct_forces_method'] is not '': - self.correct_forces = True - self.correct_forces_generator = gen_interface.generator_from_string(self.settings['correct_forces_method'])() - self.correct_forces_generator.initialise(in_dict=self.settings['correct_forces_settings'], - aero=self.data.aero, - structure=self.data.structure, - rho=self.settings['aero_solver_settings']['rho'], - vortex_radius=self.settings['aero_solver_settings']['vortex_radius']) - - # initialise runtime generators - self.runtime_generators = dict() + # Define the function to correct aerodynamic forces + if self.settings['correct_forces_method'] is not '': + self.correct_forces = True + self.correct_forces_generator = gen_interface.generator_from_string(self.settings['correct_forces_method'])() + self.correct_forces_generator.initialise(in_dict=self.settings['correct_forces_settings'], + aero=self.data.aero, + structure=self.data.structure, + rho=self.settings['aero_solver_settings']['rho'], + vortex_radius=self.settings['aero_solver_settings']['vortex_radius']) + + # initialise runtime generators + self.runtime_generators = dict() if self.settings['runtime_generators']: self.with_runtime_generators = True for id, param in self.settings['runtime_generators'].items(): @@ -182,43 +182,41 @@ def run(self): for i_iter in range(self.settings['max_iter']): # run aero - self.data = self.aero_solver.run() - - # map force - # TODO: case if nonlifting body only (coupling solvers do not make sense anyway for this case) - struct_forces = mapping.aero2struct_force_mapping( - self.data.aero.timestep_info[self.data.ts].forces, - self.data.aero.struct2aero_mapping, + self.data = self.aero_solver.run() + + # map force + # TODO: case if nonlifting body only (coupling solvers do not make sense anyway for this case) + struct_forces = mapping.aero2struct_force_mapping( + self.data.aero.timestep_info[self.data.ts].forces, + self.data.aero.struct2aero_mapping, self.data.aero.timestep_info[self.data.ts].zeta, self.data.structure.timestep_info[self.data.ts].pos, self.data.structure.timestep_info[self.data.ts].psi, self.data.structure.node_master_elem, - self.data.structure.connectivities, - self.data.structure.timestep_info[self.data.ts].cag(), - self.data.aero.data_dict) - - if self.correct_forces: - struct_forces = \ - self.correct_forces_generator.generate(aero_kstep=self.data.aero.timestep_info[self.data.ts], - structural_kstep=self.data.structure.timestep_info[self.data.ts], - struct_forces=struct_forces) - rho=self.aero_solver.settings['rho']) - - - # if self.settings['nonlifting_body_interaction']: - # struct_forces += mapping.aero2struct_force_mapping( - # self.data.nonlifting_body.timestep_info[self.data.ts].forces, - # self.data.nonlifting_body.struct2aero_mapping, - # self.data.nonlifting_body.timestep_info[self.data.ts].zeta, - # self.data.structure.timestep_info[self.data.ts].pos, - # self.data.structure.timestep_info[self.data.ts].psi, - # self.data.structure.node_master_elem, - # self.data.structure.connectivities, - # self.data.structure.timestep_info[self.data.ts].cag(), - # self.data.nonlifting_body.data_dict) - - # Add external forces - if self.with_runtime_generators: + self.data.structure.connectivities, + self.data.structure.timestep_info[self.data.ts].cag(), + self.data.aero.data_dict) + + if self.correct_forces: + struct_forces = \ + self.correct_forces_generator.generate(aero_kstep=self.data.aero.timestep_info[self.data.ts], + structural_kstep=self.data.structure.timestep_info[self.data.ts], + struct_forces=struct_forces) + + # if self.settings['nonlifting_body_interaction']: + # struct_forces += mapping.aero2struct_force_mapping( + # self.data.nonlifting_body.timestep_info[self.data.ts].forces, + # self.data.nonlifting_body.struct2aero_mapping, + # self.data.nonlifting_body.timestep_info[self.data.ts].zeta, + # self.data.structure.timestep_info[self.data.ts].pos, + # self.data.structure.timestep_info[self.data.ts].psi, + # self.data.structure.node_master_elem, + # self.data.structure.connectivities, + # self.data.structure.timestep_info[self.data.ts].cag(), + # self.data.nonlifting_body.data_dict) + + # Add external forces + if self.with_runtime_generators: self.data.structure.timestep_info[self.data.ts].runtime_generated_forces.fill(0.) params = dict() params['data'] = self.data @@ -255,7 +253,7 @@ def run(self): # update grid self.aero_solver.update_step() - self.structural_solver.update(self.data.structure.timestep_info[self.data.ts]) + self.structural_solver.update(self.data.structure.timestep_info[self.data.ts]) # convergence if self.convergence(i_iter, i_step): # create q and dqdt vectors diff --git a/sharpy/solvers/stepuvlm.py b/sharpy/solvers/stepuvlm.py index de6452e64..af563dc11 100644 --- a/sharpy/solvers/stepuvlm.py +++ b/sharpy/solvers/stepuvlm.py @@ -252,7 +252,6 @@ def run(self, 'is_wake': False}, nl_body_tstep.u_ext) - print("convection scheme = ", self.settings['convection_scheme']) uvlmlib.uvlm_solver_lifting_and_nonlifting(self.data.ts, aero_tstep, nl_body_tstep, From 3c5a06f6794f3f05d8f39f9466a2cf39ccf934c5 Mon Sep 17 00:00:00 2001 From: sduess Date: Wed, 10 Nov 2021 16:20:57 +0000 Subject: [PATCH 075/232] add: polaraeroforces - copy & pasted Norberto's polaraeroforces (yes, I feel bad) --- sharpy/generators/polaraeroforces.py | 286 +++++++++++++++++++++++++++ 1 file changed, 286 insertions(+) create mode 100644 sharpy/generators/polaraeroforces.py diff --git a/sharpy/generators/polaraeroforces.py b/sharpy/generators/polaraeroforces.py new file mode 100644 index 000000000..5bf451642 --- /dev/null +++ b/sharpy/generators/polaraeroforces.py @@ -0,0 +1,286 @@ +import numpy as np +import sharpy.utils.generator_interface as generator_interface +import sharpy.utils.settings as settings +import sharpy.utils.algebra as algebra +from sharpy.aero.utils.utils import magnitude_and_direction_of_relative_velocity, local_stability_axes, span_chord +from sharpy.utils.generate_cases import get_aoacl0_from_camber + + +@generator_interface.generator +class PolarCorrection(generator_interface.BaseGenerator): + """ + This generator corrects the aerodynamic forces from UVLM based on the airfoil polars provided by the user in the + ``aero.h5`` file. Polars are entered for each airfoil, in a table comprising ``AoA (rad), CL, CD, CM``. + + This ``generator_id = 'PolarCorrection'`` and can be used in the coupled solvers through the + ``correct_forces_method`` setting as: + + .. python:: + settings = dict() # SHARPy settings + settings['StaticCoupled']['correct_forces_method'] = 'PolarCorrection' + settings['StaticCoupled']['correct_forces_settings'] = {'cd_from_cl': 'off', # recommended settings (default) + 'correct_lift': 'off', + 'moment_from_polar': 'off'} + + These are the steps needed to correct the forces: + + 1. The force coming from UVLM is divided into induced drag (parallel to the incoming flow velocity) and lift + (the remaining force). + + If ``cd_from_cl == 'on'``. + 2. The viscous drag and pitching moment are found at the computed lift coefficient. Then forces and + moments are updated + + Else, the angle of attack is computed: + + 2. The angle of attack is computed based on that lift force and the angle of zero lift computed from the + airfoil polar and assuming the potential flow lift curve slope of :math:`2 \pi` + + 3. The drag force is computed based on the angle of attack and the polars provided by the user + + 4. If ``correct_lift == 'on'``, the lift coefficient is also corrected with the polar data. Else, only the + UVLM results are used. + + The pitching moment is added in a similar manner as the viscous drag. However, if ``moment_from_polar == 'on'`` + and ``correct_lift == 'on'``, the total moment (the one used for the FSI) is computed just from polar data, + overriding any moment computed in SHARPy. That is, the moment will include the polar pitching moment, and moments + due to lift and drag computed from the polar data. + + """ + generator_id = 'PolarCorrection' + + settings_types = dict() + settings_default = dict() + settings_description = dict() + settings_options = dict() + + settings_types['correct_lift'] = 'bool' + settings_default['correct_lift'] = False + settings_description['correct_lift'] = 'Correct lift according to the polars' + + settings_types['cd_from_cl'] = 'bool' + settings_default['cd_from_cl'] = False + settings_description['cd_from_cl'] = 'Interpolate the C_D for the given C_L, as opposed to getting the C_D from ' \ + 'the section AoA.' + + settings_types['moment_from_polar'] = 'bool' + settings_default['moment_from_polar'] = False + settings_description['moment_from_polar'] = 'If ``correct_lift`` is selected, it will compute the pitching moment ' \ + 'simply from polar derived data, i.e. the polars Cm and the moments' \ + 'arising from the lift and drag (derived from the polar) contribution. ' \ + 'Else, it will add the polar Cm to the moment already computed by ' \ + 'SHARPy.' + + settings_table = settings.SettingsTable() + __doc__ += settings_table.generate(settings_types, settings_default, settings_description, settings_options, + header_line='This generator takes in the following settings.') + + def __init__(self): + self.settings = None + + self.aero = None + self.structure = None + self.rho = None + self.vortex_radius = None + + def initialise(self, in_dict, **kwargs): + self.settings = in_dict + settings.to_custom_types(self.settings, self.settings_types, self.settings_default) + + self.aero = kwargs.get('aero') + self.structure = kwargs.get('structure') + self.rho = kwargs.get('rho') + self.vortex_radius = kwargs.get('vortex_radius', 1e-6) + + def generate(self, **params): + """ + Keyword Args: + aero_kstep (:class:`sharpy.utils.datastructures.AeroTimeStepInfo`): Current aerodynamic substep + structural_kstep (:class:`sharpy.utils.datastructures.StructTimeStepInfo`): Current structural substep + struct_forces (np.array): Array with the aerodynamic forces mapped on the structure in the B frame of + reference + + Returns: + np.array: New corrected structural forces + """ + aero_kstep = params['aero_kstep'] + structural_kstep = params['structural_kstep'] + struct_forces = params['struct_forces'] + + aerogrid = self.aero + structure = self.structure + rho = self.rho + correct_lift = self.settings['correct_lift'] + cd_from_cl = self.settings['cd_from_cl'] + moment_from_polar = self.settings['moment_from_polar'] + + aero_dict = aerogrid.aero_dict + if aerogrid.polars is None: + return struct_forces + new_struct_forces = np.zeros_like(struct_forces) + + nnode = struct_forces.shape[0] + + # Compute induced velocities at the structural points + cga = algebra.quat2rotation(structural_kstep.quat) + pos_g = np.array([cga.dot(structural_kstep.pos[inode]) + np.array([0, 0, 0]) for inode in range(nnode)]) + + for inode in range(nnode): + new_struct_forces[inode, :] = struct_forces[inode, :].copy() + if aero_dict['aero_node'][inode]: + ielem, inode_in_elem = structure.node_master_elem[inode] + iairfoil = aero_dict['airfoil_distribution'][ielem, inode_in_elem] + isurf = aerogrid.struct2aero_mapping[inode][0]['i_surf'] + i_n = aerogrid.struct2aero_mapping[inode][0]['i_n'] + N = aerogrid.aero_dimensions[isurf, 1] + polar = aerogrid.polars[iairfoil] + cab = algebra.crv2rotation(structural_kstep.psi[ielem, inode_in_elem, :]) + cgb = np.dot(cga, cab) + + if not cd_from_cl: + airfoil_coords = aerogrid.aero_dict['airfoils'][str(aero_dict['airfoil_distribution'][ielem, inode_in_elem])] + + dir_span, span, dir_chord, chord = span_chord(i_n, aero_kstep.zeta[isurf]) + + # Define the relative velocity and its direction + urel, dir_urel = magnitude_and_direction_of_relative_velocity(structural_kstep.pos[inode, :], + structural_kstep.pos_dot[inode, :], + structural_kstep.for_vel[:], + cga, + aero_kstep.u_ext[isurf][:, :, i_n]) + + # Coefficient to change from aerodynamic coefficients to forces (and viceversa) + coef = 0.5 * rho * np.linalg.norm(urel) ** 2 * chord * span + + # Stability axes - projects forces in B onto S + c_bs = local_stability_axes(cgb.T.dot(dir_urel), cgb.T.dot(dir_chord)) + forces_s = c_bs.T.dot(struct_forces[inode, :3]) + moment_s = c_bs.T.dot(struct_forces[inode, 3:]) + drag_force = forces_s[0] + lift_force = forces_s[2] + + # Compute the associated lift + cl = np.sign(lift_force) * np.linalg.norm(lift_force) / coef + cd_sharpy = np.linalg.norm(drag_force) / coef + + if cd_from_cl: + # Compute the drag from the UVLM computed lift + cd, cm = polar.get_cdcm_from_cl(cl) + + else: + # Compute L, D, M from polar depending on: + # ii) Compute the effective angle of attack from potential flow theory. The local lift curve + # slope is 2pi and the zero-lift angle of attack is given by thin airfoil theory. From this, + # the effective angle of attack is computed for the section and includes 3D effects. + aoa_0cl = get_aoacl0_from_camber(airfoil_coords[:, 0], airfoil_coords[:, 1]) + aoa = cl / 2 / np.pi + aoa_0cl + # Compute the coefficients associated to that angle of attack + cl_polar, cd, cm = polar.get_coefs(aoa) + + if correct_lift: + # Use polar generated CL rather than UVLM computed CL + cl = cl_polar + + # Recompute the forces based on the coefficients (side force is uncorrected) + forces_s[0] += cd * coef # add viscous drag to induced drag from UVLM + forces_s[2] = cl * coef + + new_struct_forces[inode, 0:3] = c_bs.dot(forces_s) + + # Pitching moment + # The panels are shifted by 0.25 of a panel aft from the leading edge + panel_shift = 0.25 * (aero_kstep.zeta[isurf][:, 1, i_n] - aero_kstep.zeta[isurf][:, 0, i_n]) + ref_point = aero_kstep.zeta[isurf][:, 0, i_n] + 0.25 * chord * dir_chord - panel_shift + + # viscous contribution (pure moment) + moment_s[1] += cm * coef * chord + + # moment due to drag + arm = cgb.T.dot(ref_point - pos_g[inode]) # in B frame + moment_polar_drag = algebra.cross3(c_bs.T.dot(arm), cd * dir_urel * coef) # in S frame + moment_s += moment_polar_drag + + # moment due to lift (if corrected) + if correct_lift and moment_from_polar: + # add moment from scratch: cm_polar + cm_drag_polar + cl_lift_polar + moment_s = np.zeros(3) + moment_s[1] = cm * coef * chord + moment_s += moment_polar_drag + moment_polar_lift = algebra.cross3(c_bs.T.dot(arm), forces_s[2] * np.array([0, 0, 1])) + moment_s += moment_polar_lift + + new_struct_forces[inode, 3:6] = c_bs.dot(moment_s) + + return new_struct_forces + + +@generator_interface.generator +class EfficiencyCorrection(generator_interface.BaseGenerator): + """ + The efficiency and constant terms are introduced by means of the array ``airfoil_efficiency`` in the ``aero.h5`` + + .. math:: + \mathbf{f}_{struct}^B &= \varepsilon^f_0 \mathbf{f}_{i,struct}^B + \varepsilon^f_1\\ + \mathbf{m}_{struct}^B &= \varepsilon^m_0 \mathbf{m}_{i,struct}^B + \varepsilon^m_1 + + Notice that the moment correction is applied on top of the force correction. As a consequence, the aerodynamic + moments generated by the forces on the vertices are corrected sequentially by both efficiencies. + + See Also: + The SHARPy case files documentation for a detailed overview on how to include the airfoil efficiencies. + + Returns: + np.ndarray: corresponding aerodynamic force at the structural node from the force and moment at a grid vertex + """ + generator_id = 'EfficiencyCorrection' + + settings_types = dict() + settings_default = dict() + + def __init__(self): + self.aero = None + self.structure = None + + def initialise(self, in_dict, **kwargs): + self.aero = kwargs.get('aero') + self.structure = kwargs.get('structure') + + def generate(self, **params): + """ + Keyword Args: + aero_kstep (:class:`sharpy.utils.datastructures.AeroTimeStepInfo`): Current aerodynamic substep + structural_kstep (:class:`sharpy.utils.datastructures.StructTimeStepInfo`): Current structural substep + struct_forces (np.array): Array with the aerodynamic forces mapped on the structure in the B frame of + reference + + Returns: + np.array: New corrected structural forces + """ + struct_forces = params['struct_forces'] + + n_node = self.structure.num_node + n_elem = self.structure.num_elem + aero_dict = self.aero.aero_dict + new_struct_forces = np.zeros_like(struct_forces) + + # load airfoil efficiency (if it exists); else set to one (to avoid multiple ifs in the loops) + airfoil_efficiency = aero_dict['airfoil_efficiency'] + # force efficiency dimensions [n_elem, n_node_elem, 2, [fx, fy, fz]] - all defined in B frame + force_efficiency = np.zeros((n_elem, 3, 2, 3)) + force_efficiency[:, :, 0, :] = 1. + force_efficiency[:, :, :, 1] = airfoil_efficiency[:, :, :, 0] + force_efficiency[:, :, :, 2] = airfoil_efficiency[:, :, :, 1] + + # moment efficiency dimensions [n_elem, n_node_elem, 2, [mx, my, mz]] - all defined in B frame + moment_efficiency = np.zeros((n_elem, 3, 2, 3)) + moment_efficiency[:, :, 0, :] = 1. + moment_efficiency[:, :, :, 0] = airfoil_efficiency[:, :, :, 2] + + for inode in range(n_node): + i_elem, i_local_node = self.structure.node_master_elem[inode] + new_struct_forces[inode, :] = struct_forces[inode, :].copy() + new_struct_forces[inode, 0:3] *= force_efficiency[i_elem, i_local_node, 0, :] # element wise multiplication + new_struct_forces[inode, 0:3] += force_efficiency[i_elem, i_local_node, 1, :] + new_struct_forces[inode, 3:6] *= moment_efficiency[i_elem, i_local_node, 0, :] + new_struct_forces[inode, 3:6] += moment_efficiency[i_elem, i_local_node, 1, :] + return new_struct_forces From 59fda53d69b56a97e503ee39364577408a2511c7 Mon Sep 17 00:00:00 2001 From: sduess Date: Wed, 10 Nov 2021 16:30:46 +0000 Subject: [PATCH 076/232] update: latest version of UVLM submodule --- lib/UVLM | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/UVLM b/lib/UVLM index 80feb4948..049336d7b 160000 --- a/lib/UVLM +++ b/lib/UVLM @@ -1 +1 @@ -Subproject commit 80feb4948f2445183377537acc5ce0e51822f3cd +Subproject commit 049336d7bfec73bc54c292c23cdfd780e0094720 From 783827a008f06627f123910e87fbeb0897b3fb64 Mon Sep 17 00:00:00 2001 From: sduess Date: Thu, 11 Nov 2021 09:50:47 +0000 Subject: [PATCH 077/232] fix: add missing whitespaces --- sharpy/aero/models/aerogrid.py | 2 +- sharpy/solvers/staticuvlm.py | 13 +++++++------ 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/sharpy/aero/models/aerogrid.py b/sharpy/aero/models/aerogrid.py index 15ae16eec..c12d90a6f 100644 --- a/sharpy/aero/models/aerogrid.py +++ b/sharpy/aero/models/aerogrid.py @@ -290,7 +290,7 @@ def generate_zeta_timestep_info(self, structure_tstep, aero_tstep, beam, setting # set junction boundary conditions for later phantom cell creation in UVLM if "junction_boundary_condition" in self.data_dict: if np.any(self.data_dict["junction_boundary_condition"] >= 0): - self.generate_phantom_panels_at_junction(aero_tstep) + self.generate_phantom_panels_at_junction(aero_tstep) def generate_phantom_panels_at_junction(self, aero_tstep): diff --git a/sharpy/solvers/staticuvlm.py b/sharpy/solvers/staticuvlm.py index 1fdb984c1..afced63c4 100644 --- a/sharpy/solvers/staticuvlm.py +++ b/sharpy/solvers/staticuvlm.py @@ -169,12 +169,13 @@ def run(self): uvlmlib.vlm_solver_lifting_and_nonlifting_bodies(self.data.aero.timestep_info[self.data.ts], self.data.nonlifting_body.timestep_info[self.data.ts], self.settings) - else: - # generate uext - self.velocity_generator.generate({'zeta': self.data.aero.timestep_info[self.data.ts].zeta, - 'override': True, - 'for_pos': self.data.structure.timestep_info[self.data.ts].for_pos[0:3]}, - self.data.aero.timestep_info[self.data.ts].u_ext) + else: + # generate uext + self.velocity_generator.generate({'zeta': self.data.aero.timestep_info[self.data.ts].zeta, + 'override': True, + 'for_pos': self.data.structure.timestep_info[self.data.ts].for_pos[0:3]}, + self.data.aero.timestep_info[self.data.ts].u_ext) + # grid orientation uvlmlib.vlm_solver(self.data.aero.timestep_info[self.data.ts], From 995c73ebbd5b6ab3382c267951fe3140beaf4f8f Mon Sep 17 00:00:00 2001 From: sduess Date: Thu, 11 Nov 2021 09:51:20 +0000 Subject: [PATCH 078/232] add: script to run static trim case --- run_static_trim.py | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 run_static_trim.py diff --git a/run_static_trim.py b/run_static_trim.py new file mode 100644 index 000000000..7db8e805f --- /dev/null +++ b/run_static_trim.py @@ -0,0 +1,19 @@ +# -*- coding: utf-8 -*- +""" +Created on Mon Oct 12 06:30:29 2020 + +@author: sdues +""" + +import numpy as np +import sharpy +import sharpy.sharpy_main as sharpy_main + + +import sys +# insert at 1, 0 is the script path (or '' in REPL) +sys.path.insert(1, '../01_case_files/') +import generate_flex_op + +route_to_case = '../01_case_files/' +case_data = sharpy_main.main(['', route_to_case + 'flex_op_static_trim.sharpy']) From cc103f9e9c30b4d83e34d51a87c08740f08cc83a Mon Sep 17 00:00:00 2001 From: sduess Date: Thu, 11 Nov 2021 09:53:45 +0000 Subject: [PATCH 079/232] update: UVLM submodule --- lib/UVLM | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/UVLM b/lib/UVLM index 049336d7b..40cb4a99d 160000 --- a/lib/UVLM +++ b/lib/UVLM @@ -1 +1 @@ -Subproject commit 049336d7bfec73bc54c292c23cdfd780e0094720 +Subproject commit 40cb4a99d60b7fd549c9c494f613b6cdf7ffe3ec From cf8afa5ea4ce26a98dcba361a152cf972c987d61 Mon Sep 17 00:00:00 2001 From: sduess Date: Thu, 11 Nov 2021 10:56:37 +0000 Subject: [PATCH 080/232] fix: correct VLM inputs --- sharpy/aero/utils/uvlmlib.py | 1 - 1 file changed, 1 deletion(-) diff --git a/sharpy/aero/utils/uvlmlib.py b/sharpy/aero/utils/uvlmlib.py index 5f2777d92..d8acf91a8 100644 --- a/sharpy/aero/utils/uvlmlib.py +++ b/sharpy/aero/utils/uvlmlib.py @@ -311,7 +311,6 @@ def vlm_solver_lifting_and_nonlifting_bodies(ts_info_lifting, ts_info_nonlifting ts_info_lifting.ct_p_gamma_star, ts_info_lifting.ct_p_forces, ts_info_lifting.ct_p_flag_zeta_phantom, - p_rbm_vel_g, ts_info_nonlifting.ct_p_dimensions, ts_info_nonlifting.ct_p_zeta, ts_info_nonlifting.ct_p_u_ext, From f5a6db940d8d53756e60f0a64ccc3bd001424d05 Mon Sep 17 00:00:00 2001 From: sduess Date: Mon, 15 Nov 2021 15:32:46 +0000 Subject: [PATCH 081/232] update submodule UVLM include latest fixes --- lib/UVLM | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/UVLM b/lib/UVLM index 40cb4a99d..afc8414f8 160000 --- a/lib/UVLM +++ b/lib/UVLM @@ -1 +1 @@ -Subproject commit 40cb4a99d60b7fd549c9c494f613b6cdf7ffe3ec +Subproject commit afc8414f8bdd13bc52fc9f6b0ea49f509f6db353 From 3c1843ce0ab19a98996df0431713c077e341cbe3 Mon Sep 17 00:00:00 2001 From: sduess Date: Mon, 15 Nov 2021 16:42:52 +0000 Subject: [PATCH 082/232] update UVLM --- lib/UVLM | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/UVLM b/lib/UVLM index afc8414f8..921670b79 160000 --- a/lib/UVLM +++ b/lib/UVLM @@ -1 +1 @@ -Subproject commit afc8414f8bdd13bc52fc9f6b0ea49f509f6db353 +Subproject commit 921670b798321ce664c797a1a6f93e34dc601fb8 From 0cb930e5a84bfb7822dd1789149102eab8508a9a Mon Sep 17 00:00:00 2001 From: sduess Date: Mon, 15 Nov 2021 16:52:47 +0000 Subject: [PATCH 083/232] update UVLM - fixed error in template included in phantom.h --- lib/UVLM | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/UVLM b/lib/UVLM index 921670b79..4257d78f2 160000 --- a/lib/UVLM +++ b/lib/UVLM @@ -1 +1 @@ -Subproject commit 921670b798321ce664c797a1a6f93e34dc601fb8 +Subproject commit 4257d78f2d165476b96746d05c03d13709cc9c84 From c511b728b482acd9df732673de18ce3eae2d9c19 Mon Sep 17 00:00:00 2001 From: sduess Date: Mon, 21 Mar 2022 17:51:25 +0000 Subject: [PATCH 084/232] update: xbeam submodule --- lib/xbeam | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/xbeam b/lib/xbeam index 041217e63..56d933b8f 160000 --- a/lib/xbeam +++ b/lib/xbeam @@ -1 +1 @@ -Subproject commit 041217e634504c9d3c0f66192d67176f7ce4c3a5 +Subproject commit 56d933b8f0de6e03615bfc73003c524dfdafc00b From a2bd561bcca2e4cdbf4719aa6a3eacb9d5deb068 Mon Sep 17 00:00:00 2001 From: sduess Date: Wed, 4 May 2022 12:30:14 +0100 Subject: [PATCH 085/232] fix multiple lumped masses defined per node - without the plus, the next defined lumped mass on the same node overwrites the previous defined lumped mass there --- sharpy/structure/models/beam.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sharpy/structure/models/beam.py b/sharpy/structure/models/beam.py index 614531529..ad9f5c38c 100644 --- a/sharpy/structure/models/beam.py +++ b/sharpy/structure/models/beam.py @@ -330,7 +330,7 @@ def add_lumped_mass_to_element(self, i_lumped_node, inertia_tensor, replace=Fals self.elements[i_lumped_master_elem].rbmass[i_lumped_master_node_local, :, :] = ( inertia_tensor) # += necessary in case multiple masses defined per node else: - self.elements[i_lumped_master_elem].rbmass[i_lumped_master_node_local, :, :] = ( + self.elements[i_lumped_master_elem].rbmass[i_lumped_master_node_local, :, :] += ( inertia_tensor) # += necessary in case multiple masses defined per node # def generate_master_structure(self): From b98c3a2b3d5051422fd5cb519b17f7f18adf4526 Mon Sep 17 00:00:00 2001 From: sduess Date: Tue, 23 Aug 2022 22:38:12 +0100 Subject: [PATCH 086/232] fix [aerogridloader]: remove twice read file function - bug introduced in merging process - read_files function called also in parent class leading to updating filename and thus causing an error --- sharpy/solvers/aerogridloader.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/sharpy/solvers/aerogridloader.py b/sharpy/solvers/aerogridloader.py index d7c86ae3a..3124b95ec 100644 --- a/sharpy/solvers/aerogridloader.py +++ b/sharpy/solvers/aerogridloader.py @@ -106,9 +106,6 @@ def initialise(self, data): self.settings_types, self.settings_default, options=self.settings_options) - # read input file (aero) - self.read_files() - wake_shape_generator_type = gen_interface.generator_from_string( self.settings['wake_shape_generator']) self.wake_shape_generator = wake_shape_generator_type() From 4a8a7908fd0d68b774ac7bd6ac507d8adb464611 Mon Sep 17 00:00:00 2001 From: sduess Date: Tue, 23 Aug 2022 22:39:13 +0100 Subject: [PATCH 087/232] remove [datastructure] not used parameters for nonlifting bodies --- sharpy/utils/datastructures.py | 33 --------------------------------- 1 file changed, 33 deletions(-) diff --git a/sharpy/utils/datastructures.py b/sharpy/utils/datastructures.py index f002ac758..cfa0acfb2 100644 --- a/sharpy/utils/datastructures.py +++ b/sharpy/utils/datastructures.py @@ -105,39 +105,6 @@ def __init__(self, dimensions): dimensions[i_surf, 1] + 1), dtype=ct.c_double)) - self.u_ext_star = [] - for i_surf in range(self.n_surf): - self.u_ext_star.append(np.zeros((3, - dimensions_star[i_surf, 0] + 1, - dimensions_star[i_surf, 1] + 1), - dtype=ct.c_double)) - - # allocate gamma and gamma star matrices - self.gamma = [] - for i_surf in range(self.n_surf): - self.gamma.append(np.zeros((dimensions[i_surf, 0], - dimensions[i_surf, 1]), - dtype=ct.c_double)) - - self.gamma_star = [] - for i_surf in range(self.n_surf): - self.gamma_star.append(np.zeros((dimensions_star[i_surf, 0], - dimensions_star[i_surf, 1]), - dtype=ct.c_double)) - - self.gamma_dot = [] - for i_surf in range(self.n_surf): - self.gamma_dot.append(np.zeros((dimensions[i_surf, 0], - dimensions[i_surf, 1]), - dtype=ct.c_double)) - - # Distance from the trailing edge of the wake vertices - self.dist_to_orig = [] - for i_surf in range(self.n_surf): - self.dist_to_orig.append(np.zeros((dimensions_star[i_surf, 0] + 1, - dimensions_star[i_surf, 1] + 1), - dtype=ct.c_double)) - # total forces - written by AeroForcesCalculator self.inertial_steady_forces = np.zeros((self.n_surf, 6)) self.body_steady_forces = np.zeros((self.n_surf, 6)) From 54e19aa2c9b68360ee14911218444a8343bd30f2 Mon Sep 17 00:00:00 2001 From: sduess Date: Tue, 23 Aug 2022 22:43:20 +0100 Subject: [PATCH 088/232] format [datastructure] fixes after big merge --- sharpy/utils/datastructures.py | 66 +++++++++++++++++----------------- 1 file changed, 33 insertions(+), 33 deletions(-) diff --git a/sharpy/utils/datastructures.py b/sharpy/utils/datastructures.py index cfa0acfb2..db9dc3a6e 100644 --- a/sharpy/utils/datastructures.py +++ b/sharpy/utils/datastructures.py @@ -509,14 +509,14 @@ def __init__(self, dimensions, dimensions_star): for i_surf in range(self.n_surf): self.wake_conv_vel.append(np.zeros((dimensions_star[i_surf, 0], dimensions_star[i_surf, 1]), - dtype=ct.c_double)) - - # Junction handling - self.flag_zeta_phantom = np.zeros((1, self.n_surf), - dtype=ct.c_int) - self.control_surface_deflection = np.array([]) - - def copy(self): + dtype=ct.c_double)) + + # Junction handling + self.flag_zeta_phantom = np.zeros((1, self.n_surf), + dtype=ct.c_int) + self.control_surface_deflection = np.array([]) + + def copy(self): return self.create_placeholder(AeroTimeStepInfo(self.dimensions, self.dimensions_star)) def create_placeholder(self, copied): @@ -545,14 +545,14 @@ def create_placeholder(self, copied): for i_surf in range(copied.n_surf): copied.wake_conv_vel[i_surf] = self.wake_conv_vel[i_surf].astype(dtype=ct.c_double, copy=True, order='C') - copied.control_surface_deflection = self.control_surface_deflection.astype(dtype=ct.c_double, copy=True) - - # phantom panel flags - copied.flag_zeta_phantom = self.flag_zeta_phantom.astype(dtype=ct.c_int, copy=True, order='C') - - return copied - - def generate_ctypes_pointers(self): + copied.control_surface_deflection = self.control_surface_deflection.astype(dtype=ct.c_double, copy=True) + + # phantom panel flags + copied.flag_zeta_phantom = self.flag_zeta_phantom.astype(dtype=ct.c_int, copy=True, order='C') + + return copied + + def generate_ctypes_pointers(self): from sharpy.utils.constants import NDIM n_surf = len(self.dimensions) super().generate_ctypes_pointers() @@ -584,16 +584,16 @@ def generate_ctypes_pointers(self): for i_surf in range(self.n_surf): self.ct_dist_to_orig_list.append(self.dist_to_orig[i_surf][:, :].reshape(-1)) - self.ct_wake_conv_vel_list = [] - for i_surf in range(self.n_surf): - self.ct_wake_conv_vel_list.append(self.wake_conv_vel[i_surf][:, :].reshape(-1)) - self.ct_flag_zeta_phantom_list = self.flag_zeta_phantom[:].reshape(-1) - - - - self.ct_p_dimensions_star = ((ct.POINTER(ct.c_uint)*n_surf) - (* np.ctypeslib.as_ctypes(self.ct_dimensions_star))) - self.ct_p_zeta_star = ((ct.POINTER(ct.c_double)*len(self.ct_zeta_star_list)) + self.ct_wake_conv_vel_list = [] + for i_surf in range(self.n_surf): + self.ct_wake_conv_vel_list.append(self.wake_conv_vel[i_surf][:, :].reshape(-1)) + self.ct_flag_zeta_phantom_list = self.flag_zeta_phantom[:].reshape(-1) + + + + self.ct_p_dimensions_star = ((ct.POINTER(ct.c_uint)*n_surf) + (* np.ctypeslib.as_ctypes(self.ct_dimensions_star))) + self.ct_p_zeta_star = ((ct.POINTER(ct.c_double)*len(self.ct_zeta_star_list)) (* [np.ctypeslib.as_ctypes(array) for array in self.ct_zeta_star_list])) self.ct_p_u_ext_star = ((ct.POINTER(ct.c_double)*len(self.ct_u_ext_star_list)) (* [np.ctypeslib.as_ctypes(array) for array in self.ct_u_ext_star_list])) @@ -604,13 +604,13 @@ def generate_ctypes_pointers(self): self.ct_p_gamma_star = ((ct.POINTER(ct.c_double)*len(self.ct_gamma_star_list)) (* [np.ctypeslib.as_ctypes(array) for array in self.ct_gamma_star_list])) self.ct_p_dist_to_orig = ((ct.POINTER(ct.c_double)*len(self.ct_dist_to_orig_list)) - (* [np.ctypeslib.as_ctypes(array) for array in self.ct_dist_to_orig_list])) - self.ct_p_wake_conv_vel = ((ct.POINTER(ct.c_double)*len(self.ct_wake_conv_vel_list)) - (* [np.ctypeslib.as_ctypes(array) for array in self.ct_wake_conv_vel_list])) - self.ct_p_flag_zeta_phantom = np.ctypeslib.as_ctypes(self.ct_flag_zeta_phantom_list) - - - def remove_ctypes_pointers(self): + (* [np.ctypeslib.as_ctypes(array) for array in self.ct_dist_to_orig_list])) + self.ct_p_wake_conv_vel = ((ct.POINTER(ct.c_double)*len(self.ct_wake_conv_vel_list)) + (* [np.ctypeslib.as_ctypes(array) for array in self.ct_wake_conv_vel_list])) + self.ct_p_flag_zeta_phantom = np.ctypeslib.as_ctypes(self.ct_flag_zeta_phantom_list) + + + def remove_ctypes_pointers(self): super().remove_ctypes_pointers() try: del self.ct_p_dimensions_star From 35ba93845245c8be37b8ef75b5219b37e0b6b16e Mon Sep 17 00:00:00 2001 From: sduess Date: Tue, 23 Aug 2022 22:45:30 +0100 Subject: [PATCH 089/232] fix [dynamiccoupled] typos in setting name --- sharpy/solvers/dynamiccoupled.py | 34 ++++++++++++++++---------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/sharpy/solvers/dynamiccoupled.py b/sharpy/solvers/dynamiccoupled.py index 14f7c0509..20e066f69 100644 --- a/sharpy/solvers/dynamiccoupled.py +++ b/sharpy/solvers/dynamiccoupled.py @@ -179,9 +179,9 @@ class DynamicCoupled(BaseSolver): 'needed by each generator.' - settings_types['nonlifting_body_interaction'] = 'bool' - settings_default['nonlifting_body_interaction'] = True #False - settings_description['nonlifting_body_interaction'] = 'Effect of Nonlifting Bodies on Lifting bodies are considered' + settings_types['nonlifting_body_interactions'] = 'bool' + settings_default['nonlifting_body_interactions'] = False + settings_description['nonlifting_body_interactions'] = 'Effect of Nonlifting Bodies on Lifting bodies are considered' settings_table = settings.SettingsTable() __doc__ += settings_table.generate(settings_types, settings_default, settings_description, settings_options) @@ -494,7 +494,7 @@ def time_loop(self, in_queue=None, out_queue=None, finish_event=None): structural_kstep = self.data.structure.timestep_info[-1].copy() aero_kstep = self.data.aero.timestep_info[-1].copy() - if self.settings['nonlifting_body_interaction']: + if self.settings['nonlifting_body_interactions']: nl_body_kstep = self.data.nonlifting_body.timestep_info[-1].copy() else: nl_body_kstep = None @@ -587,7 +587,7 @@ def time_loop(self, in_queue=None, out_queue=None, finish_event=None): # run the solver ini_time_aero = time.perf_counter() - if self.settings['nonlifting_body_interaction']: + if self.settings['nonlifting_body_interactions']: self.data = self.aero_solver.run(aero_kstep, structural_kstep, convect_wake=True, @@ -611,7 +611,7 @@ def time_loop(self, in_queue=None, out_queue=None, finish_event=None): structural_kstep, aero_kstep, nl_body_kstep) - if self.settings['nonlifting_body_interaction']: + if self.settings['nonlifting_body_interactions']: self.map_forces(aero_kstep, structural_kstep, nl_body_kstep = nl_body_kstep, @@ -815,17 +815,17 @@ def map_forces(self, aero_kstep, structural_kstep, nl_body_kstep = None, unstead structural_kstep.postproc_node['aero_steady_forces'] = struct_forces structural_kstep.postproc_node['aero_unsteady_forces'] = dynamic_struct_forces - if self.settings['nonlifting_body_interaction']: - struct_forces += mapping.aero2struct_force_mapping( - nl_body_kstep.forces, - self.data.nonlifting_body.struct2aero_mapping, - nl_body_kstep.zeta, - structural_kstep.pos, - structural_kstep.psi, - self.data.structure.node_master_elem, - self.data.structure.connectivities, - structural_kstep.cag(), - self.data.nonlifting_body.data_dict) + # if self.settings['nonlifting_body_interactions']: + # struct_forces += mapping.aero2struct_force_mapping( + # nl_body_kstep.forces, + # self.data.nonlifting_body.struct2aero_mapping, + # nl_body_kstep.zeta, + # structural_kstep.pos, + # structural_kstep.psi, + # self.data.structure.node_master_elem, + # self.data.structure.connectivities, + # structural_kstep.cag(), + # self.data.nonlifting_body.data_dict) # prescribed forces + aero forces structural_kstep.steady_applied_forces = ( (struct_forces + self.data.structure.ini_info.steady_applied_forces). From fa88ac6cc1c8e02e6e2919c8947057dda819e705 Mon Sep 17 00:00:00 2001 From: sduess Date: Tue, 23 Aug 2022 22:45:58 +0100 Subject: [PATCH 090/232] format [dynamiccoupled] fixes after big merge --- sharpy/solvers/dynamiccoupled.py | 74 ++++++++++++++++---------------- 1 file changed, 37 insertions(+), 37 deletions(-) diff --git a/sharpy/solvers/dynamiccoupled.py b/sharpy/solvers/dynamiccoupled.py index 20e066f69..5ba671c3f 100644 --- a/sharpy/solvers/dynamiccoupled.py +++ b/sharpy/solvers/dynamiccoupled.py @@ -493,14 +493,14 @@ def time_loop(self, in_queue=None, out_queue=None, finish_event=None): self.set_of_variables.update_timestep(self.data, values) structural_kstep = self.data.structure.timestep_info[-1].copy() - aero_kstep = self.data.aero.timestep_info[-1].copy() + aero_kstep = self.data.aero.timestep_info[-1].copy() if self.settings['nonlifting_body_interactions']: - nl_body_kstep = self.data.nonlifting_body.timestep_info[-1].copy() - else: - nl_body_kstep = None - self.logger.debug('Time step {}'.format(self.data.ts)) - - # Add the controller here + nl_body_kstep = self.data.nonlifting_body.timestep_info[-1].copy() + else: + nl_body_kstep = None + self.logger.debug('Time step {}'.format(self.data.ts)) + + # Add the controller here if self.with_controllers: state = {'structural': structural_kstep, 'aero': aero_kstep} @@ -546,23 +546,23 @@ def time_loop(self, in_queue=None, out_queue=None, finish_event=None): self.settings['fsi_substeps']): print_res = 0 if self.res == 0. else np.log10(self.res) print_res_dqdt = 0 if self.res_dqdt == 0. else np.log10(self.res_dqdt) - cout.cout_wrap(("The FSI solver did not converge!!! residuals: %f %f" % (print_res, print_res_dqdt))) - self.aero_solver.update_custom_grid( - structural_kstep, - aero_kstep, - nl_body_kstep) - break - - # generate new grid (already rotated) - aero_kstep = controlled_aero_kstep.copy() - - self.aero_solver.update_custom_grid( - structural_kstep, - aero_kstep, - nl_body_kstep) - - # compute unsteady contribution - force_coeff = 0.0 + cout.cout_wrap(("The FSI solver did not converge!!! residuals: %f %f" % (print_res, print_res_dqdt))) + self.aero_solver.update_custom_grid( + structural_kstep, + aero_kstep, + nl_body_kstep) + break + + # generate new grid (already rotated) + aero_kstep = controlled_aero_kstep.copy() + + self.aero_solver.update_custom_grid( + structural_kstep, + aero_kstep, + nl_body_kstep) + + # compute unsteady contribution + force_coeff = 0.0 unsteady_contribution = False if self.settings['include_unsteady_force_contribution']: if self.data.ts > self.settings['steps_without_unsteady_force']: @@ -607,10 +607,10 @@ def time_loop(self, in_queue=None, out_queue=None, finish_event=None): previous_kstep.runtime_generated_forces = previous_runtime_generated_forces.astype(dtype=ct.c_double, order='F', copy=True) # move the aerodynamic surface according the the structural one - self.aero_solver.update_custom_grid( - structural_kstep, - aero_kstep, - nl_body_kstep) + self.aero_solver.update_custom_grid( + structural_kstep, + aero_kstep, + nl_body_kstep) if self.settings['nonlifting_body_interactions']: self.map_forces(aero_kstep, structural_kstep, @@ -650,8 +650,8 @@ def time_loop(self, in_queue=None, out_queue=None, finish_event=None): coeff=coeff) self.data = self.structural_solver.run( - structural_step=structural_kstep) #, - # dt=self.substep_dt) + structural_step=structural_kstep) #, + # dt=self.substep_dt) self.time_struc += time.perf_counter() - ini_time_struc @@ -663,19 +663,19 @@ def time_loop(self, in_queue=None, out_queue=None, finish_event=None): self.settings['aero_solver'].lower(), self.with_runtime_generators): # move the aerodynamic surface according to the structural one - self.aero_solver.update_custom_grid(structural_kstep, - aero_kstep, - nl_body_tstep = nl_body_kstep) + self.aero_solver.update_custom_grid(structural_kstep, + aero_kstep, + nl_body_tstep = nl_body_kstep) break # move the aerodynamic surface according the the structural one - self.aero_solver.update_custom_grid(structural_kstep, - aero_kstep, - nl_body_tstep = nl_body_kstep) + self.aero_solver.update_custom_grid(structural_kstep, + aero_kstep, + nl_body_tstep = nl_body_kstep) self.aero_solver.add_step() self.data.aero.timestep_info[-1] = aero_kstep.copy() - self.data.nonlifting_body.timestep_info[-1] = nl_body_kstep.copy() + self.data.nonlifting_body.timestep_info[-1] = nl_body_kstep.copy() self.structural_solver.add_step() self.data.structure.timestep_info[-1] = structural_kstep.copy() From 787324f70ed805cbff7ac08a1b4dbec6e4b1b34d Mon Sep 17 00:00:00 2001 From: sduess Date: Tue, 23 Aug 2022 22:47:54 +0100 Subject: [PATCH 091/232] fix [uvlmlib]: add missing UVLM inputs --- sharpy/aero/utils/uvlmlib.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/sharpy/aero/utils/uvlmlib.py b/sharpy/aero/utils/uvlmlib.py index 62f001144..296b55843 100644 --- a/sharpy/aero/utils/uvlmlib.py +++ b/sharpy/aero/utils/uvlmlib.py @@ -302,6 +302,7 @@ def vlm_solver_lifting_and_nonlifting_bodies(ts_info_lifting, ts_info_nonlifting flightconditions.uinf_direction = np.ctypeslib.as_ctypes(ts_info_lifting.u_ext[0][:, 0, 0]/flightconditions.uinf) p_rbm_vel_g = options['rbm_vel_g'].ctypes.data_as(ct.POINTER(ct.c_double)) + p_centre_rot = options['centre_rot_g'].ctypes.data_as(ct.POINTER(ct.c_double)) ts_info_lifting.generate_ctypes_pointers() ts_info_nonlifting.generate_ctypes_pointers() run_VLM_lifting_and_nonlifting(ct.byref(vmopts), @@ -320,7 +321,9 @@ def vlm_solver_lifting_and_nonlifting_bodies(ts_info_lifting, ts_info_nonlifting ts_info_nonlifting.ct_p_zeta, ts_info_nonlifting.ct_p_u_ext, ts_info_nonlifting.ct_p_sigma, - ts_info_nonlifting.ct_p_forces) + ts_info_nonlifting.ct_p_forces, + p_rbm_vel_g, + p_centre_rot) ts_info_lifting.remove_ctypes_pointers() ts_info_nonlifting.remove_ctypes_pointers() @@ -437,7 +440,9 @@ def uvlm_solver_lifting_and_nonlifting(i_iter, ts_info, ts_info_nonlifting, stru ts_info_nonlifting.ct_p_zeta, ts_info_nonlifting.ct_p_u_ext, ts_info_nonlifting.ct_p_sigma, - ts_info_nonlifting.ct_p_forces) + ts_info_nonlifting.ct_p_forces, + p_rbm_vel, + p_centre_rot) ts_info.remove_ctypes_pointers() ts_info_nonlifting.remove_ctypes_pointers() From d3eb4e4235e5a8318701fe30257e17e395ac5db0 Mon Sep 17 00:00:00 2001 From: sduess Date: Wed, 24 Aug 2022 13:34:01 +0100 Subject: [PATCH 092/232] rename parameters for latest develop code updates - changed aero_dimensions to dimension and aero_dict to data_dict when adding fuselage module to reuse parameters from parent class - no renaming parameters in code added in the latest merges into develop --- sharpy/aero/models/aerogrid.py | 2 +- sharpy/generators/polaraeroforces.py | 14 +++++++------- .../linear/assembler/lincontrolsurfacedeflector.py | 4 ++-- sharpy/linear/assembler/lineargustassembler.py | 4 ++-- sharpy/linear/assembler/linearuvlm.py | 4 ++-- sharpy/postproc/liftdistribution.py | 2 +- sharpy/structure/utils/modalutils.py | 4 ++-- 7 files changed, 17 insertions(+), 17 deletions(-) diff --git a/sharpy/aero/models/aerogrid.py b/sharpy/aero/models/aerogrid.py index aa4032ba6..5da86c0f1 100644 --- a/sharpy/aero/models/aerogrid.py +++ b/sharpy/aero/models/aerogrid.py @@ -113,7 +113,7 @@ def generate(self, data_dict, beam, settings, ts): nairfoils = np.amax(self.data_dict['airfoil_distribution']) + 1 for iairfoil in range(nairfoils): new_polar = ap.Polar() - new_polar.initialise(aero_dict['polars'][str(iairfoil)]) + new_polar.initialise(data_dict['polars'][str(iairfoil)]) self.polars.append(new_polar) def output_info(self): diff --git a/sharpy/generators/polaraeroforces.py b/sharpy/generators/polaraeroforces.py index 5bf451642..b8dc2a8e9 100644 --- a/sharpy/generators/polaraeroforces.py +++ b/sharpy/generators/polaraeroforces.py @@ -114,7 +114,7 @@ def generate(self, **params): cd_from_cl = self.settings['cd_from_cl'] moment_from_polar = self.settings['moment_from_polar'] - aero_dict = aerogrid.aero_dict + data_dict = aerogrid.data_dict if aerogrid.polars is None: return struct_forces new_struct_forces = np.zeros_like(struct_forces) @@ -127,18 +127,18 @@ def generate(self, **params): for inode in range(nnode): new_struct_forces[inode, :] = struct_forces[inode, :].copy() - if aero_dict['aero_node'][inode]: + if data_dict['aero_node'][inode]: ielem, inode_in_elem = structure.node_master_elem[inode] - iairfoil = aero_dict['airfoil_distribution'][ielem, inode_in_elem] + iairfoil = data_dict['airfoil_distribution'][ielem, inode_in_elem] isurf = aerogrid.struct2aero_mapping[inode][0]['i_surf'] i_n = aerogrid.struct2aero_mapping[inode][0]['i_n'] - N = aerogrid.aero_dimensions[isurf, 1] + N = aerogrid.dimensions[isurf, 1] polar = aerogrid.polars[iairfoil] cab = algebra.crv2rotation(structural_kstep.psi[ielem, inode_in_elem, :]) cgb = np.dot(cga, cab) if not cd_from_cl: - airfoil_coords = aerogrid.aero_dict['airfoils'][str(aero_dict['airfoil_distribution'][ielem, inode_in_elem])] + airfoil_coords = aerogrid.data_dict['airfoils'][str(data_dict['airfoil_distribution'][ielem, inode_in_elem])] dir_span, span, dir_chord, chord = span_chord(i_n, aero_kstep.zeta[isurf]) @@ -260,11 +260,11 @@ def generate(self, **params): n_node = self.structure.num_node n_elem = self.structure.num_elem - aero_dict = self.aero.aero_dict + data_dict = self.aero.data_dict new_struct_forces = np.zeros_like(struct_forces) # load airfoil efficiency (if it exists); else set to one (to avoid multiple ifs in the loops) - airfoil_efficiency = aero_dict['airfoil_efficiency'] + airfoil_efficiency = data_dict['airfoil_efficiency'] # force efficiency dimensions [n_elem, n_node_elem, 2, [fx, fy, fz]] - all defined in B frame force_efficiency = np.zeros((n_elem, 3, 2, 3)) force_efficiency[:, :, 0, :] = 1. diff --git a/sharpy/linear/assembler/lincontrolsurfacedeflector.py b/sharpy/linear/assembler/lincontrolsurfacedeflector.py index 1469c9cf9..2f99fda4b 100644 --- a/sharpy/linear/assembler/lincontrolsurfacedeflector.py +++ b/sharpy/linear/assembler/lincontrolsurfacedeflector.py @@ -125,8 +125,8 @@ def generate(self): continue # Surface panelling - M = aero.aero_dimensions[i_surf][0] - N = aero.aero_dimensions[i_surf][1] + M = aero.dimensions[i_surf][0] + N = aero.dimensions[i_surf][1] K_zeta_start = 3 * sum(linuvlm.MS.KKzeta[:i_surf]) shape_zeta = (3, M + 1, N + 1) diff --git a/sharpy/linear/assembler/lineargustassembler.py b/sharpy/linear/assembler/lineargustassembler.py index 3a02b4838..646672529 100644 --- a/sharpy/linear/assembler/lineargustassembler.py +++ b/sharpy/linear/assembler/lineargustassembler.py @@ -250,7 +250,7 @@ def assemble(self): c_i = np.zeros((3 * Kzeta, N)) for i_surf in range(self.aero.n_surf): - M_surf, N_surf = self.aero.aero_dimensions[i_surf] + M_surf, N_surf = self.aero.dimensions[i_surf] Kzeta_start = 3 * sum(self.KKzeta[:i_surf]) # number of coordinates up to current surface shape_zeta = (3, M_surf + 1, N_surf + 1) @@ -341,7 +341,7 @@ def assemble(self): gust_d = np.zeros((gust_c.shape[0], gust_b.shape[1])) for i_surf in range(self.aero.n_surf): - M_surf, N_surf = self.aero.aero_dimensions[i_surf] + M_surf, N_surf = self.aero.dimensions[i_surf] Kzeta_start = 3 * sum(self.KKzeta[:i_surf]) # number of coordinates up to current surface shape_zeta = (3, M_surf + 1, N_surf + 1) diff --git a/sharpy/linear/assembler/linearuvlm.py b/sharpy/linear/assembler/linearuvlm.py index b9b20e277..59df15366 100644 --- a/sharpy/linear/assembler/linearuvlm.py +++ b/sharpy/linear/assembler/linearuvlm.py @@ -425,8 +425,8 @@ def unpack_ss_vector(self, data, x_n, u_aero, aero_tstep, track_body=False, stat for i_surf in range(aero_tstep.n_surf): # Tuple with dimensions of the aerogrid zeta, which is the same shape for forces dimensions = aero_tstep.zeta[i_surf].shape - dimensions_gamma = data.aero.aero_dimensions[i_surf] - dimensions_wake = data.aero.aero_dimensions_star[i_surf] + dimensions_gamma = data.aero.dimensions[i_surf] + dimensions_wake = data.aero.dimensions_star[i_surf] # Number of entries in zeta points_in_surface = aero_tstep.zeta[i_surf].size diff --git a/sharpy/postproc/liftdistribution.py b/sharpy/postproc/liftdistribution.py index ca47afd17..596f64481 100644 --- a/sharpy/postproc/liftdistribution.py +++ b/sharpy/postproc/liftdistribution.py @@ -85,7 +85,7 @@ def lift_distribution(self, struct_tstep, aero_tstep): lift_distribution = np.concatenate((lift_distribution, np.zeros((N_nodes, 2))), axis=1) for inode in range(N_nodes): - if self.data.aero.aero_dict['aero_node'][inode]: + if self.data.aero.data_dict['aero_node'][inode]: local_node = self.data.aero.struct2aero_mapping[inode][0]["i_n"] ielem, inode_in_elem = self.data.structure.node_master_elem[inode] i_surf = int(self.data.aero.surface_distribution[ielem]) diff --git a/sharpy/structure/utils/modalutils.py b/sharpy/structure/utils/modalutils.py index bdcbd7781..f16353c26 100644 --- a/sharpy/structure/utils/modalutils.py +++ b/sharpy/structure/utils/modalutils.py @@ -166,8 +166,8 @@ def get_mode_zeta(data, eigvect): # print('%.2d,%.2d'%(nn,ss)) # surface panelling - M = aero.aero_dimensions[ss][0] - N = aero.aero_dimensions[ss][1] + M = aero.dimensions[ss][0] + N = aero.dimensions[ss][1] for mm in range(M + 1): # get position of vertex in B FoR From df9e96b22d4eaf93780c0a2ce18af838160c6fe5 Mon Sep 17 00:00:00 2001 From: sduess Date: Thu, 25 Aug 2022 08:12:43 +0100 Subject: [PATCH 093/232] fix [uvlmlib] add missing inputs for UVLM solver --- sharpy/aero/utils/uvlmlib.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/sharpy/aero/utils/uvlmlib.py b/sharpy/aero/utils/uvlmlib.py index 296b55843..b47bde931 100644 --- a/sharpy/aero/utils/uvlmlib.py +++ b/sharpy/aero/utils/uvlmlib.py @@ -378,7 +378,9 @@ def uvlm_solver(i_iter, ts_info, struct_ts_info, options, convect_wake=True, dt= # previous_ts_info.ct_p_gamma, ts_info.ct_p_normals, ts_info.ct_p_forces, - ts_info.ct_p_dynamic_forces) + ts_info.ct_p_dynamic_forces, + p_rbm_vel, + p_centre_rot) ts_info.remove_ctypes_pointers() # previous_ts_info.remove_ctypes_pointers() From 76a35616fb169d9462cad14621ceac886edce6e2 Mon Sep 17 00:00:00 2001 From: sduess Date: Thu, 25 Aug 2022 08:13:28 +0100 Subject: [PATCH 094/232] fix rename parameters after merge --- sharpy/linear/assembler/linearaeroelastic.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sharpy/linear/assembler/linearaeroelastic.py b/sharpy/linear/assembler/linearaeroelastic.py index 752654cd1..0dfdfd1ae 100644 --- a/sharpy/linear/assembler/linearaeroelastic.py +++ b/sharpy/linear/assembler/linearaeroelastic.py @@ -680,8 +680,8 @@ def get_gebm2uvlm_gains(self, data): # print('%.2d,%.2d'%(nn,ss)) # surface panelling - M = aero.aero_dimensions[ss][0] - N = aero.aero_dimensions[ss][1] + M = aero.dimensions[ss][0] + N = aero.dimensions[ss][1] Kzeta_start = 3 * sum(self.uvlm.sys.MS.KKzeta[:ss]) shape_zeta = (3, M + 1, N + 1) From 58b737fb54f33b069be98dc2abab363cb43cebe7 Mon Sep 17 00:00:00 2001 From: sduess Date: Thu, 25 Aug 2022 08:13:57 +0100 Subject: [PATCH 095/232] remove unused settings in gridloader and subclasses --- sharpy/aero/models/aerogrid.py | 3 +-- sharpy/solvers/aerogridloader.py | 8 -------- sharpy/solvers/gridloader.py | 9 --------- sharpy/solvers/nonliftingbodygridloader.py | 18 ------------------ 4 files changed, 1 insertion(+), 37 deletions(-) diff --git a/sharpy/aero/models/aerogrid.py b/sharpy/aero/models/aerogrid.py index 5da86c0f1..c108d8cc2 100644 --- a/sharpy/aero/models/aerogrid.py +++ b/sharpy/aero/models/aerogrid.py @@ -284,7 +284,6 @@ def generate_zeta_timestep_info(self, structure_tstep, aero_tstep, beam, setting aero_tstep.zeta_dot[i_surf][:, :, i_n]) = ( generate_strip(node_info, self.airfoil_db, - self.aero_settings['aligned_grid'], orientation_in=self.aero_settings['freestream_dir'], calculate_zeta_dot=True)) # set junction boundary conditions for later phantom cell creation in UVLM @@ -368,7 +367,7 @@ def compute_gamma_dot(dt, tstep, previous_tsteps): -def generate_strip(node_info, airfoil_db, aligned_grid, orientation_in=np.array([1, 0, 0]), calculate_zeta_dot = False): +def generate_strip(node_info, airfoil_db, orientation_in=np.array([1, 0, 0]), calculate_zeta_dot = False): """ Returns a strip of panels in ``A`` frame of reference, it has to be then rotated to simulate angles of attack, etc diff --git a/sharpy/solvers/aerogridloader.py b/sharpy/solvers/aerogridloader.py index 3124b95ec..a75c56a53 100644 --- a/sharpy/solvers/aerogridloader.py +++ b/sharpy/solvers/aerogridloader.py @@ -51,14 +51,6 @@ class AerogridLoader(GridLoader): settings_description = dict() settings_options = dict() - settings_types['unsteady'] = 'bool' - settings_default['unsteady'] = False - settings_description['unsteady'] = 'Unsteady effects' - - settings_types['aligned_grid'] = 'bool' - settings_default['aligned_grid'] = True - settings_description['aligned_grid'] = 'Align grid' - settings_types['freestream_dir'] = 'list(float)' settings_default['freestream_dir'] = [1.0, 0.0, 0.0] settings_description['freestream_dir'] = 'Free stream flow direction' diff --git a/sharpy/solvers/gridloader.py b/sharpy/solvers/gridloader.py index 448c534f2..0c644c643 100644 --- a/sharpy/solvers/gridloader.py +++ b/sharpy/solvers/gridloader.py @@ -30,10 +30,6 @@ class GridLoader(BaseSolver): solver_id = 'GridLoader' solver_classification = 'loader' - settings_types = dict() - settings_default = dict() - settings_description = dict() - def __init__(self): self.data = None self.settings = None @@ -48,11 +44,6 @@ def initialise(self, data): self.data = data self.settings = data.settings[self.solver_id] - # init settings - settings_utils.to_custom_types(self.settings, - self.settings_types, - self.settings_default) - # read input file self.read_files() diff --git a/sharpy/solvers/nonliftingbodygridloader.py b/sharpy/solvers/nonliftingbodygridloader.py index a2e8667e3..68be0c469 100644 --- a/sharpy/solvers/nonliftingbodygridloader.py +++ b/sharpy/solvers/nonliftingbodygridloader.py @@ -28,24 +28,6 @@ class NonliftingbodygridLoader(GridLoader): solver_id = 'NonliftingbodygridLoader' solver_classification = 'loader' - settings_types = dict() - settings_default = dict() - settings_description = dict() - - settings_types['unsteady'] = 'bool' - settings_default['unsteady'] = False - settings_description['unsteady'] = 'Unsteady effects' - - settings_types['aligned_grid'] = 'bool' - settings_default['aligned_grid'] = True - settings_description['aligned_grid'] = 'Align grid' - - settings_types['freestream_dir'] = 'list(float)' - settings_default['freestream_dir'] = [1.0, 0.0, 0.0] - settings_description['freestream_dir'] = 'Free stream flow direction' - - settings_table = settings_utils.SettingsTable() - __doc__ += settings_table.generate(settings_types, settings_default, settings_description) def __init__(self): super().__init__() self.file_name = '.nonlifting_body.h5' From 6264d6b3a47a0b873114978e0b0171efa99f1f90 Mon Sep 17 00:00:00 2001 From: sduess Date: Thu, 25 Aug 2022 08:14:25 +0100 Subject: [PATCH 096/232] fix default value of setting --- sharpy/solvers/stepuvlm.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sharpy/solvers/stepuvlm.py b/sharpy/solvers/stepuvlm.py index af563dc11..a91049aa3 100644 --- a/sharpy/solvers/stepuvlm.py +++ b/sharpy/solvers/stepuvlm.py @@ -134,7 +134,7 @@ class StepUvlm(BaseSolver): settings_description['quasi_steady'] = 'Use quasi-steady approximation in UVLM' settings_types['nonlifting_body_interactions'] = 'bool' - settings_default['nonlifting_body_interactions'] = True #False + settings_default['nonlifting_body_interactions'] = False settings_description['nonlifting_body_interactions'] = 'Consider nonlifting body interactions' settings_types['only_nonlifting'] = 'bool' From 5b16f438b6397f1f694dfe6ec5cea601e47e69d9 Mon Sep 17 00:00:00 2001 From: sduess Date: Thu, 25 Aug 2022 08:22:41 +0100 Subject: [PATCH 097/232] fix: keep unused settings for now due to unittest failures --- sharpy/aero/models/aerogrid.py | 3 ++- sharpy/solvers/aerogridloader.py | 8 ++++++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/sharpy/aero/models/aerogrid.py b/sharpy/aero/models/aerogrid.py index c108d8cc2..5da86c0f1 100644 --- a/sharpy/aero/models/aerogrid.py +++ b/sharpy/aero/models/aerogrid.py @@ -284,6 +284,7 @@ def generate_zeta_timestep_info(self, structure_tstep, aero_tstep, beam, setting aero_tstep.zeta_dot[i_surf][:, :, i_n]) = ( generate_strip(node_info, self.airfoil_db, + self.aero_settings['aligned_grid'], orientation_in=self.aero_settings['freestream_dir'], calculate_zeta_dot=True)) # set junction boundary conditions for later phantom cell creation in UVLM @@ -367,7 +368,7 @@ def compute_gamma_dot(dt, tstep, previous_tsteps): -def generate_strip(node_info, airfoil_db, orientation_in=np.array([1, 0, 0]), calculate_zeta_dot = False): +def generate_strip(node_info, airfoil_db, aligned_grid, orientation_in=np.array([1, 0, 0]), calculate_zeta_dot = False): """ Returns a strip of panels in ``A`` frame of reference, it has to be then rotated to simulate angles of attack, etc diff --git a/sharpy/solvers/aerogridloader.py b/sharpy/solvers/aerogridloader.py index a75c56a53..3124b95ec 100644 --- a/sharpy/solvers/aerogridloader.py +++ b/sharpy/solvers/aerogridloader.py @@ -51,6 +51,14 @@ class AerogridLoader(GridLoader): settings_description = dict() settings_options = dict() + settings_types['unsteady'] = 'bool' + settings_default['unsteady'] = False + settings_description['unsteady'] = 'Unsteady effects' + + settings_types['aligned_grid'] = 'bool' + settings_default['aligned_grid'] = True + settings_description['aligned_grid'] = 'Align grid' + settings_types['freestream_dir'] = 'list(float)' settings_default['freestream_dir'] = [1.0, 0.0, 0.0] settings_description['freestream_dir'] = 'Free stream flow direction' From b8c9ea663888115bf4b9bbbfffc7afdce454cb81 Mon Sep 17 00:00:00 2001 From: sduess Date: Thu, 25 Aug 2022 13:51:59 +0100 Subject: [PATCH 098/232] fix: minor changes for unittest - mostly skipping GridLoader and NonliftingbodygridLoader in setting default settings because they do not have any settings available --- sharpy/solvers/aerogridloader.py | 7 ++----- sharpy/solvers/dynamiccoupled.py | 3 ++- sharpy/solvers/gridloader.py | 23 +++++++++------------- sharpy/solvers/nonliftingbodygridloader.py | 2 +- sharpy/solvers/stepuvlm.py | 8 +------- sharpy/utils/generate_cases.py | 5 +++-- sharpy/utils/solver_interface.py | 7 +++++-- 7 files changed, 23 insertions(+), 32 deletions(-) diff --git a/sharpy/solvers/aerogridloader.py b/sharpy/solvers/aerogridloader.py index 3124b95ec..3c6e3353f 100644 --- a/sharpy/solvers/aerogridloader.py +++ b/sharpy/solvers/aerogridloader.py @@ -92,16 +92,13 @@ class AerogridLoader(GridLoader): def __init__(self): super().__init__ self.file_name = '.aero.h5' - - # aero storage self.aero = None - self.wake_shape_generator = None def initialise(self, data): super().initialise(data) - - # init settings + + self.settings = data.settings[self.solver_id] settings_utils.to_custom_types(self.settings, self.settings_types, self.settings_default, options=self.settings_options) diff --git a/sharpy/solvers/dynamiccoupled.py b/sharpy/solvers/dynamiccoupled.py index 5ba671c3f..ef066ca8a 100644 --- a/sharpy/solvers/dynamiccoupled.py +++ b/sharpy/solvers/dynamiccoupled.py @@ -675,7 +675,8 @@ def time_loop(self, in_queue=None, out_queue=None, finish_event=None): self.aero_solver.add_step() self.data.aero.timestep_info[-1] = aero_kstep.copy() - self.data.nonlifting_body.timestep_info[-1] = nl_body_kstep.copy() + if self.settings['nonlifting_body_interactions']: + self.data.nonlifting_body.timestep_info[-1] = nl_body_kstep.copy() self.structural_solver.add_step() self.data.structure.timestep_info[-1] = structural_kstep.copy() diff --git a/sharpy/solvers/gridloader.py b/sharpy/solvers/gridloader.py index 0c644c643..1a4b438ec 100644 --- a/sharpy/solvers/gridloader.py +++ b/sharpy/solvers/gridloader.py @@ -28,36 +28,31 @@ class GridLoader(BaseSolver): """ solver_id = 'GridLoader' - solver_classification = 'loader' + solver_classification = 'other' + + settings_types = dict() + settings_default = dict() + settings_description = dict() def __init__(self): self.data = None self.settings = None self.file_name = '' - # storage of file contents self.data_dict = dict() - # aero storage - #self.aero = None - def initialise(self, data): self.data = data - self.settings = data.settings[self.solver_id] - - # read input file - self.read_files() + self.read_input_files() - def read_files(self): - # first check that the file exists + def read_input_files(self): self.file_name = (self.data.case_route + '/' + self.data.case_name + self.file_name) - # first check that the file exists + h5utils.check_file_exists(self.file_name) - # read and store the hdf5 file + # read and store the hdf5 file in dictionary with h5.File(self.file_name, 'r') as file_handle: - # store files in dictionary self.data_dict = h5utils.load_h5_in_dict(file_handle) \ No newline at end of file diff --git a/sharpy/solvers/nonliftingbodygridloader.py b/sharpy/solvers/nonliftingbodygridloader.py index 68be0c469..44faee5ad 100644 --- a/sharpy/solvers/nonliftingbodygridloader.py +++ b/sharpy/solvers/nonliftingbodygridloader.py @@ -29,7 +29,7 @@ class NonliftingbodygridLoader(GridLoader): solver_classification = 'loader' def __init__(self): - super().__init__() + super().__init__ self.file_name = '.nonlifting_body.h5' # nonlifting_body storage diff --git a/sharpy/solvers/stepuvlm.py b/sharpy/solvers/stepuvlm.py index a91049aa3..fceb7e3f1 100644 --- a/sharpy/solvers/stepuvlm.py +++ b/sharpy/solvers/stepuvlm.py @@ -242,7 +242,6 @@ def run(self, if self.settings['nonlifting_body_interactions']: if nl_body_tstep is None: nl_body_tstep = self.data.nonlifting_body.timestep_info[-1] - # TODO: Extent velocity field generators self.velocity_generator.generate({'zeta': nl_body_tstep.zeta, 'override': True, 'ts': self.data.ts, @@ -259,11 +258,7 @@ def run(self, self.settings, convect_wake=convect_wake, dt=dt) - - # print("UNSTEADY UVLM finsihed in StepUVLM!") - - else: - + else: uvlmlib.uvlm_solver(self.data.ts, aero_tstep, structure_tstep, @@ -293,7 +288,6 @@ def run(self, else: for i_surf in range(len(aero_tstep.gamma)): aero_tstep.gamma_dot[i_surf][:] = 0.0 - # print("Step UVLM finsihed!") return self.data def add_step(self): diff --git a/sharpy/utils/generate_cases.py b/sharpy/utils/generate_cases.py index 9d36be63f..1ca005a30 100644 --- a/sharpy/utils/generate_cases.py +++ b/sharpy/utils/generate_cases.py @@ -1685,9 +1685,7 @@ def set_default_values(self): """ self.solvers = dict() - # cout.start_writer() aux_names = solver_interface.dictionary_of_solvers(print_info=False) - # cout.finish_writer() aux_names.update(generator_interface.dictionary_of_generators(print_info=False)) # TODO: I am sure this can be done in a better way @@ -1697,6 +1695,9 @@ def set_default_values(self): else: solver_name = solver self.solvers[solver_name] = {} + if solver in ['GridLoader', 'NonliftingbodygridLoader']: + # Skip this solver as no default values for GridLoader exist. + continue try: aux_solver = solver_interface.solver_from_string(solver) except: diff --git a/sharpy/utils/solver_interface.py b/sharpy/utils/solver_interface.py index 898e4b70b..ad6291f55 100644 --- a/sharpy/utils/solver_interface.py +++ b/sharpy/utils/solver_interface.py @@ -100,8 +100,11 @@ def dictionary_of_solvers(print_info=True): import sharpy.postproc dictionary = dict() for solver in dict_of_solvers: - init_solver = initialise_solver(solver, print_info) - dictionary[solver] = init_solver.settings_default + if solver not in ['GridLoader', 'NonliftingBodyGridLoader']: + init_solver = initialise_solver(solver, print_info) + dictionary[solver] = init_solver.settings_default + else: + dictionary[solver] = {} return dictionary From 2541c3b7f9e48186e7d111ab9d424d858c7750c7 Mon Sep 17 00:00:00 2001 From: sduess Date: Thu, 25 Aug 2022 13:53:18 +0100 Subject: [PATCH 099/232] update UVLM submodule - includes the fix for the horseshoe bug prior a dynamic simulation --- lib/UVLM | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/UVLM b/lib/UVLM index 3bc5a0c81..3bc064b8f 160000 --- a/lib/UVLM +++ b/lib/UVLM @@ -1 +1 @@ -Subproject commit 3bc5a0c815f2fd5c33160f97cca2260a135e4fe1 +Subproject commit 3bc064b8f53788d82b30152dbff10816e1ce6083 From d703d0448de5e85b9a0c96f24c1b769d9e140987 Mon Sep 17 00:00:00 2001 From: sduess Date: Fri, 28 Oct 2022 11:56:42 +0100 Subject: [PATCH 100/232] add influence of nonlifting-inudced forces on structure in staticcoupled --- sharpy/solvers/staticcoupled.py | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/sharpy/solvers/staticcoupled.py b/sharpy/solvers/staticcoupled.py index 6265739c2..77f63bdf1 100644 --- a/sharpy/solvers/staticcoupled.py +++ b/sharpy/solvers/staticcoupled.py @@ -196,6 +196,17 @@ def run(self): self.data.structure.timestep_info[self.data.ts].cag(), self.data.aero.data_dict) + if self.settings['nonlifting_body_interaction']: + struct_forces += mapping.aero2struct_force_mapping( + self.data.nonlifting_body.timestep_info[self.data.ts].forces, + self.data.nonlifting_body.struct2aero_mapping, + self.data.nonlifting_body.timestep_info[self.data.ts].zeta, + self.data.structure.timestep_info[self.data.ts].pos, + self.data.structure.timestep_info[self.data.ts].psi, + self.data.structure.node_master_elem, + self.data.structure.connectivities, + self.data.structure.timestep_info[self.data.ts].cag(), + self.data.nonlifting_body.data_dict) if self.correct_forces: struct_forces = \ self.correct_forces_generator.generate(aero_kstep=self.data.aero.timestep_info[self.data.ts], From 21f2a240ac53d15a94707926e6fc2d4455481efe Mon Sep 17 00:00:00 2001 From: sduess Date: Fri, 28 Oct 2022 13:20:17 +0100 Subject: [PATCH 101/232] fix [gridloader] set settings in parent class - lead to error since nonlifting body gridloader did not have defined settings --- sharpy/solvers/aerogridloader.py | 5 ----- sharpy/solvers/gridloader.py | 6 ++++++ 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/sharpy/solvers/aerogridloader.py b/sharpy/solvers/aerogridloader.py index 20beb62cd..70e2d71c5 100644 --- a/sharpy/solvers/aerogridloader.py +++ b/sharpy/solvers/aerogridloader.py @@ -97,11 +97,6 @@ def __init__(self): def initialise(self, data): super().initialise(data) - - self.settings = data.settings[self.solver_id] - settings_utils.to_custom_types(self.settings, - self.settings_types, - self.settings_default, options=self.settings_options) wake_shape_generator_type = gen_interface.generator_from_string( self.settings['wake_shape_generator']) diff --git a/sharpy/solvers/gridloader.py b/sharpy/solvers/gridloader.py index 1a4b438ec..ee5803faa 100644 --- a/sharpy/solvers/gridloader.py +++ b/sharpy/solvers/gridloader.py @@ -33,6 +33,7 @@ class GridLoader(BaseSolver): settings_types = dict() settings_default = dict() settings_description = dict() + settings_options = dict() def __init__(self): self.data = None @@ -43,6 +44,11 @@ def __init__(self): def initialise(self, data): self.data = data self.read_input_files() + + self.settings = data.settings[self.solver_id] + settings_utils.to_custom_types(self.settings, + self.settings_types, + self.settings_default, options=self.settings_options) def read_input_files(self): From b25ca061d1c642690dfce9e0915f62f9083023d4 Mon Sep 17 00:00:00 2001 From: sduess Date: Thu, 22 Dec 2022 14:22:41 +0000 Subject: [PATCH 102/232] rename [nl_body] class and file for nonliftingbodygrid --- .../models/{nonlifting_body_grid.py => nonliftingbodygrid.py} | 2 +- sharpy/solvers/nonliftingbodygridloader.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) rename sharpy/aero/models/{nonlifting_body_grid.py => nonliftingbodygrid.py} (99%) diff --git a/sharpy/aero/models/nonlifting_body_grid.py b/sharpy/aero/models/nonliftingbodygrid.py similarity index 99% rename from sharpy/aero/models/nonlifting_body_grid.py rename to sharpy/aero/models/nonliftingbodygrid.py index 2808d995d..465b87d68 100644 --- a/sharpy/aero/models/nonlifting_body_grid.py +++ b/sharpy/aero/models/nonliftingbodygrid.py @@ -10,7 +10,7 @@ import sharpy.utils.algebra as algebra -class Nonlifting_body_grid(Grid): +class NonliftingBodyGrid(Grid): """ ``Nonlifting Body Grid`` is the main object containing information of the nonlifting bodygrid, consisting of triangular and quadrilateral panels. diff --git a/sharpy/solvers/nonliftingbodygridloader.py b/sharpy/solvers/nonliftingbodygridloader.py index 44faee5ad..75a73d74d 100644 --- a/sharpy/solvers/nonliftingbodygridloader.py +++ b/sharpy/solvers/nonliftingbodygridloader.py @@ -1,5 +1,5 @@ from sharpy.utils.solver_interface import solver -import sharpy.aero.models.nonlifting_body_grid as nonlifting_body_grid +import sharpy.aero.models.nonliftingbodygrid as nonliftingbodygrid import sharpy.utils.settings as settings_utils from sharpy.solvers.gridloader import GridLoader @@ -36,7 +36,7 @@ def __init__(self): self.nonlifting_body = None def run(self): - self.data.nonlifting_body = nonlifting_body_grid.Nonlifting_body_grid() + self.data.nonlifting_body = nonliftingbodygrid.NonliftingBodyGrid() self.data.nonlifting_body.generate(self.data_dict, self.data.structure, self.settings, From 2f4b2197a271efe2ec7bb71f968767e3728d1a0a Mon Sep 17 00:00:00 2001 From: sduess Date: Thu, 22 Dec 2022 14:22:54 +0000 Subject: [PATCH 103/232] add [savedata] data of nonlifting bodies to possible classes to save --- sharpy/postproc/savedata.py | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/sharpy/postproc/savedata.py b/sharpy/postproc/savedata.py index 360393064..2d889db9b 100644 --- a/sharpy/postproc/savedata.py +++ b/sharpy/postproc/savedata.py @@ -40,6 +40,10 @@ class SaveData(BaseSolver): settings_default['save_aero'] = True settings_description['save_aero'] = 'Save aerodynamic classes.' + settings_types['save_nonlifting'] = 'bool' + settings_default['save_nonlifting'] = True + settings_description['save_nonlifting'] = 'Save aerodynamic classes.' + settings_types['save_struct'] = 'bool' settings_default['save_struct'] = True settings_description['save_struct'] = 'Save structural classes.' @@ -172,6 +176,11 @@ def initialise(self, data, custom_settings=None, caller=None): self.settings['skip_attr'].append('dist_to_orig') self.settings['skip_attr'].append('wake_conv_vel') + + if self.settings['save_nonlifting']: + self.ClassesToSave += (sharpy.aero.models.nonliftingbodygrid.NonliftingBodyGrid, + sharpy.utils.datastructures.NonliftingBodyTimeStepInfo,) + if self.settings['save_struct']: self.ClassesToSave += ( sharpy.structure.models.beam.Beam, @@ -217,6 +226,10 @@ def run(self, online=False): h5utils.add_as_grp(list(), hdfile['data']['aero'], grpname='timestep_info') + if self.settings['save_nonlifting']: + h5utils.add_as_grp(list(), + hdfile['data']['nonlifting_body'], + grpname='timestep_info') for it in range(len(self.data.structure.timestep_info)): tstep_p = self.data.structure.timestep_info[it] @@ -299,6 +312,13 @@ def save_timestep(data, settings, ts, hdfile): ClassesToSave=(sharpy.utils.datastructures.AeroTimeStepInfo,), SkipAttr=settings['skip_attr'], compress_float=settings['compress_float']) + if settings['save_nonlifting']: + h5utils.add_as_grp(data.nonlifting_body.timestep_info[ts], + hdfile['data']['nonlifting_body']['timestep_info'], + grpname=("%05d" % ts), + ClassesToSave=(sharpy.utils.datastructures.NonliftingBodyTimeStepInfo,), + SkipAttr=settings['skip_attr'], + compress_float=settings['compress_float']) if settings['save_struct']: tstep = data.structure.timestep_info[ts] From 53ae1b1d1060c55dd37c0459a3c174e529f504ab Mon Sep 17 00:00:00 2001 From: sduess Date: Thu, 22 Dec 2022 18:38:39 +0000 Subject: [PATCH 104/232] add [datastructure] cp coefficient to store in nl body datastructure --- lib/UVLM | 2 +- sharpy/aero/utils/uvlmlib.py | 5 ++++- sharpy/utils/datastructures.py | 27 ++++++++++++++++++++++++--- 3 files changed, 29 insertions(+), 5 deletions(-) diff --git a/lib/UVLM b/lib/UVLM index 3bc064b8f..fdfbc9d7e 160000 --- a/lib/UVLM +++ b/lib/UVLM @@ -1 +1 @@ -Subproject commit 3bc064b8f53788d82b30152dbff10816e1ce6083 +Subproject commit fdfbc9d7e570da29fbe9a9d6a08b2014d7d745d0 diff --git a/sharpy/aero/utils/uvlmlib.py b/sharpy/aero/utils/uvlmlib.py index b47bde931..18fa67f6b 100644 --- a/sharpy/aero/utils/uvlmlib.py +++ b/sharpy/aero/utils/uvlmlib.py @@ -286,7 +286,8 @@ def vlm_solver_nonlifting_body(ts_info, options): ts_info.ct_p_zeta, ts_info.ct_p_u_ext, ts_info.ct_p_sigma, - ts_info.ct_p_forces) + ts_info.ct_p_forces, + ts_info.ct_p_pressure_coefficients) ts_info.remove_ctypes_pointers() def vlm_solver_lifting_and_nonlifting_bodies(ts_info_lifting, ts_info_nonlifting, options): @@ -322,6 +323,7 @@ def vlm_solver_lifting_and_nonlifting_bodies(ts_info_lifting, ts_info_nonlifting ts_info_nonlifting.ct_p_u_ext, ts_info_nonlifting.ct_p_sigma, ts_info_nonlifting.ct_p_forces, + ts_info_nonlifting.ct_p_pressure_coefficients, p_rbm_vel_g, p_centre_rot) @@ -443,6 +445,7 @@ def uvlm_solver_lifting_and_nonlifting(i_iter, ts_info, ts_info_nonlifting, stru ts_info_nonlifting.ct_p_u_ext, ts_info_nonlifting.ct_p_sigma, ts_info_nonlifting.ct_p_forces, + ts_info_nonlifting.ct_p_pressure_coefficients, p_rbm_vel, p_centre_rot) diff --git a/sharpy/utils/datastructures.py b/sharpy/utils/datastructures.py index db9dc3a6e..454b011be 100644 --- a/sharpy/utils/datastructures.py +++ b/sharpy/utils/datastructures.py @@ -340,6 +340,12 @@ def __init__(self, dimensions): #remove dimensions_star as input self.sigma_dot.append(np.zeros((dimensions[i_surf, 0], dimensions[i_surf, 1]), dtype=ct.c_double)) + + self.pressure_coefficients = [] + for i_surf in range(self.n_surf): + self.pressure_coefficients.append(np.zeros((dimensions[i_surf, 0], + dimensions[i_surf, 1]), + dtype=ct.c_double)) def copy(self): """ Returns a copy of a deepcopy of a :class:`~sharpy.utils.datastructures.AeroTimeStepInfo` @@ -349,13 +355,17 @@ def copy(self): def create_placeholder(self, copied): super().create_placeholder(copied) - # allocate sigma star matrices + # allocate sigma matrices for i_surf in range(copied.n_surf): copied.sigma[i_surf] = self.sigma[i_surf].astype(dtype=ct.c_double, copy=True, order='C') for i_surf in range(copied.n_surf): copied.sigma_dot[i_surf] = self.sigma_dot[i_surf].astype(dtype=ct.c_double, copy=True, order='C') + for i_surf in range(copied.n_surf): + copied.pressure_coefficients[i_surf] = self.pressure_coefficients[i_surf].astype(dtype=ct.c_double, copy=True, order='C') + + return copied @@ -375,11 +385,16 @@ def generate_ctypes_pointers(self): for i_surf in range(self.n_surf): self.ct_sigma_dot_list.append(self.sigma_dot[i_surf][:, :].reshape(-1)) + self.ct_pressure_coefficients_list = [] + for i_surf in range(self.n_surf): + self.ct_pressure_coefficients_list.append(self.pressure_coefficients[i_surf][:, :].reshape(-1)) + self.ct_p_sigma = ((ct.POINTER(ct.c_double)*len(self.ct_sigma_list)) (* [np.ctypeslib.as_ctypes(array) for array in self.ct_sigma_list])) self.ct_p_sigma_dot = ((ct.POINTER(ct.c_double)*len(self.ct_sigma_dot_list)) - (* [np.ctypeslib.as_ctypes(array) for array in self.ct_sigma_dot_list])) - + (* [np.ctypeslib.as_ctypes(array) for array in self.ct_sigma_list])) + self.ct_p_pressure_coefficients = ((ct.POINTER(ct.c_double)*len(self.ct_pressure_coefficients_list)) + (* [np.ctypeslib.as_ctypes(array) for array in self.ct_pressure_coefficients_list])) def remove_ctypes_pointers(self): """ @@ -396,6 +411,12 @@ def remove_ctypes_pointers(self): except AttributeError: pass + try: + del self.ct_p_pressure_coefficients + except AttributeError: + pass + + class AeroTimeStepInfo(TimeStepInfo): """ From da32a97fd11c51241f6b3b2af7b4326526dfdc08 Mon Sep 17 00:00:00 2001 From: sduess Date: Thu, 22 Dec 2022 18:42:39 +0000 Subject: [PATCH 105/232] fix [staticcoupled] force calculation for nonlifting bodies - fixed force transformation from panels to nodes in UVLM - ignore moments generated by nonlifting bodies on structure for now --- lib/UVLM | 2 +- sharpy/aero/utils/mapping.py | 8 +++++--- sharpy/solvers/staticcoupled.py | 28 +++++++++++++++------------- 3 files changed, 21 insertions(+), 17 deletions(-) diff --git a/lib/UVLM b/lib/UVLM index fdfbc9d7e..867f57e41 160000 --- a/lib/UVLM +++ b/lib/UVLM @@ -1 +1 @@ -Subproject commit fdfbc9d7e570da29fbe9a9d6a08b2014d7d745d0 +Subproject commit 867f57e41f51c5e7c1dfb2dc453f62da7a966fed diff --git a/sharpy/aero/utils/mapping.py b/sharpy/aero/utils/mapping.py index 3876f1116..c7792ef3d 100644 --- a/sharpy/aero/utils/mapping.py +++ b/sharpy/aero/utils/mapping.py @@ -11,7 +11,8 @@ def aero2struct_force_mapping(aero_forces, master, conn, cag=np.eye(3), - data_dict=None): + data_dict=None, + skip_moments_generated_by_forces = False): r""" Maps the aerodynamic forces at the lattice to the structural nodes @@ -69,10 +70,11 @@ def aero2struct_force_mapping(aero_forces, cbg = np.dot(cab.T, cag) for i_m in range(n_m): - chi_g = zeta[i_surf][:, i_m, i_n] - np.dot(cag.T, pos_def[i_global_node, :]) struct_forces[i_global_node, 0:3] += np.dot(cbg, aero_forces[i_surf][0:3, i_m, i_n]) struct_forces[i_global_node, 3:6] += np.dot(cbg, aero_forces[i_surf][3:6, i_m, i_n]) - struct_forces[i_global_node, 3:6] += np.dot(cbg, algebra.cross3(chi_g, aero_forces[i_surf][0:3, i_m, i_n])) + if not skip_moments_generated_by_forces: + chi_g = zeta[i_surf][:, i_m, i_n] - np.dot(cag.T, pos_def[i_global_node, :]) + struct_forces[i_global_node, 3:6] += np.dot(cbg, algebra.cross3(chi_g, aero_forces[i_surf][0:3, i_m, i_n])) return struct_forces diff --git a/sharpy/solvers/staticcoupled.py b/sharpy/solvers/staticcoupled.py index c19da5808..29f128bc3 100644 --- a/sharpy/solvers/staticcoupled.py +++ b/sharpy/solvers/staticcoupled.py @@ -77,9 +77,9 @@ class StaticCoupled(BaseSolver): 'The dictionary values are dictionaries with the settings ' \ 'needed by each generator.' - settings_types['nonlifting_body_interaction'] = 'bool' - settings_default['nonlifting_body_interaction'] = False - settings_description['nonlifting_body_interaction'] = 'Consider forces induced by nonlifting bodies' + settings_types['nonlifting_body_interactions'] = 'bool' + settings_default['nonlifting_body_interactions'] = False + settings_description['nonlifting_body_interactions'] = 'Consider forces induced by nonlifting bodies' settings_table = settings.SettingsTable() __doc__ += settings_table.generate(settings_types, settings_default, settings_description, settings_options) @@ -197,17 +197,19 @@ def run(self): self.data.structure.timestep_info[self.data.ts].cag(), self.data.aero.data_dict) - if self.settings['nonlifting_body_interaction']: + if self.settings['nonlifting_body_interactions']: struct_forces += mapping.aero2struct_force_mapping( - self.data.nonlifting_body.timestep_info[self.data.ts].forces, - self.data.nonlifting_body.struct2aero_mapping, - self.data.nonlifting_body.timestep_info[self.data.ts].zeta, - self.data.structure.timestep_info[self.data.ts].pos, - self.data.structure.timestep_info[self.data.ts].psi, - self.data.structure.node_master_elem, - self.data.structure.connectivities, - self.data.structure.timestep_info[self.data.ts].cag(), - self.data.nonlifting_body.data_dict) + self.data.nonlifting_body.timestep_info[self.data.ts].forces, + self.data.nonlifting_body.struct2aero_mapping, + self.data.nonlifting_body.timestep_info[self.data.ts].zeta, + self.data.structure.timestep_info[self.data.ts].pos, + self.data.structure.timestep_info[self.data.ts].psi, + self.data.structure.node_master_elem, + self.data.structure.connectivities, + self.data.structure.timestep_info[self.data.ts].cag(), + self.data.nonlifting_body.data_dict, + skip_moments_generated_by_forces = True) + if self.correct_forces: struct_forces = \ self.correct_forces_generator.generate(aero_kstep=self.data.aero.timestep_info[self.data.ts], From 52fe302fdd6954821629a4ed832ddfe470ff5050 Mon Sep 17 00:00:00 2001 From: sduess Date: Tue, 3 Jan 2023 22:00:58 +0000 Subject: [PATCH 106/232] fix [savedata] correct default value - change default for saving nonlinear to false, otherwise it would throw an error if a case without a nonlifting body is computed --- sharpy/postproc/savedata.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sharpy/postproc/savedata.py b/sharpy/postproc/savedata.py index 2d889db9b..a22c17850 100644 --- a/sharpy/postproc/savedata.py +++ b/sharpy/postproc/savedata.py @@ -41,7 +41,7 @@ class SaveData(BaseSolver): settings_description['save_aero'] = 'Save aerodynamic classes.' settings_types['save_nonlifting'] = 'bool' - settings_default['save_nonlifting'] = True + settings_default['save_nonlifting'] = False settings_description['save_nonlifting'] = 'Save aerodynamic classes.' settings_types['save_struct'] = 'bool' From d95410ffe8cfc9140fad93ad78015a82ea6f9c41 Mon Sep 17 00:00:00 2001 From: sduess Date: Wed, 8 Feb 2023 16:56:17 +0000 Subject: [PATCH 107/232] add [aeroforcescalculator] nonlifting aero forces - now, the postprocessor aeroforces calculator can also take forces generated from nonlifting bodies into account --- sharpy/postproc/aeroforcescalculator.py | 159 +++++++++++++++++------- 1 file changed, 114 insertions(+), 45 deletions(-) diff --git a/sharpy/postproc/aeroforcescalculator.py b/sharpy/postproc/aeroforcescalculator.py index 0e74b722f..05d02f0f8 100644 --- a/sharpy/postproc/aeroforcescalculator.py +++ b/sharpy/postproc/aeroforcescalculator.py @@ -38,6 +38,14 @@ class AeroForcesCalculator(BaseSolver): settings_types['coefficients'] = 'bool' settings_description['coefficients'] = 'Calculate aerodynamic coefficients' + settings_default['lifting_surfaces'] = True + settings_types['lifting_surfaces'] = 'bool' + settings_description['lifting_surfaces'] = 'Includes aerodynamic forces from lifting surfaces' + + settings_default['nonlifting_body'] = False + settings_types['nonlifting_body'] = 'bool' + settings_description['nonlifting_body'] = 'Includes aerodynamic forces from nonlifting bodies' + settings_types['q_ref'] = 'float' settings_default['q_ref'] = 1 settings_description['q_ref'] = 'Reference dynamic pressure' @@ -67,6 +75,8 @@ def __init__(self): self.caller = None self.table = None + self.rot = None + self.moment_reference_location = np.array([0., 0., 0.]) def initialise(self, data, custom_settings=None, caller=None): self.data = data @@ -105,77 +115,122 @@ def run(self, online=False): if self.settings['write_text_file']: self.file_output(self.settings['text_file_name']) return self.data - - def calculate_forces(self, ts): - rot = algebra.quat2rotation(self.data.structure.timestep_info[ts].quat) - + + def calculate_forces_lifting(self, ts): # Forces per surface in G frame force = self.data.aero.timestep_info[ts].forces unsteady_force = self.data.aero.timestep_info[ts].dynamic_forces - n_surf = len(force) - for i_surf in range(n_surf): - total_steady_force = np.zeros((3,)) - total_unsteady_force = np.zeros((3,)) - _, n_rows, n_cols = force[i_surf].shape - for i_m in range(n_rows): - for i_n in range(n_cols): - total_steady_force += force[i_surf][0:3, i_m, i_n] - total_unsteady_force += unsteady_force[i_surf][0:3, i_m, i_n] - self.data.aero.timestep_info[ts].inertial_steady_forces[i_surf, 0:3] = total_steady_force - self.data.aero.timestep_info[ts].inertial_unsteady_forces[i_surf, 0:3] = total_unsteady_force - self.data.aero.timestep_info[ts].body_steady_forces[i_surf, 0:3] = np.dot(rot.T, total_steady_force) - self.data.aero.timestep_info[ts].body_unsteady_forces[i_surf, 0:3] = np.dot(rot.T, total_unsteady_force) - - # Forces expressed in the beam degrees of freedom - try: - steady_forces_b = self.data.structure.timestep_info[ts].postproc_node['aero_steady_forces'] - except KeyError: - steady_forces_b = self.map_forces_beam_dof(ts, force) - + for i_surf in range(self.data.aero.n_surf): + ( + self.data.aero.timestep_info[ts].inertial_steady_forces[i_surf, 0:3], + self.data.aero.timestep_info[ts].inertial_unsteady_forces[i_surf, 0:3], + self.data.aero.timestep_info[ts].body_steady_forces[i_surf, 0:3], + self.data.aero.timestep_info[ts].body_unsteady_forces[i_surf, 0:3] + ) = self.calculate_forces_for_isurf_in_g_frame(force[i_surf], unsteady_force=unsteady_force[i_surf]) + + # Convert to forces in B frame + steady_forces_b = self.map_forces_beam_dof(self.data.aero, ts, force, nonlifting=False) try: unsteady_forces_b = self.data.structure.timestep_info[ts].postproc_node['aero_unsteady_forces'] except KeyError: - unsteady_forces_b = self.map_forces_beam_dof(ts, unsteady_force) - + unsteady_forces_b = self.map_forces_beam_dof(self.data.aero, ts, unsteady_force, nonlifting=False) + # Convert to forces in A frame steady_forces_a = self.data.structure.nodal_b_for_2_a_for(steady_forces_b, - self.data.structure.timestep_info[ts]) + self.data.structure.timestep_info[ts]) unsteady_forces_a = self.data.structure.nodal_b_for_2_a_for(unsteady_forces_b, self.data.structure.timestep_info[ts]) # Express total forces in A frame - moment_reference_location = np.array([0., 0., 0.]) self.data.aero.timestep_info[ts].total_steady_body_forces = \ mapping.total_forces_moments(steady_forces_a, self.data.structure.timestep_info[ts].pos, - ref_pos=moment_reference_location) + ref_pos=self.moment_reference_location) self.data.aero.timestep_info[ts].total_unsteady_body_forces = \ mapping.total_forces_moments(unsteady_forces_a, self.data.structure.timestep_info[ts].pos, - ref_pos=moment_reference_location) + ref_pos=self.moment_reference_location) # Express total forces in G frame self.data.aero.timestep_info[ts].total_steady_inertial_forces = \ - np.block([[rot, np.zeros((3, 3))], - [np.zeros((3, 3)), rot]]).dot( + np.block([[self.rot, np.zeros((3, 3))], + [np.zeros((3, 3)), self.rot]]).dot( self.data.aero.timestep_info[ts].total_steady_body_forces) self.data.aero.timestep_info[ts].total_unsteady_inertial_forces = \ - np.block([[rot, np.zeros((3, 3))], - [np.zeros((3, 3)), rot]]).dot( + np.block([[self.rot, np.zeros((3, 3))], + [np.zeros((3, 3)), self.rot]]).dot( self.data.aero.timestep_info[ts].total_unsteady_body_forces) - def map_forces_beam_dof(self, ts, force): - aero_tstep = self.data.aero.timestep_info[ts] + def calculate_forces_nonlifting(self, ts): + # Forces per surface in G frame + force = self.data.nonlifting_body.timestep_info[ts].forces + n_surf = len(force) + for i_surf in range(n_surf): + total_steady_force = np.zeros((3,)) + _, n_rows, n_cols = force[i_surf].shape + for i_m in range(n_rows): + for i_n in range(n_cols): + total_steady_force += force[i_surf][0:3, i_m, i_n] + self.data.nonlifting_body.timestep_info[ts].inertial_steady_forces[i_surf, 0:3] = total_steady_force + self.data.nonlifting_body.timestep_info[ts].body_steady_forces[i_surf, 0:3] = np.dot(self.rot.T, total_steady_force) + + # Convert to B frame + steady_forces_b = self.map_forces_beam_dof(self.data.nonlifting_body, ts, force, nonlifting=True) + # Convert to A frame + steady_forces_a = self.data.structure.nodal_b_for_2_a_for(steady_forces_b, + self.data.structure.timestep_info[ts]) + # Express total forces in A frame + self.data.nonlifting_body.timestep_info[ts].total_steady_body_forces = \ + mapping.total_forces_moments(steady_forces_a, + self.data.structure.timestep_info[ts].pos, + ref_pos=self.moment_reference_location) + + # Express total forces in G frame + self.data.nonlifting_body.timestep_info[ts].total_steady_inertial_forces = \ + np.block([[self.rot, np.zeros((3, 3))], + [np.zeros((3, 3)), self.rot]]).dot( + self.data.nonlifting_body.timestep_info[ts].total_steady_body_forces) + + def calculate_forces(self, ts): + self.rot = algebra.quat2rotation(self.data.structure.timestep_info[ts].quat) + # Forces per surface in G frame + if self.settings["lifting_surfaces"]: + self.calculate_forces_lifting(ts) + if self.settings["nonlifting_body"]: + self.calculate_forces_nonlifting(ts) + + def calculate_forces_for_isurf_in_g_frame(self, force, unsteady_force = None, nonlifting = False): + """ + Forces for a surface in G frame + """ + # Forces per surface in G frame + total_steady_force = np.zeros((3,)) + total_unsteady_force = np.zeros((3,)) + print(force.shape) + _, n_rows, n_cols = force.shape + for i_m in range(n_rows): + for i_n in range(n_cols): + total_steady_force += force[0:3, i_m, i_n] + if not nonlifting: + total_unsteady_force += unsteady_force[0:3, i_m, i_n] + if not nonlifting: + return total_steady_force, total_unsteady_force, np.dot(self.rot.T, total_steady_force), np.dot(self.rot.T, total_unsteady_force) + else: + return total_steady_force, np.dot(self.rot.T, total_steady_force) + + + def map_forces_beam_dof(self, aero_data, ts, force, nonlifting=False): struct_tstep = self.data.structure.timestep_info[ts] aero_forces_beam_dof = mapping.aero2struct_force_mapping(force, - self.data.aero.struct2aero_mapping, - aero_tstep.zeta, + aero_data.struct2aero_mapping, + aero_data.timestep_info[ts].zeta, struct_tstep.pos, struct_tstep.psi, None, self.data.structure.connectivities, - struct_tstep.cag()) + struct_tstep.cag(), + skip_moments_generated_by_forces=nonlifting) return aero_forces_beam_dof def calculate_coefficients(self, fx, fy, fz, mx, my, mz): @@ -185,15 +240,23 @@ def calculate_coefficients(self, fx, fy, fz, mx, my, mz): def screen_output(self, ts): # print time step total aero forces - aero_tstep = self.data.aero.timestep_info[ts] - fx, fy, fz = aero_tstep.total_steady_inertial_forces[:3] + aero_tstep.total_unsteady_inertial_forces[:3] - mx, my, mz = aero_tstep.total_steady_inertial_forces[3:] + aero_tstep.total_unsteady_inertial_forces[3:] + forces = np.zeros(3) + moments = np.zeros(3) + + if self.settings["lifting_surfaces"]: + aero_tstep = self.data.aero.timestep_info[ts] + forces += aero_tstep.total_steady_inertial_forces[:3] + aero_tstep.total_unsteady_inertial_forces[:3] + moments += aero_tstep.total_steady_inertial_forces[3:] + aero_tstep.total_unsteady_inertial_forces[3:] + + if self.settings["nonlifting_body"]: + forces += self.data.nonlifting_body.timestep_info[ts].total_steady_inertial_forces[:3] + moments += self.data.nonlifting_body.timestep_info[ts].total_steady_inertial_forces[3:] - if self.settings['coefficients']: - Cfx, Cfy, Cfz, Cmx, Cmy, Cmz = self.calculate_coefficients(fx, fy, fz, mx, my, mz) + if self.settings['coefficients']: # TODO: Check if coefficients have to be computed differently for fuselages + Cfx, Cfy, Cfz, Cmx, Cmy, Cmz = self.calculate_coefficients(*forces, *moments) self.table.print_line([ts, Cfx, Cfy, Cfz, Cmx, Cmy, Cmz]) else: - self.table.print_line([ts, fx, fy, fz, mx, my, mz]) + self.table.print_line([ts, *forces, *moments]) def file_output(self, filename): # assemble forces/moments matrix @@ -210,6 +273,9 @@ def file_output(self, filename): # Steady forces/moments G force_matrix[ts, i:i+3] = aero_tstep.total_steady_inertial_forces[:3] moment_matrix[ts, i:i+3] = aero_tstep.total_steady_inertial_forces[3:] + if self.settings["nonlifting_body"]: + force_matrix[ts, i:i+3] += self.data.nonlifting_body.timestep_info[ts].total_steady_inertial_forces[:3] + moment_matrix[ts, i:i+3] += self.data.nonlifting_body.timestep_info[ts].total_steady_inertial_forces[3:] i += 3 # Unsteady forces/moments G @@ -220,6 +286,9 @@ def file_output(self, filename): # Steady forces/moments A force_matrix[ts, i:i+3] = aero_tstep.total_steady_body_forces[:3] moment_matrix[ts, i:i+3] = aero_tstep.total_steady_body_forces[3:] + if self.settings["nonlifting_body"]: + force_matrix[ts, i:i+3] += self.data.nonlifting_body.timestep_info[ts].total_steady_body_forces[:3] + moment_matrix[ts, i:i+3] += self.data.nonlifting_body.timestep_info[ts].total_steady_body_forces[3:] i += 3 # Unsteady forces/moments A From 630583f057e67e0cdb92133886bcb3aa9e832877 Mon Sep 17 00:00:00 2001 From: sduess Date: Mon, 13 Feb 2023 19:13:49 +0000 Subject: [PATCH 108/232] fix [coupled] add nonlifting forces after polar corrections --- sharpy/solvers/staticcoupled.py | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/sharpy/solvers/staticcoupled.py b/sharpy/solvers/staticcoupled.py index 29f128bc3..09149d807 100644 --- a/sharpy/solvers/staticcoupled.py +++ b/sharpy/solvers/staticcoupled.py @@ -196,6 +196,13 @@ def run(self): self.data.structure.connectivities, self.data.structure.timestep_info[self.data.ts].cag(), self.data.aero.data_dict) + + if self.correct_forces: + struct_forces = \ + self.correct_forces_generator.generate(aero_kstep=self.data.aero.timestep_info[self.data.ts], + structural_kstep=self.data.structure.timestep_info[self.data.ts], + struct_forces=struct_forces, + ts=0) if self.settings['nonlifting_body_interactions']: struct_forces += mapping.aero2struct_force_mapping( @@ -209,13 +216,7 @@ def run(self): self.data.structure.timestep_info[self.data.ts].cag(), self.data.nonlifting_body.data_dict, skip_moments_generated_by_forces = True) - - if self.correct_forces: - struct_forces = \ - self.correct_forces_generator.generate(aero_kstep=self.data.aero.timestep_info[self.data.ts], - structural_kstep=self.data.structure.timestep_info[self.data.ts], - struct_forces=struct_forces, - ts=0) + self.data.aero.timestep_info[self.data.ts].aero_steady_forces_beam_dof = struct_forces self.data.structure.timestep_info[self.data.ts].postproc_node['aero_steady_forces'] = struct_forces # B From 3084086b802fc42ad881c6665dae150f4e686ab4 Mon Sep 17 00:00:00 2001 From: sduess Date: Tue, 14 Feb 2023 10:13:31 +0000 Subject: [PATCH 109/232] fix [aeroforces] only add forces from lifting surfaces if considered --- sharpy/postproc/aeroforcescalculator.py | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/sharpy/postproc/aeroforcescalculator.py b/sharpy/postproc/aeroforcescalculator.py index 05d02f0f8..c790f79ef 100644 --- a/sharpy/postproc/aeroforcescalculator.py +++ b/sharpy/postproc/aeroforcescalculator.py @@ -271,29 +271,35 @@ def file_output(self, filename): i += 1 # Steady forces/moments G - force_matrix[ts, i:i+3] = aero_tstep.total_steady_inertial_forces[:3] - moment_matrix[ts, i:i+3] = aero_tstep.total_steady_inertial_forces[3:] + if self.settings["lifting_surfaces"]: + force_matrix[ts, i:i+3] = aero_tstep.total_steady_inertial_forces[:3] + moment_matrix[ts, i:i+3] = aero_tstep.total_steady_inertial_forces[3:] if self.settings["nonlifting_body"]: force_matrix[ts, i:i+3] += self.data.nonlifting_body.timestep_info[ts].total_steady_inertial_forces[:3] moment_matrix[ts, i:i+3] += self.data.nonlifting_body.timestep_info[ts].total_steady_inertial_forces[3:] i += 3 # Unsteady forces/moments G - force_matrix[ts, i:i+3] = aero_tstep.total_unsteady_inertial_forces[:3] - moment_matrix[ts, i:i+3] = aero_tstep.total_unsteady_inertial_forces[3:] + + if self.settings["lifting_surfaces"]: + force_matrix[ts, i:i+3] = aero_tstep.total_unsteady_inertial_forces[:3] + moment_matrix[ts, i:i+3] = aero_tstep.total_unsteady_inertial_forces[3:] i += 3 # Steady forces/moments A - force_matrix[ts, i:i+3] = aero_tstep.total_steady_body_forces[:3] - moment_matrix[ts, i:i+3] = aero_tstep.total_steady_body_forces[3:] + + if self.settings["lifting_surfaces"]: + force_matrix[ts, i:i+3] = aero_tstep.total_steady_body_forces[:3] + moment_matrix[ts, i:i+3] = aero_tstep.total_steady_body_forces[3:] if self.settings["nonlifting_body"]: force_matrix[ts, i:i+3] += self.data.nonlifting_body.timestep_info[ts].total_steady_body_forces[:3] moment_matrix[ts, i:i+3] += self.data.nonlifting_body.timestep_info[ts].total_steady_body_forces[3:] i += 3 # Unsteady forces/moments A - force_matrix[ts, i:i+3] = aero_tstep.total_unsteady_body_forces[:3] - moment_matrix[ts, i:i+3] = aero_tstep.total_unsteady_body_forces[3:] + if self.settings["lifting_surfaces"]: + force_matrix[ts, i:i+3] = aero_tstep.total_unsteady_body_forces[:3] + moment_matrix[ts, i:i+3] = aero_tstep.total_unsteady_body_forces[3:] header = '' From 1826b4189dafa3a1242caf4a3f49a190e6a15f8d Mon Sep 17 00:00:00 2001 From: sduess Date: Tue, 14 Feb 2023 11:06:55 +0000 Subject: [PATCH 110/232] fix [aeroforces] get forces from postproc cell --- sharpy/postproc/aeroforcescalculator.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/sharpy/postproc/aeroforcescalculator.py b/sharpy/postproc/aeroforcescalculator.py index c790f79ef..f6e7d408f 100644 --- a/sharpy/postproc/aeroforcescalculator.py +++ b/sharpy/postproc/aeroforcescalculator.py @@ -127,9 +127,12 @@ def calculate_forces_lifting(self, ts): self.data.aero.timestep_info[ts].body_steady_forces[i_surf, 0:3], self.data.aero.timestep_info[ts].body_unsteady_forces[i_surf, 0:3] ) = self.calculate_forces_for_isurf_in_g_frame(force[i_surf], unsteady_force=unsteady_force[i_surf]) - + # Convert to forces in B frame - steady_forces_b = self.map_forces_beam_dof(self.data.aero, ts, force, nonlifting=False) + try: + steady_forces_b = self.data.structure.timestep_info[ts].postproc_node['aero_steady_forces'] + except KeyError: + steady_forces_b = self.map_forces_beam_dof(self.data.aero, ts, force, nonlifting=False) try: unsteady_forces_b = self.data.structure.timestep_info[ts].postproc_node['aero_unsteady_forces'] except KeyError: @@ -207,7 +210,6 @@ def calculate_forces_for_isurf_in_g_frame(self, force, unsteady_force = None, no # Forces per surface in G frame total_steady_force = np.zeros((3,)) total_unsteady_force = np.zeros((3,)) - print(force.shape) _, n_rows, n_cols = force.shape for i_m in range(n_rows): for i_n in range(n_cols): From 20893cba6dd48a109163803d6ce1fb5f6ce158b9 Mon Sep 17 00:00:00 2001 From: sduess Date: Tue, 14 Feb 2023 16:07:30 +0000 Subject: [PATCH 111/232] fix [aeroforces calculator] rely on postproc node forces - basically only get the inertial and body steady forces seperated from the aero forces - the b frame forces are retrieved from the postproc node generated in the coupling solver - if that does not work, and the aeroforces calculator goes into the exception, nonlifting body forces are not retrieved. However, it is very unlikely to happen and a warning is thrown --- sharpy/postproc/aeroforcescalculator.py | 103 +++++++----------------- 1 file changed, 31 insertions(+), 72 deletions(-) diff --git a/sharpy/postproc/aeroforcescalculator.py b/sharpy/postproc/aeroforcescalculator.py index f6e7d408f..14f2256ce 100644 --- a/sharpy/postproc/aeroforcescalculator.py +++ b/sharpy/postproc/aeroforcescalculator.py @@ -6,6 +6,7 @@ import sharpy.utils.settings as settings import sharpy.utils.algebra as algebra import sharpy.aero.utils.mapping as mapping +import warnings @solver @@ -116,8 +117,9 @@ def run(self, online=False): self.file_output(self.settings['text_file_name']) return self.data - def calculate_forces_lifting(self, ts): + def calculate_forces(self, ts): # Forces per surface in G frame + self.rot = algebra.quat2rotation(self.data.structure.timestep_info[ts].quat) force = self.data.aero.timestep_info[ts].forces unsteady_force = self.data.aero.timestep_info[ts].dynamic_forces for i_surf in range(self.data.aero.n_surf): @@ -128,15 +130,28 @@ def calculate_forces_lifting(self, ts): self.data.aero.timestep_info[ts].body_unsteady_forces[i_surf, 0:3] ) = self.calculate_forces_for_isurf_in_g_frame(force[i_surf], unsteady_force=unsteady_force[i_surf]) + print(self.settings["nonlifting_body"]) + if self.settings["nonlifting_body"]: + print(self.data.nonlifting_body.n_surf) + for i_surf in range(self.data.nonlifting_body.n_surf): + print(i_surf) + ( + self.data.nonlifting_body.timestep_info[ts].inertial_steady_forces[i_surf, 0:3], + self.data.nonlifting_body.timestep_info[ts].body_steady_forces[i_surf, 0:3], + ) = self.calculate_forces_for_isurf_in_g_frame(self.data.nonlifting_body.timestep_info[ts].forces[i_surf], nonlifting=True) + # Convert to forces in B frame try: steady_forces_b = self.data.structure.timestep_info[ts].postproc_node['aero_steady_forces'] except KeyError: - steady_forces_b = self.map_forces_beam_dof(self.data.aero, ts, force, nonlifting=False) + if self.settings["nonlifting_body"]: + warnings.warn('Nonlifting forces are not considered in aero forces calculation since forces cannot not be retrieved from postproc node.') + steady_forces_b = self.map_forces_beam_dof(self.data.aero, ts, force) + try: unsteady_forces_b = self.data.structure.timestep_info[ts].postproc_node['aero_unsteady_forces'] except KeyError: - unsteady_forces_b = self.map_forces_beam_dof(self.data.aero, ts, unsteady_force, nonlifting=False) + unsteady_forces_b = self.map_forces_beam_dof(self.data.aero, ts, unsteady_force) # Convert to forces in A frame steady_forces_a = self.data.structure.nodal_b_for_2_a_for(steady_forces_b, self.data.structure.timestep_info[ts]) @@ -165,44 +180,6 @@ def calculate_forces_lifting(self, ts): [np.zeros((3, 3)), self.rot]]).dot( self.data.aero.timestep_info[ts].total_unsteady_body_forces) - def calculate_forces_nonlifting(self, ts): - # Forces per surface in G frame - force = self.data.nonlifting_body.timestep_info[ts].forces - n_surf = len(force) - for i_surf in range(n_surf): - total_steady_force = np.zeros((3,)) - _, n_rows, n_cols = force[i_surf].shape - for i_m in range(n_rows): - for i_n in range(n_cols): - total_steady_force += force[i_surf][0:3, i_m, i_n] - self.data.nonlifting_body.timestep_info[ts].inertial_steady_forces[i_surf, 0:3] = total_steady_force - self.data.nonlifting_body.timestep_info[ts].body_steady_forces[i_surf, 0:3] = np.dot(self.rot.T, total_steady_force) - - # Convert to B frame - steady_forces_b = self.map_forces_beam_dof(self.data.nonlifting_body, ts, force, nonlifting=True) - # Convert to A frame - steady_forces_a = self.data.structure.nodal_b_for_2_a_for(steady_forces_b, - self.data.structure.timestep_info[ts]) - # Express total forces in A frame - self.data.nonlifting_body.timestep_info[ts].total_steady_body_forces = \ - mapping.total_forces_moments(steady_forces_a, - self.data.structure.timestep_info[ts].pos, - ref_pos=self.moment_reference_location) - - # Express total forces in G frame - self.data.nonlifting_body.timestep_info[ts].total_steady_inertial_forces = \ - np.block([[self.rot, np.zeros((3, 3))], - [np.zeros((3, 3)), self.rot]]).dot( - self.data.nonlifting_body.timestep_info[ts].total_steady_body_forces) - - def calculate_forces(self, ts): - self.rot = algebra.quat2rotation(self.data.structure.timestep_info[ts].quat) - # Forces per surface in G frame - if self.settings["lifting_surfaces"]: - self.calculate_forces_lifting(ts) - if self.settings["nonlifting_body"]: - self.calculate_forces_nonlifting(ts) - def calculate_forces_for_isurf_in_g_frame(self, force, unsteady_force = None, nonlifting = False): """ Forces for a surface in G frame @@ -222,7 +199,7 @@ def calculate_forces_for_isurf_in_g_frame(self, force, unsteady_force = None, no return total_steady_force, np.dot(self.rot.T, total_steady_force) - def map_forces_beam_dof(self, aero_data, ts, force, nonlifting=False): + def map_forces_beam_dof(self, aero_data, ts, force): struct_tstep = self.data.structure.timestep_info[ts] aero_forces_beam_dof = mapping.aero2struct_force_mapping(force, aero_data.struct2aero_mapping, @@ -231,8 +208,7 @@ def map_forces_beam_dof(self, aero_data, ts, force, nonlifting=False): struct_tstep.psi, None, self.data.structure.connectivities, - struct_tstep.cag(), - skip_moments_generated_by_forces=nonlifting) + struct_tstep.cag()) return aero_forces_beam_dof def calculate_coefficients(self, fx, fy, fz, mx, my, mz): @@ -245,14 +221,9 @@ def screen_output(self, ts): forces = np.zeros(3) moments = np.zeros(3) - if self.settings["lifting_surfaces"]: - aero_tstep = self.data.aero.timestep_info[ts] - forces += aero_tstep.total_steady_inertial_forces[:3] + aero_tstep.total_unsteady_inertial_forces[:3] - moments += aero_tstep.total_steady_inertial_forces[3:] + aero_tstep.total_unsteady_inertial_forces[3:] - - if self.settings["nonlifting_body"]: - forces += self.data.nonlifting_body.timestep_info[ts].total_steady_inertial_forces[:3] - moments += self.data.nonlifting_body.timestep_info[ts].total_steady_inertial_forces[3:] + aero_tstep = self.data.aero.timestep_info[ts] + forces += aero_tstep.total_steady_inertial_forces[:3] + aero_tstep.total_unsteady_inertial_forces[:3] + moments += aero_tstep.total_steady_inertial_forces[3:] + aero_tstep.total_unsteady_inertial_forces[3:] if self.settings['coefficients']: # TODO: Check if coefficients have to be computed differently for fuselages Cfx, Cfy, Cfz, Cmx, Cmy, Cmz = self.calculate_coefficients(*forces, *moments) @@ -273,35 +244,23 @@ def file_output(self, filename): i += 1 # Steady forces/moments G - if self.settings["lifting_surfaces"]: - force_matrix[ts, i:i+3] = aero_tstep.total_steady_inertial_forces[:3] - moment_matrix[ts, i:i+3] = aero_tstep.total_steady_inertial_forces[3:] - if self.settings["nonlifting_body"]: - force_matrix[ts, i:i+3] += self.data.nonlifting_body.timestep_info[ts].total_steady_inertial_forces[:3] - moment_matrix[ts, i:i+3] += self.data.nonlifting_body.timestep_info[ts].total_steady_inertial_forces[3:] + force_matrix[ts, i:i+3] = aero_tstep.total_steady_inertial_forces[:3] + moment_matrix[ts, i:i+3] = aero_tstep.total_steady_inertial_forces[3:] i += 3 # Unsteady forces/moments G - - if self.settings["lifting_surfaces"]: - force_matrix[ts, i:i+3] = aero_tstep.total_unsteady_inertial_forces[:3] - moment_matrix[ts, i:i+3] = aero_tstep.total_unsteady_inertial_forces[3:] + force_matrix[ts, i:i+3] = aero_tstep.total_unsteady_inertial_forces[:3] + moment_matrix[ts, i:i+3] = aero_tstep.total_unsteady_inertial_forces[3:] i += 3 # Steady forces/moments A - - if self.settings["lifting_surfaces"]: - force_matrix[ts, i:i+3] = aero_tstep.total_steady_body_forces[:3] - moment_matrix[ts, i:i+3] = aero_tstep.total_steady_body_forces[3:] - if self.settings["nonlifting_body"]: - force_matrix[ts, i:i+3] += self.data.nonlifting_body.timestep_info[ts].total_steady_body_forces[:3] - moment_matrix[ts, i:i+3] += self.data.nonlifting_body.timestep_info[ts].total_steady_body_forces[3:] + force_matrix[ts, i:i+3] = aero_tstep.total_steady_body_forces[:3] + moment_matrix[ts, i:i+3] = aero_tstep.total_steady_body_forces[3:] i += 3 # Unsteady forces/moments A - if self.settings["lifting_surfaces"]: - force_matrix[ts, i:i+3] = aero_tstep.total_unsteady_body_forces[:3] - moment_matrix[ts, i:i+3] = aero_tstep.total_unsteady_body_forces[3:] + force_matrix[ts, i:i+3] = aero_tstep.total_unsteady_body_forces[:3] + moment_matrix[ts, i:i+3] = aero_tstep.total_unsteady_body_forces[3:] header = '' From 2b64f3d7cb527f466824d475857d303e757a8f36 Mon Sep 17 00:00:00 2001 From: sduess Date: Mon, 20 Feb 2023 15:03:34 +0000 Subject: [PATCH 112/232] add [fuselage] phantom debug test option - enables to test the UVLM with a wing plus phantom surface case while neglecting the fuselage effects - the solution of this model should result in similiar results as a wing only case if wing is large enough --- sharpy/aero/utils/uvlmlib.py | 10 ++++++++-- sharpy/solvers/staticuvlm.py | 5 +++++ sharpy/solvers/stepuvlm.py | 10 +++++++--- 3 files changed, 20 insertions(+), 5 deletions(-) diff --git a/sharpy/aero/utils/uvlmlib.py b/sharpy/aero/utils/uvlmlib.py index 18fa67f6b..ea21d6bc8 100644 --- a/sharpy/aero/utils/uvlmlib.py +++ b/sharpy/aero/utils/uvlmlib.py @@ -64,7 +64,8 @@ class VMopts(ct.Structure): ("vortex_radius", ct.c_double), ("vortex_radius_wake_ind", ct.c_double), ("centre_rot_g", ct.c_double * 3), - ("rbm_vel_g", ct.c_double * 6)] + ("rbm_vel_g", ct.c_double * 6), + ("phantom_wing_test", ct.c_bool)] @@ -94,6 +95,7 @@ def __init__(self): self.vortex_radius_wake_ind = ct.c_double(vortex_radius_def) self.centre_rot_g = np.ctypeslib.as_ctypes(np.zeros((3))) self.rbm_vel_g = np.ctypeslib.as_ctypes(np.zeros((6))) + self.phantom_wing_test = ct.c_bool(False) def set_options(self, options, n_surfaces = 0, n_surfaces_nonlifting = 0, rbm_vel_g = np.zeros(6)): @@ -115,6 +117,7 @@ def set_options(self, options, n_surfaces = 0, n_surfaces_nonlifting = 0, rbm_ve self.only_nonlifting = ct.c_bool(options["only_nonlifting"]) self.only_lifting = ct.c_bool(not options["nonlifting_body_interactions"]) + self.phantom_wing_test = ct.c_bool(options["phantom_wing_test"]) for i in range(len(options["centre_rot_g"])): self.centre_rot_g[i] = ct.c_double(options["centre_rot_g"][i]) @@ -150,7 +153,8 @@ class UVMopts(ct.Structure): ("quasi_steady", ct.c_bool), ("centre_rot_g", ct.c_double * 3), ("rbm_vel_g", ct.c_double * 6), - ("num_spanwise_panels_wo_induced_velocity", ct.c_uint)] + ("num_spanwise_panels_wo_induced_velocity", ct.c_uint), + ("phantom_wing_test", ct.c_bool)] def __init__(self): ct.Structure.__init__(self) @@ -173,6 +177,7 @@ def __init__(self): self.centre_rot_g = np.ctypeslib.as_ctypes(np.zeros((3))) self.rbm_vel_g = np.ctypeslib.as_ctypes(np.zeros((6))) self.num_spanwise_panels_wo_induced_velocity = ct.c_uint(0) + self.phantom_wing_test = ct.c_bool(False) def set_options(self, options, @@ -207,6 +212,7 @@ def set_options(self, self.only_nonlifting = ct.c_bool(options["only_nonlifting"]) self.only_lifting = ct.c_bool(not options["nonlifting_body_interactions"]) + self.phantom_wing_test = ct.c_bool(options["phantom_wing_test"]) self.num_spanwise_panels_wo_induced_velocity = n_span_panels_wo_u_ind for i in range(len(options["centre_rot_g"])): diff --git a/sharpy/solvers/staticuvlm.py b/sharpy/solvers/staticuvlm.py index afced63c4..9966b495f 100644 --- a/sharpy/solvers/staticuvlm.py +++ b/sharpy/solvers/staticuvlm.py @@ -53,6 +53,11 @@ class StaticUvlm(BaseSolver): settings_types['only_nonlifting'] = 'bool' settings_default['only_nonlifting'] = False settings_description['only_nonlifting'] = 'Consider only nonlifting bodies' + + settings_types['phantom_wing_test'] = 'bool' + settings_default['phantom_wing_test'] = False + settings_description['phantom_wing_test'] = 'Debug option' + settings_types['num_cores'] = 'int' settings_default['num_cores'] = 0 settings_description['num_cores'] = 'Number of cores to use in the VLM lib' diff --git a/sharpy/solvers/stepuvlm.py b/sharpy/solvers/stepuvlm.py index fceb7e3f1..d40e611e3 100644 --- a/sharpy/solvers/stepuvlm.py +++ b/sharpy/solvers/stepuvlm.py @@ -133,13 +133,17 @@ class StepUvlm(BaseSolver): settings_default['quasi_steady'] = False settings_description['quasi_steady'] = 'Use quasi-steady approximation in UVLM' + settings_types['only_nonlifting'] = 'bool' + settings_default['only_nonlifting'] = False + settings_description['only_nonlifting'] = 'Consider nonlifting body interactions' + settings_types['nonlifting_body_interactions'] = 'bool' settings_default['nonlifting_body_interactions'] = False settings_description['nonlifting_body_interactions'] = 'Consider nonlifting body interactions' - settings_types['only_nonlifting'] = 'bool' - settings_default['only_nonlifting'] = False - settings_description['only_nonlifting'] = 'Consider only nonlifting bodies' + settings_types['phantom_wing_test'] = 'bool' + settings_default['phantom_wing_test'] = False + settings_description['phantom_wing_test'] = 'Debug option' settings_types['centre_rot_g'] = 'list(float)' settings_default['centre_rot_g'] = [0., 0., 0.] From 7b8ee7971555acf5f9a59a0519bded82464b3fd7 Mon Sep 17 00:00:00 2001 From: sduess Date: Wed, 22 Feb 2023 11:31:40 +0000 Subject: [PATCH 113/232] update submodule UVLM --- lib/UVLM | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/UVLM b/lib/UVLM index 867f57e41..64074367b 160000 --- a/lib/UVLM +++ b/lib/UVLM @@ -1 +1 @@ -Subproject commit 867f57e41f51c5e7c1dfb2dc453f62da7a966fed +Subproject commit 64074367b76df2330ab2ebe31360af78fc047697 From 051f37d5f23ff99352774693d493cb9902bde3e7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stefanie=20D=C3=BCssler?= Date: Mon, 17 Jul 2023 11:16:57 +0800 Subject: [PATCH 114/232] update submodule xbeam --- lib/xbeam | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/xbeam b/lib/xbeam index 56d933b8f..50e4cad40 160000 --- a/lib/xbeam +++ b/lib/xbeam @@ -1 +1 @@ -Subproject commit 56d933b8f0de6e03615bfc73003c524dfdafc00b +Subproject commit 50e4cad4064ddac65d1444351c51f4d1a0bc4aba From 6517f2c6970093f15d32d978e70ef5a63be7ae2a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stefanie=20D=C3=BCssler?= Date: Mon, 17 Jul 2023 11:17:27 +0800 Subject: [PATCH 115/232] documentation [datastructure] add missing parameter description --- sharpy/utils/datastructures.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/sharpy/utils/datastructures.py b/sharpy/utils/datastructures.py index 454b011be..9a1b25f26 100644 --- a/sharpy/utils/datastructures.py +++ b/sharpy/utils/datastructures.py @@ -307,6 +307,8 @@ class NonliftingBodyTimeStepInfo(TimeStepInfo): sigma (list(np.ndarray)): Source strength associated to solid panels ``[n_surf][3 x radial panel x spanwise panel]`` sigma_dot (list(np.ndarray)): Time derivative of ``sigma`` + pressure_coefficients (list(np.ndarray)): Pressure coefficient associated to solid panels + ``[n_surf][radial panel x spanwise panel]`` inertial_total_forces (list(np.ndarray)): Total aerodynamic forces in ``G`` FoR ``[n_surf x 6]`` body_total_forces (list(np.ndarray)): Total aerodynamic forces in ``A`` FoR ``[n_surf x 6]`` From 1376bfab53f56cab8561cbefa3596fd7974c8181 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stefanie=20D=C3=BCssler?= Date: Mon, 17 Jul 2023 11:18:16 +0800 Subject: [PATCH 116/232] fix [writevariablestime] saving aero panel variables --- sharpy/postproc/writevariablestime.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sharpy/postproc/writevariablestime.py b/sharpy/postproc/writevariablestime.py index 17b6355d4..58f46b9aa 100644 --- a/sharpy/postproc/writevariablestime.py +++ b/sharpy/postproc/writevariablestime.py @@ -269,7 +269,7 @@ def write(self, it): with open(filename, 'a') as fid: var = getattr(self.data.aero.timestep_info[it], self.settings['aero_panels_variables'][ivariable]) - self.write_value_to_file(fid, self.data.ts, var.gamma[i_surf][i_m,i_n], self.settings['delimiter']) + self.write_value_to_file(fid, self.data.ts, var[i_surf][i_m,i_n], self.settings['delimiter']) # Aerodynamic variables at nodes From 7f02a6a34b1e2f1b791fb8677bff65fdbb0a3558 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stefanie=20D=C3=BCssler?= Date: Mon, 17 Jul 2023 11:18:41 +0800 Subject: [PATCH 117/232] add [writevariablestime] setting to export nonlifting panel attributes --- sharpy/postproc/writevariablestime.py | 42 +++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/sharpy/postproc/writevariablestime.py b/sharpy/postproc/writevariablestime.py index 58f46b9aa..c376fc76c 100644 --- a/sharpy/postproc/writevariablestime.py +++ b/sharpy/postproc/writevariablestime.py @@ -48,6 +48,22 @@ class WriteVariablesTime(BaseSolver): settings_default['structure_nodes'] = np.array([-1]) settings_description['structure_nodes'] = 'Number of the nodes to be writen' + settings_types['nonlifting_nodes_variables'] = 'list(str)' + settings_default['nonlifting_nodes_variables'] = [''] + settings_description['nonlifting_nodes_variables'] = 'Variables of :class:`~sharpy.utils.datastructures.NonliftingBodyTimeStepInfo` associated to panels to be writen' + + settings_types['nonlifting_nodes_im'] = 'list(int)' + settings_default['nonlifting_nodes_im'] = np.array([0]) + settings_description['nonlifting_nodes_im'] = 'Chordwise index of the nonlifting panels to be output' + + settings_types['nonlifting_nodes_in'] = 'list(int)' + settings_default['nonlifting_nodes_in'] = np.array([0]) + settings_description['nonlifting_nodes_in'] = 'Spanwise index of the nonlifting panels to be output' + + settings_types['nonlifting_nodes_isurf'] = 'list(int)' + settings_default['nonlifting_nodes_isurf'] = np.array([0]) + settings_description['nonlifting_nodes_isurf'] = "Number of the panels' surface to be output" + settings_types['aero_panels_variables'] = 'list(str)' settings_default['aero_panels_variables'] = [''] settings_description['aero_panels_variables'] = 'Variables of :class:`~sharpy.utils.datastructures.AeroTimeStepInfo` associated to panels to be writen' @@ -149,6 +165,17 @@ def initialise(self, data, custom_settings=None, caller=None): if os.path.isfile(filename): os.remove(filename) + # Nonlifting variables at panels + for ivariable in range(len(self.settings['nonlifting_nodes_variables'])): + for ipanel in range(len(self.settings['nonlifting_nodes_isurf'])): + i_surf = self.settings['nonlifting_nodes_isurf'][ipanel] + i_m = self.settings['nonlifting_nodes_im'][ipanel] + i_n = self.settings['nonlifting_nodes_in'][ipanel] + filename = self.folder + "nonlifting_" + self.settings['nonlifting_nodes_variables'][ivariable] + "_panel" + "_isurf" + str(i_surf) + "_im"+ str(i_m) + "_in"+ str(i_n) + ".dat" + if self.settings['cleanup_old_solution']: + if os.path.isfile(filename): + os.remove(filename) + # Aerodynamic variables at panels for ivariable in range(len(self.settings['aero_panels_variables'])): for ipanel in range(len(self.settings['aero_panels_isurf'])): @@ -256,6 +283,21 @@ def write(self, it): self.write_nparray_to_file(fid, self.data.ts, var[ielem,inode_in_elem,:], self.settings['delimiter']) + # Aerodynamic variables at nonlifting panels + for ivariable in range(len(self.settings['nonlifting_nodes_variables'])): + if self.settings['nonlifting_nodes_variables'][ivariable] == '': + continue + + for ipanel in range(len(self.settings['nonlifting_nodes_isurf'])): + i_surf = self.settings['nonlifting_nodes_isurf'][ipanel] + i_m = self.settings['nonlifting_nodes_im'][ipanel] + i_n = self.settings['nonlifting_nodes_in'][ipanel] + filename = self.folder + "nonlifting_" + self.settings['nonlifting_nodes_variables'][ivariable] + "_panel" + "_isurf" + str(i_surf) + "_im"+ str(i_m) + "_in"+ str(i_n) + ".dat" + + with open(filename, 'a') as fid: + var = getattr(self.data.nonlifting_body.timestep_info[it], self.settings['nonlifting_nodes_variables'][ivariable]) + self.write_value_to_file(fid, self.data.ts, var[i_surf][i_m,i_n], self.settings['delimiter']) + # Aerodynamic variables at panels for ivariable in range(len(self.settings['aero_panels_variables'])): if self.settings['aero_panels_variables'][ivariable] == '': From 00b6dd18c6be5ff6e3138d5d166b7919ba11e2a2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stefanie=20D=C3=BCssler?= Date: Mon, 17 Jul 2023 11:19:08 +0800 Subject: [PATCH 118/232] add [unittest] ellipsoid test to check for source panel method functionality --- .../nonlifting/define_simulation_settings.py | 100 +++++++++++ tests/uvlm/static/nonlifting/fuselage.py | 156 ++++++++++++++++++ .../uvlm/static/nonlifting/test_ellipsoid.py | 101 ++++++++++++ 3 files changed, 357 insertions(+) create mode 100644 tests/uvlm/static/nonlifting/define_simulation_settings.py create mode 100644 tests/uvlm/static/nonlifting/fuselage.py create mode 100644 tests/uvlm/static/nonlifting/test_ellipsoid.py diff --git a/tests/uvlm/static/nonlifting/define_simulation_settings.py b/tests/uvlm/static/nonlifting/define_simulation_settings.py new file mode 100644 index 000000000..a4ed49e82 --- /dev/null +++ b/tests/uvlm/static/nonlifting/define_simulation_settings.py @@ -0,0 +1,100 @@ +import numpy as np +import sharpy.utils.algebra as algebra + +def define_simulation_settings(flow, model, alpha_deg, u_inf, rho = 1.225, lifting_only=True, nonlifting_only=False, horseshoe=False, **kwargs): + gravity = kwargs.get('gravity',True) + nonlifting_body_interactions = not lifting_only and not nonlifting_only + wake_length = kwargs.get('wake_length', 10) + # Other parameters + if horseshoe: + dt = 1 + mstar = 1 + else: + dt = model.aero.chord_main / model.aero.m / u_inf + mstar = wake_length*model.aero.m + # numerics + n_step = kwargs.get('n_step', 5) + structural_relaxation_factor = kwargs.get('structural_relaxation_factor', 0.6) + tolerance = kwargs.get('tolerance', 1e-6) + fsi_tolerance = kwargs.get('fsi_tolerance', 1e-4) + num_cores = kwargs.get('num_cores',2) + + if not lifting_only: + nonlifting_body_interactions = True + else: + nonlifting_body_interactions = False + flow.remove("NonliftingbodygridLoader") + settings = {} + settings['SHARPy'] = {'case': model.case_name, + 'route': model.case_route, + 'flow': flow, + 'write_screen': 'on', + 'write_log': 'on', + 'log_folder': model.output_route, + 'log_file': model.case_name + '.log'} + + + settings['BeamLoader'] = {'unsteady': 'on', + 'orientation': algebra.euler2quat(np.array([0., + np.deg2rad(alpha_deg), + 0.]))} + + + settings['LiftDistribution'] = {'rho': rho, + 'coefficients': True} + + settings['NonLinearStatic'] = {'print_info': 'off', + 'max_iterations': 150, + 'num_load_steps': 1, + 'delta_curved': 1e-1, + 'min_delta': tolerance, + 'gravity_on': gravity, + 'gravity': 9.81} + + settings['StaticUvlm'] = {'print_info': 'on', + 'horseshoe': horseshoe, + 'num_cores': num_cores, + 'velocity_field_generator': 'SteadyVelocityField', + 'velocity_field_input': {'u_inf': u_inf, + 'u_inf_direction': [1., 0, 0]}, + 'rho': rho, + 'nonlifting_body_interactions': nonlifting_body_interactions, + 'only_nonlifting': nonlifting_only, + } + + settings['StaticCoupled'] = {'print_info': 'off', + 'structural_solver': 'NonLinearStatic', + 'structural_solver_settings': settings['NonLinearStatic'], + 'aero_solver': 'StaticUvlm', + 'aero_solver_settings': settings['StaticUvlm'], + 'max_iter': 100, + 'n_load_steps': n_step, + 'tolerance': fsi_tolerance, + 'relaxation_factor': structural_relaxation_factor, + 'nonlifting_body_interactions': nonlifting_body_interactions} + + settings['AerogridLoader'] = {'unsteady': 'on', + 'aligned_grid': 'on', + 'mstar': mstar, #int(20/tstep_factor), + 'wake_shape_generator': 'StraightWake', + 'wake_shape_generator_input': { + 'u_inf': u_inf, + 'u_inf_direction': [1., 0., 0.], + 'dt': dt, + }, + } + + settings['WriteVariablesTime'] = { + 'cleanup_old_solution': True, + 'nonlifting_nodes_variables': ['pressure_coefficients'], + 'nonlifting_nodes_isurf': np.zeros((model.n_node - 1,)), + 'nonlifting_nodes_im': np.zeros((model.n_node - 1,)), + 'nonlifting_nodes_in': list(range(model.n_node - 1)), + } + + settings['NonliftingbodygridLoader'] = {} + + settings['AeroForcesCalculator'] = {'coefficients': False} + + + return settings \ No newline at end of file diff --git a/tests/uvlm/static/nonlifting/fuselage.py b/tests/uvlm/static/nonlifting/fuselage.py new file mode 100644 index 000000000..13ef014f4 --- /dev/null +++ b/tests/uvlm/static/nonlifting/fuselage.py @@ -0,0 +1,156 @@ +import os +import numpy as np +import h5py as h5 +import configobj +import sharpy.sharpy_main + + +class Fuselage(): + def __init__(self, case_name, case_route, output_route): + self.case_name = case_name + self.case_route = case_route + self.output_route = output_route + + self.settings = None + + self.n_node_elem = 3 + + def clean(self): + list_files = ['.fem.h5', '.aero.h5', '.nonlifting_body.h5', '.dyn.h5', '.mb.h5', '.sharpy', '.flightcon.txt'] + for file in list_files: + path_file = self.case_route + '/' + self.case_name + file + if os.path.isfile(path_file): + os.remove(path_file) + + def generate_structure(self, **kwargs): + self.length = kwargs.get('length', 10) + self.n_elem = kwargs.get('n_elem', 11) + self.sigma_fuselage = kwargs.get('sigma_fuselage', 10.) + + self.set_beam_properties() + self.set_stiffness_and_mass_propoerties() + + self.write_structural_input_file() + + def generate(self, **kwargs): + self.clean() + self.generate_structure(**kwargs) + self.generate_fuselage(**kwargs) + + def generate_fuselage(self, **kwargs): + self.num_radial_panels = kwargs.get('num_radial_panels', 24) + self.max_radius = kwargs.get('max_radius', 2) # rename + self.n_nonlifting_bodies = 1 + self.fuselage_shape = kwargs.get('fuselage_shape', 'ellipsoid') + + + self.set_fuselage_properties() + self.write_fuselage_input_file() + + def set_fuselage_properties(self): + self.nonlifting_body_node = np.ones((self.n_node,), dtype=bool) + self.nonlifting_body_distribution = np.zeros((self.n_elem,), dtype=int) + self.nonlifting_body_m = np.zeros((self.n_nonlifting_bodies, ), dtype=int) + self.num_radial_panels + self.radius = np.zeros((self.n_node,)) + + if self.fuselage_shape == 'ellipsoid': + self.radius = self.get_radius_ellipsoid(self.x, self.length/2, self.max_radius) + + def get_radius_ellipsoid(self, x_coordinates, a, b): + x_coordinates -= np.mean(x_coordinates) # move origin to center + y_coordinates = b*np.sqrt(1-(x_coordinates/a)**2) + return y_coordinates + + def set_beam_properties(self): + # number of nodes + self.n_node = self.n_elem*2+1 + # coordinates + self.x = np.linspace(0, self.length, self.n_node) + self.y = np.zeros((self.n_node,)) + self.z = np.zeros((self.n_node,)) + + self.frame_of_reference_delta = np.zeros((self.n_elem, self.n_node_elem, 3)) + self.conn = np.zeros((self.n_elem, self.n_node_elem), dtype=int) + for ielem in range(self.n_elem): + self.conn[ielem, :] = ((np.ones((3, )) * ielem * (self.n_node_elem - 1)) + + [0, 2, 1]) + for ilocalnode in range(self.n_node_elem): + self.frame_of_reference_delta[ielem, ilocalnode, :] = [0.0, 1.0, 0.0] + + + self.beam_number = np.zeros((self.n_elem, ), dtype=int) + self.boundary_conditions = np.zeros((self.n_node, ), dtype=int) + self.boundary_conditions[0] = -1 + self.boundary_conditions[-1] = -1 + self.boundary_conditions[self.n_node//2-1] = 1 + + self.structural_twist = np.zeros((self.n_elem, self.n_node_elem)) + + def set_stiffness_and_mass_propoerties(self): + n_material = 1 # body + self.stiffness = np.zeros((n_material, 6, 6)) + self.mass = np.zeros((n_material, 6, 6)) + self.elem_stiffness = np.zeros((self.n_elem, ), dtype=int) + self.mass = np.zeros((n_material, 6, 6)) + self.elem_mass = np.zeros((self.n_elem, ), dtype=int) + + # Define aeroelastic properties (negligible here) + ea = 1e7 + ga = 1e5 + gj = 1e4 + eiy = 2e4 + eiz = 4e6 + m_bar_main = 0.75 + j_bar_main = 0.075 + base_stiffness_main = np.diag([ea, ga, ga, gj, eiy, eiz])*self.sigma_fuselage + base_stiffness_main[4, 4] = base_stiffness_main[5, 5] + + self.stiffness[0, ...] = base_stiffness_main + self.mass[0, ...] = np.diag([m_bar_main, m_bar_main, m_bar_main, j_bar_main, 0.1 * j_bar_main, 1.0 * j_bar_main]) + + def write_fuselage_input_file(self): + """ + Writes previously defined parameters to an .h5 file which serves later as an + input file for SHARPy. + """ + with h5.File(self.case_route + '/' + self.case_name + '.nonlifting_body.h5', 'a') as h5file: + h5file.create_dataset('shape', data='cylindrical') + h5file.create_dataset('surface_m', data=self.nonlifting_body_m) + h5file.create_dataset('nonlifting_body_node', data=self.nonlifting_body_node) + + h5file.create_dataset('surface_distribution', data=self.nonlifting_body_distribution) + h5file.create_dataset('radius', data=self.radius) + + def write_structural_input_file(self): + """ + Writes previously defined parameters to an .h5 file which serves later as an + input file for SHARPy. + """ + with h5.File(self.case_route + '/' + self.case_name + '.fem.h5', 'a') as h5file: + h5file.create_dataset('coordinates', data=np.column_stack((self.x, self.y, self.z))) + h5file.create_dataset('connectivities', data=self.conn) + h5file.create_dataset('num_node_elem', data=self.n_node_elem) + h5file.create_dataset('num_node', data=self.n_node) + h5file.create_dataset('num_elem', data=self.n_elem) + h5file.create_dataset('stiffness_db', data=self.stiffness) + h5file.create_dataset('elem_stiffness', data=self.elem_stiffness) + h5file.create_dataset('mass_db', data=self.mass) + h5file.create_dataset('elem_mass', data=self.elem_mass) + h5file.create_dataset('frame_of_reference_delta', data=self.frame_of_reference_delta) + h5file.create_dataset('boundary_conditions', data=self.boundary_conditions) + h5file.create_dataset('beam_number', data=self.beam_number) + + h5file.create_dataset('structural_twist', data=self.structural_twist) + h5file.create_dataset('app_forces', data=np.zeros((self.n_node, 6))) + + def create_settings(self, settings): + file_name = self.case_route + '/' + self.case_name + '.sharpy' + config = configobj.ConfigObj() + config.filename = file_name + for k, v in settings.items(): + config[k] = v + config.write() + self.settings = settings + + def run(self): + sharpy.sharpy_main.main(['', self.case_route + '/' + self.case_name + '.sharpy']) diff --git a/tests/uvlm/static/nonlifting/test_ellipsoid.py b/tests/uvlm/static/nonlifting/test_ellipsoid.py new file mode 100644 index 000000000..bdce44661 --- /dev/null +++ b/tests/uvlm/static/nonlifting/test_ellipsoid.py @@ -0,0 +1,101 @@ +import unittest +import os +import numpy as np +from fuselage import Fuselage +from define_simulation_settings import define_simulation_settings + + + +class TestFuselage(unittest.TestCase): + route_test_dir = os.path.abspath(os.path.dirname(os.path.realpath(__file__))) + + + def test_ellipsoid(self): + """ + Computes the pressure distribution over an ellipsoid. The results should + match the analyitcal solution according to potential flow theory (see Chapter 5 + Katz, J., and Plotkin, A., Low-speed aerodynamics, Vol. 13, Cambridge University + Press, 2001). + """ + + # define model variables + radius_ellipsoid = 0.2 + length_ellipsoid = 2. + u_inf = 10 + alpha_deg = 0 + n_elem = 21 + num_radial_panels = 12 + + # define case name and folders + case_route = self.route_test_dir + '/cases/' + output_route = self.route_test_dir + '/output/' + if not os.path.exists(case_route): + os.makedirs(case_route) + case_name = 'ellipsoid' + + # generate ellipsoid model + ellipsoidal_body = Fuselage(case_name, case_route, output_route) + ellipsoidal_body.generate( + num_radial_panels = num_radial_panels, + max_radius = radius_ellipsoid, + fuselage_shape = 'ellipsoid', + length = length_ellipsoid, + n_elem = n_elem) + + # define settings + flow = ['BeamLoader', + 'NonliftingbodygridLoader', + 'StaticUvlm', + 'WriteVariablesTime' + ] + settings = define_simulation_settings(flow, ellipsoidal_body, alpha_deg, u_inf, lifting_only=False, nonlifting_only=True, horseshoe=True) + ellipsoidal_body.create_settings(settings) + + # run simulation + ellipsoidal_body.run() + + # postprocess + cp_distribution_SHARPy = self.load_pressure_distribution(output_route + '/' + case_name + '/WriteVariablesTime/', ellipsoidal_body.n_node - 1) + x_collocation_points = ellipsoidal_body.x[:-1] + np.diff(ellipsoidal_body.x[:2])/2 + cp_distribution_analytcal = self.get_analytical_pressure_distribution(radius_ellipsoid, x_collocation_points) + + # check results + with self.subTest('pressure_coefficient'): + # Higher deviations near the nose and tail due to local coarser discretisation. Check only mid-body values! + np.testing.assert_array_almost_equal(cp_distribution_SHARPy[4:-4], cp_distribution_analytcal[4:-4], decimal=3) + + def load_pressure_distribution(self, output_folder, n_collocation_points): + """ + Loads the resulting pressure coefficients saved in txt-files. + """ + cp_distribution = np.zeros((n_collocation_points,)) + for i_collocation_point in range(n_collocation_points): + cp_distribution[i_collocation_point] = np.loadtxt(output_folder + 'nonlifting_pressure_coefficients_panel_isurf0_im0_in{}.dat'.format(i_collocation_point))[1] + return cp_distribution + + def get_analytical_pressure_distribution(self, radius, x_coordinates): + """ + Computes the analytical solution of the pressure distribution over + an ellipsoid in potential flow for the previous specified ellipsoid + model. Equations used are taken from + https://www.symscape.com/examples/panel/potential_flow_ellipsoid + """ + a = np.sqrt(1 - radius**2) + b = 2 * ((1-a**2)/a**3) * (np.arctanh(a)-a) + u = 2./(2.-b) * np.sqrt((1-x_coordinates**2)/(1-x_coordinates**2 * a**2)) + return 1-u**2 + + def tearDown(self): + """ + Removes all created files within this test. + """ + import shutil + folders = ['cases', 'output'] + for folder in folders: + shutil.rmtree(self.route_test_dir + '/' + folder) + + +if __name__ == '__main__': + import unittest + + unittest.main() From 58134324f595d90dc91525020c334c63f27aa44a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stefanie=20D=C3=BCssler?= Date: Wed, 19 Jul 2023 11:19:47 +0800 Subject: [PATCH 119/232] add [cases] fuselage wing as template class --- .../fuselage_wing_configuration.py | 69 ++++++ .../fuselage_wing_configuration/fwc_aero.py | 127 ++++++++++ .../fwc_fuselage.py | 121 +++++++++ .../fwc_structure.py | 233 ++++++++++++++++++ 4 files changed, 550 insertions(+) create mode 100644 tests/uvlm/static/nonlifting/fuselage_wing_configuration/fuselage_wing_configuration.py create mode 100644 tests/uvlm/static/nonlifting/fuselage_wing_configuration/fwc_aero.py create mode 100644 tests/uvlm/static/nonlifting/fuselage_wing_configuration/fwc_fuselage.py create mode 100644 tests/uvlm/static/nonlifting/fuselage_wing_configuration/fwc_structure.py diff --git a/tests/uvlm/static/nonlifting/fuselage_wing_configuration/fuselage_wing_configuration.py b/tests/uvlm/static/nonlifting/fuselage_wing_configuration/fuselage_wing_configuration.py new file mode 100644 index 000000000..9f0db4440 --- /dev/null +++ b/tests/uvlm/static/nonlifting/fuselage_wing_configuration/fuselage_wing_configuration.py @@ -0,0 +1,69 @@ +import configobj +from .fwc_structure import FWC_Structure +from .fwc_aero import FWC_Aero +from .fwc_fuselage import FWC_Fuselage +import os +import sharpy.sharpy_main + + +class Fuselage_Wing_Configuration: + """ + Fuselage_Wing_Configuration is a template class to create an + aircraft model of a simple wing-fuselage configuration within + SHARPy. + + """ + + def __init__(self, case_name, case_route, output_route): + self.case_name = case_name + self.case_route = case_route + self.output_route = output_route + + self.structure = None + self.aero = None + self.fuselage = None + + self.settings = None + + def init_aeroelastic(self, **kwargs): + self.clean() + self.init_structure(**kwargs) + self.init_aero(**kwargs) + if not kwargs.get('lifting_only', True): + self.init_fuselage(**kwargs) + + def init_structure(self, **kwargs): + self.structure = FWC_Structure(self.case_name, self.case_route, **kwargs) + + def init_aero(self, **kwargs): + self.aero = FWC_Aero(self.structure, self.case_name, self.case_route,**kwargs) + + def init_fuselage(self, **kwargs): + self.fuselage = FWC_Fuselage(self.structure, self.case_name, self.case_route,**kwargs) + def generate(self): + if not os.path.isdir(self.case_route): + os.makedirs(self.case_route) + self.structure.generate() + self.aero.generate() + if self.fuselage is not None: + self.fuselage.generate() + + def create_settings(self, settings): + file_name = self.case_route + '/' + self.case_name + '.sharpy' + config = configobj.ConfigObj() + config.filename = file_name + for k, v in settings.items(): + config[k] = v + config.write() + self.settings = settings + + def clean(self): + list_files = ['.fem.h5', '.aero.h5', '.nonlifting_body.h5', '.dyn.h5', '.mb.h5', '.sharpy', '.flightcon.txt'] + for file in list_files: + path_file = self.case_route + '/' + self.case_name + file + if os.path.isfile(path_file): + os.remove(path_file) + + def run(self): + sharpy.sharpy_main.main(['', self.case_route + '/' + self.case_name + '.sharpy']) + diff --git a/tests/uvlm/static/nonlifting/fuselage_wing_configuration/fwc_aero.py b/tests/uvlm/static/nonlifting/fuselage_wing_configuration/fwc_aero.py new file mode 100644 index 000000000..55e5c2356 --- /dev/null +++ b/tests/uvlm/static/nonlifting/fuselage_wing_configuration/fwc_aero.py @@ -0,0 +1,127 @@ +import h5py as h5 +import numpy as np + + +class FWC_Aero: + """ + FWC_Aero contains all attributes to define the aerodynamic grid and makes it accessible for SHARPy. + """ + def __init__(self, structure, case_name, case_route, **kwargs): + """ + Key-Word Arguments: + - structure: structural object of BFF model + """ + self.structure = structure + + self.route = case_route + self.case_name = case_name + + self.ea_wing = kwargs.get('elastic_axis',0.5) + self.num_chordwise_panels = kwargs.get('num_chordwise_panels', 4) + self.chord_wing = kwargs.get('chord', 1.) + self.n_surfaces = 2 + self.radius_fuselage = kwargs.get('max_radius', 0.5) + self.lifting_only = kwargs.get('lifting_only', True) + + + def generate(self): + """ + Function to set up all necessary parameter inputs to define the geometry and discretisation + of the lifting surfaces. Finally, informations are saved to an .h5 file serving as an input + file for SHARPy. + """ + self.initialize_attributes() + self.set_wing_properties() + self.set_junction_boundary_conditions() + self.write_input_file() + + def initialize_attributes(self): + """ + Initilializes all necessary attributes for the aero.h5 input file based on the number of + nodes, elements, and surfaces of the model. + """ + self.airfoil_distribution = np.zeros((self.structure.n_elem, self.structure.n_node_elem), dtype=int) + self.surface_distribution = np.zeros((self.structure.n_elem,), dtype=int) + self.surface_m = np.zeros((self.n_surfaces, ), dtype=int) + self.num_chordwise_panels + self.aero_node = np.zeros((self.structure.n_node,), dtype=bool) + + self.twist = np.zeros((self.structure.n_elem, self.structure.n_node_elem)) + self.sweep = np.zeros_like(self.twist) + self.chord = np.zeros_like(self.twist) + self.elastic_axis = np.zeros_like(self.twist) + + self.junction_boundary_condition_aero = np.zeros((1, self.n_surfaces), dtype=int) - 1 + + def set_wing_properties(self): + """ + Sets necessary parameters to define the lifting surfaces of one wing (right). + """ + + self.aero_node[:self.structure.n_node_wing_total] = True + if not self.lifting_only: + self.aero_node[:self.structure.n_node_right_wing] = abs(self.structure.y[:self.structure.n_node_right_wing]) > self.radius_fuselage + self.aero_node[self.structure.n_node_right_wing:self.structure.n_node_wing_total] = self.aero_node[1:self.structure.n_node_right_wing] + + self.chord[:2*self.structure.n_elem_per_wing, :] = self.chord_wing + self.elastic_axis[:2*self.structure.n_elem_per_wing, :] = self.ea_wing + # surf distribution 0 for right and 1 for left wing + self.surface_distribution[self.structure.n_elem_per_wing:2*self.structure.n_elem_per_wing] = 1 + + def set_junction_boundary_conditions(self): + """ + Sets the boundary conditions for the fuselage-wing junction. These BCs + define the partner surface. + """ + # Right wing (surface 0) has the left wing (surface 1) as a partner surface. + self.junction_boundary_condition_aero[0, 0] = 1 + # Left wing (surface 1) has the left wing (surface 0) as a partner surface. + self.junction_boundary_condition_aero[0, 1] = 0 + + def write_input_file(self): + """ + Writes previously defined parameters to an .h5 file which serves later as an + input file for SHARPy. + """ + + with h5.File(self.route + '/' + self.case_name + '.aero.h5', 'a') as h5file: + airfoils_group = h5file.create_group('airfoils') + # add one airfoil + airfoils_group.create_dataset('0', + data=np.column_stack( + self.generate_naca_camber(P=0, M=0) + )) + + h5file.create_dataset('chord', data=self.chord) + h5file.create_dataset('twist', data=self.twist) + h5file.create_dataset('sweep', data=self.sweep) + + # airfoil distribution + h5file.create_dataset('airfoil_distribution', data=self.airfoil_distribution) + h5file.create_dataset('surface_distribution', data=self.surface_distribution) + h5file.create_dataset('surface_m', data=self.surface_m) + h5file.create_dataset('m_distribution', data='uniform') + h5file.create_dataset('aero_node', data=self.aero_node) + h5file.create_dataset('elastic_axis', data=self.elastic_axis) + h5file.create_dataset('junction_boundary_condition', data=self.junction_boundary_condition_aero) + + + def generate_naca_camber(self,M=0, P=0): + """ + Generates the camber line coordinates of a specified NACA airfoil. + # TODO: needed? + """ + mm = M*1e-2 + p = P*1e-1 + + def naca(x, mm, p): + if x < 1e-6: + return 0.0 + elif x < p: + return mm/(p*p)*(2*p*x - x*x) + elif x > p and x < 1+1e-6: + return mm/((1-p)*(1-p))*(1 - 2*p + 2*p*x - x*x) + + x_vec = np.linspace(0, 1, 1000) + y_vec = np.array([naca(x, mm, p) for x in x_vec]) + + return x_vec, y_vec diff --git a/tests/uvlm/static/nonlifting/fuselage_wing_configuration/fwc_fuselage.py b/tests/uvlm/static/nonlifting/fuselage_wing_configuration/fwc_fuselage.py new file mode 100644 index 000000000..2a37f52a9 --- /dev/null +++ b/tests/uvlm/static/nonlifting/fuselage_wing_configuration/fwc_fuselage.py @@ -0,0 +1,121 @@ +#! /usr/bin/env python3 +import h5py as h5 +import numpy as np +import matplotlib.pyplot as plt + + +class FWC_Fuselage: + """ + FWC_Fuselage contains all attributes to define the nonlifting body grid + representing the fuselage shape and makes it accessible for SHARPy. + """ + def __init__(self, structure, case_name, case_route, **kwargs): + """ + Key-Word Arguments: + - structure: structural object of BFF model + """ + self.num_radial_panels = kwargs.get('num_radial_panels', 12) + self.max_radius = kwargs.get('max_radius') + self.fuselage_shape = kwargs.get('fuselage_shape','cylindrical') + self.flag_plot_radius = kwargs.get('flag_plot_radius', False) + self.structure = structure + + self.route = case_route + self.case_name = case_name + self.n_nonlifting_bodies = 1 + + def generate(self): + """ + Function to set up all necessary parameter inputs to define the geometry and discretisation + of the nonlifting surfaces. Finally, informations are saved to an .h5 file serving as an input + file for SHARPy. + """ + + self.initialize_parameters() + + self.nonlifting_body_node[0] = True + self.nonlifting_body_node[self.structure.n_node_wing_total:] = True + self.nonlifting_body_distribution[self.structure.n_elem_per_wing*2:] = 0 + self.nonlifting_body_m[0] = self.num_radial_panels + + self.get_fuselage_geometry() + + self.write_fuselage_input_file() + + + def initialize_parameters(self): + """ + Initilializes all necessary attributes for the nonlifting.h5 input file based on the number of + nodes, elements, and surfaces of the nonlifting model. + """ + self.nonlifting_body_node = np.zeros((self.structure.n_node,), dtype=bool) + self.nonlifting_body_distribution = np.zeros((self.structure.n_elem,), dtype=int) - 1 + self.nonlifting_body_m = np.zeros((self.n_nonlifting_bodies, ), dtype=int) + self.radius = np.zeros((self.structure.n_node,)) + + def get_fuselage_geometry(self): + x_coord_fuselage_sorted = np.sort(self.get_values_at_fuselage_nodes(self.structure.x)) + if self.fuselage_shape == 'cylindrical': + radius_fuselage = self.create_fuselage_geometry(x_coord_fuselage_sorted.copy(), 0.2*self.structure.fuselage_length, 0.8*self.structure.fuselage_length) + elif self.fuselage_shape == 'ellipsoid': + radius_fuselage = self.get_radius_ellipsoid(x_coord_fuselage_sorted.copy(), self.structure.fuselage_length/2, self.max_radius) + else: + raise "ERROR Fuselage shape {} unknown.".format(self.fuselage_shape) + self.radius[0] = radius_fuselage[self.structure.idx_junction] + self.radius[self.structure.n_node_wing_total:] = np.delete(radius_fuselage, self.structure.idx_junction) + if self.flag_plot_radius: + self.plot_fuselage_radius(x_coord_fuselage_sorted, radius_fuselage) + + def get_values_at_fuselage_nodes(self, array): + return array[self.nonlifting_body_node] + + def plot_fuselage_radius(self, x, radius): + plt.scatter(x, + radius) + plt.grid() + plt.xlabel("x, m") + plt.ylabel("r, m") + plt.gca().set_aspect('equal') + plt.show() + + def write_fuselage_input_file(self): + with h5.File(self.route + '/' + self.case_name + '.nonlifting_body.h5', 'a') as h5file: + h5file.create_dataset('shape', data='cylindrical') + h5file.create_dataset('surface_m', data=self.nonlifting_body_m) + h5file.create_dataset('nonlifting_body_node', data=self.nonlifting_body_node) + h5file.create_dataset('surface_distribution', data=self.nonlifting_body_distribution) + h5file.create_dataset('radius', data=self.radius) + + def get_radius_ellipsoid(self, x_coordinates, a, b): + x_coordinates[:] -= x_coordinates[-1] - a + y_coordinates = b*np.sqrt(1-(x_coordinates/a)**2) + return y_coordinates + + def find_index_of_closest_entry(self, array_values, target_value): + return np.argmin(np.abs(array_values - target_value)) + + + def create_fuselage_geometry(self, x_coord_fuselage, x_nose_end, x_tail_start): + array_radius = np.zeros_like(x_coord_fuselage) + # start with nose at zero to get indices of nose and tail correct + x_coord_fuselage[:] -= x_coord_fuselage[0] + idx_cylinder_start = self.find_index_of_closest_entry(x_coord_fuselage, x_nose_end) + idx_cylinder_end = self.find_index_of_closest_entry(x_coord_fuselage, x_tail_start) + + # set constant radius of cylinder + array_radius[idx_cylinder_start:idx_cylinder_end] = self.max_radius + # get ellipsoidal nose and tail shape + array_radius[:idx_cylinder_start+1] = self.get_nose_shape(x_coord_fuselage[:idx_cylinder_start+1], + self.max_radius) + + array_radius[idx_cylinder_end-1:] = np.flip(self.get_nose_shape(x_coord_fuselage[idx_cylinder_end-1:], + self.max_radius)) + + return array_radius + + def get_nose_shape(self, x_coord, radius): + n_nodes = len(x_coord) + x_coord[:] -= x_coord[-1] + x_coord = np.concatenate((x_coord, np.flip(x_coord[:-1]))) + radius = self.get_radius_ellipsoid(x_coord, x_coord[0], radius) + return radius[:n_nodes] diff --git a/tests/uvlm/static/nonlifting/fuselage_wing_configuration/fwc_structure.py b/tests/uvlm/static/nonlifting/fuselage_wing_configuration/fwc_structure.py new file mode 100644 index 000000000..f4f48d62c --- /dev/null +++ b/tests/uvlm/static/nonlifting/fuselage_wing_configuration/fwc_structure.py @@ -0,0 +1,233 @@ +import numpy as np +import h5py as h5 + + +class FWC_Structure: + + """ + FWC_Structure contains all attributes to define the beam geometriy + and discretisation and makes it accessible for SHARPy. + """ + def __init__(self, case_name, case_route, **kwargs): + self.sigma = kwargs.get('sigma', 1) + self.n_elem_multiplier = kwargs.get('n_elem_multiplier', 1) + + self.route = case_route + self.case_name = case_name + + self.n_node_elem = 3 + self.n_surfaces = 2 + self.n_material = 2 # wing + fuselage + + self.half_wingspan = kwargs.get('half_wingspan', 2) + self.fuselage_length = kwargs.get('fuselage_length', 10) + self.offset_nose_wing_beam = kwargs.get('offset_nose_wing', self.fuselage_length/2) + + self.n_elem_per_wing = kwargs.get('n_elem_per_wing', 10) + self.n_elem_fuselage = kwargs.get('n_elem_fuselage', 10) + + self.sigma = kwargs.get('sigma', 1) + self.sigma_fuselage = kwargs.get('sigma_fuselage', 10.) + + + + self.enforce_uniform_fuselage_discretisation = kwargs.get( + 'enforce_uniform_fuselage_discretisation', False + ) + self.fuselage_discretisation = kwargs.get('fuselage_discretisation', 'uniform') + + self.thrust = 0. + + def generate(self): + """ + Function to set up all necessary parameter inputs to define the geometry and discretisation + of the beam. Finally, informations are saved to an .h5 file serving as an input + file for SHARPy. + """ + self.set_element_and_nodes() + self.initialize_parameters() + + self.set_stiffness_and_mass_propoerties() + self.set_beam_properties_right_wing() + self.mirror_wing_beam() + self.app_forces[0] = [0, self.thrust, 0, 0, 0, 0] + + self.set_beam_properties_fuselage() + + np.savetxt('./coordinates.csv', np.column_stack((self.x, self.y, self.z))) + self.write_input_file() + + def set_element_and_nodes(self): + """ + Based on the specified number of elements of the wing and fuselage, the number of nodes of each + component and the total number of elements and nodes are defined here. + """ + + self.n_node_fuselage = self.n_elem_fuselage*(self.n_node_elem - 1) + 1 + self.n_node_right_wing = self.n_elem_per_wing*(self.n_node_elem - 1) + 1 + # the left wing beam has one node less than the right one, since they shares the center node + self.n_node_left_wing = self.n_node_right_wing - 1 + + self.n_node_wing_total = self.n_node_right_wing + self.n_node_left_wing + self.n_node = self.n_node_fuselage + self.n_node_wing_total + self.n_elem =self.n_elem_fuselage + 2 * self.n_elem_per_wing + + def initialize_parameters(self): + self.x = np.zeros((self.n_node, )) + self.y = np.zeros((self.n_node, )) + self.z = np.zeros((self.n_node, )) + + self.frame_of_reference_delta = np.zeros((self.n_elem, self.n_node_elem, 3)) + self.conn = np.zeros((self.n_elem, self.n_node_elem), dtype=int) + + self.beam_number = np.zeros((self.n_elem, ), dtype=int) + self.boundary_conditions = np.zeros((self.n_node, ), dtype=int) + self.structural_twist = np.zeros((self.n_elem, self.n_node_elem)) + + self.app_forces = np.zeros((self.n_node, 6)) + + + self.stiffness = np.zeros((self.n_material, 6, 6)) + self.mass = np.zeros((self.n_material, 6, 6)) + self.elem_stiffness = np.zeros((self.n_elem, ), dtype=int) + self.mass = np.zeros((self.n_material, 6, 6)) + self.elem_mass = np.zeros((self.n_elem, ), dtype=int) + + def set_stiffness_and_mass_propoerties(self): + # Define aeroelastic properties + ea = 1e7 + ga = 1e5 + gj = 1e4 + eiy = 2e4 + eiz = 4e6 + m_bar_main = 0.75 + j_bar_main = 0.075 + + m_bar_fuselage = 0.3*1.5 + j_bar_fuselage = 0.08 + + base_stiffness_main = self.sigma*np.diag([ea, ga, ga, gj, eiy, eiz]) + base_stiffness_fuselage = base_stiffness_main.copy()*self.sigma_fuselage + base_stiffness_fuselage[4, 4] = base_stiffness_fuselage[5, 5] + + self.stiffness[0, ...] = base_stiffness_main + self.stiffness[1, ...] = base_stiffness_fuselage + + self.mass[0, ...] = self.generate_mass_matrix(m_bar_main, j_bar_main) + self.mass[1, ...] = self.generate_mass_matrix(m_bar_fuselage, j_bar_fuselage) + + def generate_mass_matrix(self, m_bar, j_bar): + return np.diag([m_bar, m_bar, m_bar, + j_bar, 0.5*j_bar, 0.5*j_bar]) + + def set_beam_properties_right_wing(self): + """ + Defines all necessary parameters to define the beam including node coordinate, + elements with their associated nodes, frame of reference delta, boundary conditions + such as reference node and free tips, stiffness and mass property ID for each element, + and twist. + """ + self.y[:self.n_node_right_wing] = np.linspace(0, self.half_wingspan, self.n_node_right_wing) + + for ielem in range(self.n_elem_per_wing): + self.conn[ielem, :] = ((np.ones((3, )) * ielem * (self.n_node_elem - 1)) + + [0, 2, 1]) + for ilocalnode in range(self.n_node_elem): + self.frame_of_reference_delta[ielem, ilocalnode, :] = [-1.0, 0.0, 0.0] + + self.boundary_conditions[0] = 1 + self.boundary_conditions[self.n_node_right_wing] = -1 # free tip + + def mirror_wing_beam(self): + """ + Mirrors the parameters from the beam representing the right free-flying wing + for the left one. + """ + + self.x[self.n_node_right_wing:self.n_node_wing_total] = self.x[1:self.n_node_right_wing] + self.y[self.n_node_right_wing:self.n_node_wing_total] = - self.y[1:self.n_node_right_wing] + self.z[self.n_node_right_wing:self.n_node_wing_total] = self.z[1:self.n_node_right_wing] + self.frame_of_reference_delta[self.n_elem_per_wing:2*self.n_elem_per_wing, :, :] = self.frame_of_reference_delta[:self.n_elem_per_wing, :, :] * (-1) + self.elem_stiffness[self.n_elem_per_wing:2*self.n_elem_per_wing] = self.elem_stiffness[:self.n_elem_per_wing] + self.elem_mass[self.n_elem_per_wing:2*self.n_elem_per_wing] = self.elem_mass[:self.n_elem_per_wing] + + self.beam_number[self.n_elem_per_wing:2*self.n_elem_per_wing] = 1 + self.boundary_conditions[self.n_node_wing_total] = -1 # free tip + self.conn[self.n_elem_per_wing:2*self.n_elem_per_wing, :] = self.conn[:self.n_elem_per_wing, :] + self.n_node_right_wing - 1 + self.conn[self.n_elem_per_wing, 0] = 0 + + def set_x_coordinate_fuselage(self): + if self.fuselage_discretisation == 'uniform': + x_coord_fuselage = np.linspace(0, self.fuselage_length, self.n_node_fuselage + 1) - self.offset_nose_wing_beam + # print(self.x) + elif self.fuselage_discretisation == '1-cosine': + x_coord_fuselage = np.linspace(0, 1, self.n_node_fuselage + 1) + x_coord_fuselage = 0.5*(1.0 - np.cos(x_coord_fuselage*np.pi)) + x_coord_fuselage *= self.fuselage_length + x_coord_fuselage -= self.offset_nose_wing_beam + else: + raise "ERROR Specified fuselage discretisation '{}' unknown".format(self.fuselage_discretisation) + self.idx_junction = self.find_index_of_closest_entry(x_coord_fuselage, self.x[0]) + if self.enforce_uniform_fuselage_discretisation: + self.x[:self.n_node_wing_total] += x_coord_fuselage[self.idx_junction] + + x_coord_fuselage = np.delete(x_coord_fuselage, self.idx_junction) + self.x[self.n_node_wing_total:] = x_coord_fuselage + + def adjust_fuselage_connectivities(self): + idx_junction_global = self.idx_junction + self.n_node_wing_total + idx_in_conn = np.where(self.conn == idx_junction_global) + + self.conn[idx_in_conn[0][0]+1, :] -= 1 + if idx_in_conn[1][0] == 2: + # if middle node, correct end node of element + self.conn[idx_in_conn[0][0], 1] -= 1 + for i_match in range(np.shape(idx_in_conn)[1]): + #several matches possible if junction node is not middle node + self.conn[idx_in_conn[0][i_match], idx_in_conn[1][i_match]] = 0 + + def set_beam_properties_fuselage(self): + self.set_x_coordinate_fuselage() + + for ielem in range(self.n_elem_per_wing * 2,self.n_elem): + self.conn[ielem, :] = ((np.ones((3, )) * (ielem-self.n_elem_per_wing * 2) * (self.n_node_elem - 1)) + + [0, 2, 1]) + self.n_node_wing_total + for ilocalnode in range(self.n_node_elem): + self.frame_of_reference_delta[ielem, ilocalnode, :] = [0.0, 1.0, 0.0] + self.elem_stiffness[self.n_elem_per_wing*2:] = 1 + self.elem_mass[self.n_elem_per_wing*2:] = 2 + + + self.adjust_fuselage_connectivities() + self.boundary_conditions[self.n_node_wing_total] = -1 + self.boundary_conditions[-1] = -1 + + np.savetxt("./conn.csv", self.conn) + + def find_index_of_closest_entry(self, array_values, target_value): + return np.argmin(np.abs(array_values - target_value)) + + def set_thrust(self, value): + self.thrust = value + + def write_input_file(self): + """ + Writes previously defined parameters to an .h5 file which serves later as an + input file for SHARPy. + """ + with h5.File(self.route + '/' + self.case_name + '.fem.h5', 'a') as h5file: + h5file.create_dataset('coordinates', data=np.column_stack((self.x, self.y, self.z))) + h5file.create_dataset('connectivities', data=self.conn) + h5file.create_dataset('num_node_elem', data=self.n_node_elem) + h5file.create_dataset('num_node', data=self.n_node) + h5file.create_dataset('num_elem', data=self.n_elem) + h5file.create_dataset('stiffness_db', data=self.stiffness) + h5file.create_dataset('elem_stiffness', data=self.elem_stiffness) + h5file.create_dataset('mass_db', data=self.mass) + h5file.create_dataset('elem_mass', data=self.elem_mass) + h5file.create_dataset('frame_of_reference_delta', data=self.frame_of_reference_delta) + h5file.create_dataset('boundary_conditions', data=self.boundary_conditions) + h5file.create_dataset('beam_number', data=self.beam_number) + + h5file.create_dataset('structural_twist', data=self.structural_twist) + h5file.create_dataset('app_forces', data=self.app_forces) From 6c11d269c383505ed3eee60644f8c05fa8903ba8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stefanie=20D=C3=BCssler?= Date: Wed, 19 Jul 2023 11:21:42 +0800 Subject: [PATCH 120/232] add [tests] replace ellipsoid model by fuselage wing configuration template --- .../nonlifting/define_simulation_settings.py | 27 +-- tests/uvlm/static/nonlifting/fuselage.py | 156 ------------------ .../uvlm/static/nonlifting/test_ellipsoid.py | 37 +++-- 3 files changed, 38 insertions(+), 182 deletions(-) delete mode 100644 tests/uvlm/static/nonlifting/fuselage.py diff --git a/tests/uvlm/static/nonlifting/define_simulation_settings.py b/tests/uvlm/static/nonlifting/define_simulation_settings.py index a4ed49e82..335eac25a 100644 --- a/tests/uvlm/static/nonlifting/define_simulation_settings.py +++ b/tests/uvlm/static/nonlifting/define_simulation_settings.py @@ -1,7 +1,11 @@ import numpy as np import sharpy.utils.algebra as algebra -def define_simulation_settings(flow, model, alpha_deg, u_inf, rho = 1.225, lifting_only=True, nonlifting_only=False, horseshoe=False, **kwargs): +def define_simulation_settings(flow, model, alpha_deg, u_inf, rho = 1.225, + lifting_only=True, + nonlifting_only=False, + phantom_test=False, + horseshoe=False, **kwargs): gravity = kwargs.get('gravity',True) nonlifting_body_interactions = not lifting_only and not nonlifting_only wake_length = kwargs.get('wake_length', 10) @@ -23,7 +27,6 @@ def define_simulation_settings(flow, model, alpha_deg, u_inf, rho = 1.225, lifti nonlifting_body_interactions = True else: nonlifting_body_interactions = False - flow.remove("NonliftingbodygridLoader") settings = {} settings['SHARPy'] = {'case': model.case_name, 'route': model.case_route, @@ -39,7 +42,7 @@ def define_simulation_settings(flow, model, alpha_deg, u_inf, rho = 1.225, lifti np.deg2rad(alpha_deg), 0.]))} - + settings['BeamLoads'] = {} settings['LiftDistribution'] = {'rho': rho, 'coefficients': True} @@ -83,18 +86,20 @@ def define_simulation_settings(flow, model, alpha_deg, u_inf, rho = 1.225, lifti 'dt': dt, }, } - - settings['WriteVariablesTime'] = { - 'cleanup_old_solution': True, - 'nonlifting_nodes_variables': ['pressure_coefficients'], - 'nonlifting_nodes_isurf': np.zeros((model.n_node - 1,)), - 'nonlifting_nodes_im': np.zeros((model.n_node - 1,)), - 'nonlifting_nodes_in': list(range(model.n_node - 1)), - } + if 'WriteVariablesTime' in flow: + settings['WriteVariablesTime'] = { + 'cleanup_old_solution': True, + 'nonlifting_nodes_variables': ['pressure_coefficients'], + 'nonlifting_nodes_isurf': np.zeros((model.structure.n_node_fuselage,)), + 'nonlifting_nodes_im': np.zeros((model.structure.n_node_fuselage)), + 'nonlifting_nodes_in': list(range(model.structure.n_node_fuselage)), + } settings['NonliftingbodygridLoader'] = {} settings['AeroForcesCalculator'] = {'coefficients': False} + settings['AerogridPlot'] = {'plot_nonlifting_surfaces': nonlifting_body_interactions} + settings['BeamPlot'] = {} return settings \ No newline at end of file diff --git a/tests/uvlm/static/nonlifting/fuselage.py b/tests/uvlm/static/nonlifting/fuselage.py deleted file mode 100644 index 13ef014f4..000000000 --- a/tests/uvlm/static/nonlifting/fuselage.py +++ /dev/null @@ -1,156 +0,0 @@ -import os -import numpy as np -import h5py as h5 -import configobj -import sharpy.sharpy_main - - -class Fuselage(): - def __init__(self, case_name, case_route, output_route): - self.case_name = case_name - self.case_route = case_route - self.output_route = output_route - - self.settings = None - - self.n_node_elem = 3 - - def clean(self): - list_files = ['.fem.h5', '.aero.h5', '.nonlifting_body.h5', '.dyn.h5', '.mb.h5', '.sharpy', '.flightcon.txt'] - for file in list_files: - path_file = self.case_route + '/' + self.case_name + file - if os.path.isfile(path_file): - os.remove(path_file) - - def generate_structure(self, **kwargs): - self.length = kwargs.get('length', 10) - self.n_elem = kwargs.get('n_elem', 11) - self.sigma_fuselage = kwargs.get('sigma_fuselage', 10.) - - self.set_beam_properties() - self.set_stiffness_and_mass_propoerties() - - self.write_structural_input_file() - - def generate(self, **kwargs): - self.clean() - self.generate_structure(**kwargs) - self.generate_fuselage(**kwargs) - - def generate_fuselage(self, **kwargs): - self.num_radial_panels = kwargs.get('num_radial_panels', 24) - self.max_radius = kwargs.get('max_radius', 2) # rename - self.n_nonlifting_bodies = 1 - self.fuselage_shape = kwargs.get('fuselage_shape', 'ellipsoid') - - - self.set_fuselage_properties() - self.write_fuselage_input_file() - - def set_fuselage_properties(self): - self.nonlifting_body_node = np.ones((self.n_node,), dtype=bool) - self.nonlifting_body_distribution = np.zeros((self.n_elem,), dtype=int) - self.nonlifting_body_m = np.zeros((self.n_nonlifting_bodies, ), dtype=int) + self.num_radial_panels - self.radius = np.zeros((self.n_node,)) - - if self.fuselage_shape == 'ellipsoid': - self.radius = self.get_radius_ellipsoid(self.x, self.length/2, self.max_radius) - - def get_radius_ellipsoid(self, x_coordinates, a, b): - x_coordinates -= np.mean(x_coordinates) # move origin to center - y_coordinates = b*np.sqrt(1-(x_coordinates/a)**2) - return y_coordinates - - def set_beam_properties(self): - # number of nodes - self.n_node = self.n_elem*2+1 - # coordinates - self.x = np.linspace(0, self.length, self.n_node) - self.y = np.zeros((self.n_node,)) - self.z = np.zeros((self.n_node,)) - - self.frame_of_reference_delta = np.zeros((self.n_elem, self.n_node_elem, 3)) - self.conn = np.zeros((self.n_elem, self.n_node_elem), dtype=int) - for ielem in range(self.n_elem): - self.conn[ielem, :] = ((np.ones((3, )) * ielem * (self.n_node_elem - 1)) + - [0, 2, 1]) - for ilocalnode in range(self.n_node_elem): - self.frame_of_reference_delta[ielem, ilocalnode, :] = [0.0, 1.0, 0.0] - - - self.beam_number = np.zeros((self.n_elem, ), dtype=int) - self.boundary_conditions = np.zeros((self.n_node, ), dtype=int) - self.boundary_conditions[0] = -1 - self.boundary_conditions[-1] = -1 - self.boundary_conditions[self.n_node//2-1] = 1 - - self.structural_twist = np.zeros((self.n_elem, self.n_node_elem)) - - def set_stiffness_and_mass_propoerties(self): - n_material = 1 # body - self.stiffness = np.zeros((n_material, 6, 6)) - self.mass = np.zeros((n_material, 6, 6)) - self.elem_stiffness = np.zeros((self.n_elem, ), dtype=int) - self.mass = np.zeros((n_material, 6, 6)) - self.elem_mass = np.zeros((self.n_elem, ), dtype=int) - - # Define aeroelastic properties (negligible here) - ea = 1e7 - ga = 1e5 - gj = 1e4 - eiy = 2e4 - eiz = 4e6 - m_bar_main = 0.75 - j_bar_main = 0.075 - base_stiffness_main = np.diag([ea, ga, ga, gj, eiy, eiz])*self.sigma_fuselage - base_stiffness_main[4, 4] = base_stiffness_main[5, 5] - - self.stiffness[0, ...] = base_stiffness_main - self.mass[0, ...] = np.diag([m_bar_main, m_bar_main, m_bar_main, j_bar_main, 0.1 * j_bar_main, 1.0 * j_bar_main]) - - def write_fuselage_input_file(self): - """ - Writes previously defined parameters to an .h5 file which serves later as an - input file for SHARPy. - """ - with h5.File(self.case_route + '/' + self.case_name + '.nonlifting_body.h5', 'a') as h5file: - h5file.create_dataset('shape', data='cylindrical') - h5file.create_dataset('surface_m', data=self.nonlifting_body_m) - h5file.create_dataset('nonlifting_body_node', data=self.nonlifting_body_node) - - h5file.create_dataset('surface_distribution', data=self.nonlifting_body_distribution) - h5file.create_dataset('radius', data=self.radius) - - def write_structural_input_file(self): - """ - Writes previously defined parameters to an .h5 file which serves later as an - input file for SHARPy. - """ - with h5.File(self.case_route + '/' + self.case_name + '.fem.h5', 'a') as h5file: - h5file.create_dataset('coordinates', data=np.column_stack((self.x, self.y, self.z))) - h5file.create_dataset('connectivities', data=self.conn) - h5file.create_dataset('num_node_elem', data=self.n_node_elem) - h5file.create_dataset('num_node', data=self.n_node) - h5file.create_dataset('num_elem', data=self.n_elem) - h5file.create_dataset('stiffness_db', data=self.stiffness) - h5file.create_dataset('elem_stiffness', data=self.elem_stiffness) - h5file.create_dataset('mass_db', data=self.mass) - h5file.create_dataset('elem_mass', data=self.elem_mass) - h5file.create_dataset('frame_of_reference_delta', data=self.frame_of_reference_delta) - h5file.create_dataset('boundary_conditions', data=self.boundary_conditions) - h5file.create_dataset('beam_number', data=self.beam_number) - - h5file.create_dataset('structural_twist', data=self.structural_twist) - h5file.create_dataset('app_forces', data=np.zeros((self.n_node, 6))) - - def create_settings(self, settings): - file_name = self.case_route + '/' + self.case_name + '.sharpy' - config = configobj.ConfigObj() - config.filename = file_name - for k, v in settings.items(): - config[k] = v - config.write() - self.settings = settings - - def run(self): - sharpy.sharpy_main.main(['', self.case_route + '/' + self.case_name + '.sharpy']) diff --git a/tests/uvlm/static/nonlifting/test_ellipsoid.py b/tests/uvlm/static/nonlifting/test_ellipsoid.py index bdce44661..739cab95f 100644 --- a/tests/uvlm/static/nonlifting/test_ellipsoid.py +++ b/tests/uvlm/static/nonlifting/test_ellipsoid.py @@ -1,7 +1,7 @@ import unittest import os import numpy as np -from fuselage import Fuselage +from fuselage_wing_configuration.fuselage_wing_configuration import Fuselage_Wing_Configuration from define_simulation_settings import define_simulation_settings @@ -23,24 +23,29 @@ def test_ellipsoid(self): length_ellipsoid = 2. u_inf = 10 alpha_deg = 0 - n_elem = 21 - num_radial_panels = 12 - + n_elem = 30 + num_radial_panels = 24 + lifting_only= False + fuselage_shape = 'ellipsoid' + fuselage_discretisation = 'uniform' # define case name and folders case_route = self.route_test_dir + '/cases/' output_route = self.route_test_dir + '/output/' - if not os.path.exists(case_route): - os.makedirs(case_route) case_name = 'ellipsoid' + enforce_uniform_fuselage_discretisation = True # generate ellipsoid model - ellipsoidal_body = Fuselage(case_name, case_route, output_route) - ellipsoidal_body.generate( - num_radial_panels = num_radial_panels, - max_radius = radius_ellipsoid, - fuselage_shape = 'ellipsoid', - length = length_ellipsoid, - n_elem = n_elem) + ellipsoidal_body = Fuselage_Wing_Configuration(case_name, case_route, output_route) + ellipsoidal_body.init_aeroelastic(lifting_only=lifting_only, + max_radius=radius_ellipsoid, + fuselage_length=length_ellipsoid, + offset_nose_wing=length_ellipsoid/2, + n_elem_fuselage=n_elem, + num_radial_panels=num_radial_panels, + fuselage_shape=fuselage_shape, + enforce_uniform_fuselage_discretisation=enforce_uniform_fuselage_discretisation, + fuselage_discretisation=fuselage_discretisation) + ellipsoidal_body.generate() # define settings flow = ['BeamLoader', @@ -55,8 +60,10 @@ def test_ellipsoid(self): ellipsoidal_body.run() # postprocess - cp_distribution_SHARPy = self.load_pressure_distribution(output_route + '/' + case_name + '/WriteVariablesTime/', ellipsoidal_body.n_node - 1) - x_collocation_points = ellipsoidal_body.x[:-1] + np.diff(ellipsoidal_body.x[:2])/2 + cp_distribution_SHARPy = self.load_pressure_distribution(output_route + '/' + case_name + '/WriteVariablesTime/', + ellipsoidal_body.structure.n_node_fuselage) + dx = length_ellipsoid/(ellipsoidal_body.structure.n_node_fuselage-1) + x_collocation_points = np.linspace(-length_ellipsoid/2+dx/2, length_ellipsoid/2-dx/2, ellipsoidal_body.structure.n_node_fuselage) cp_distribution_analytcal = self.get_analytical_pressure_distribution(radius_ellipsoid, x_collocation_points) # check results From 121808d8a23c9e0ed5dda066b6c1d60fb20ed694 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stefanie=20D=C3=BCssler?= Date: Wed, 19 Jul 2023 11:21:57 +0800 Subject: [PATCH 121/232] remove [cases] unused very old generation file --- .../fuselage_wing/generate_fuselage_wing.py | 739 ------------------ 1 file changed, 739 deletions(-) delete mode 100644 cases/coupled/fuselage_wing/generate_fuselage_wing.py diff --git a/cases/coupled/fuselage_wing/generate_fuselage_wing.py b/cases/coupled/fuselage_wing/generate_fuselage_wing.py deleted file mode 100644 index 80cf0d856..000000000 --- a/cases/coupled/fuselage_wing/generate_fuselage_wing.py +++ /dev/null @@ -1,739 +0,0 @@ -#! /usr/bin/env python3 -import h5py as h5 -import numpy as np -import os -import pandas as pd -import sharpy.utils.algebra as algebra - - -case_name = 'simple_wing_fuselage' -route = os.path.dirname(os.path.realpath(__file__)) + '/' - -# EXECUTION -flow = ['BeamLoader', - 'AerogridLoader', - # 'NonLinearStatic', - 'StaticUvlm', - #'StaticTrim', - # 'StaticCoupled', - 'BeamLoads', - 'AerogridPlot', - 'BeamPlot', - #'DynamicCoupled', - # 'Modal', - # 'LinearAssember', - # 'AsymptoticStability', - ] - - -# FLIGHT CONDITIONS -# the simulation is set such that the aircraft flies at a u_inf velocity while -# the air is calm. -u_inf = 10 -rho = 1.225 - -free_flight = True -if not free_flight: - case_name += '_prescribed' - amplitude = 0*np.pi/180 - period = 3 - case_name += '_amp_' + str(amplitude).replace('.', '') + '_period_' + str(period) - -alpha = 6.0*np.pi/180 -beta = 0 -roll = 0 -gravity = 'on' -thrust = 0 -sigma = 1.5 - -# gust settings -gust_intensity = 0.20 -gust_length = 1*u_inf -gust_offset = 0.5*u_inf - -# numerics -n_step = 5 -structural_relaxation_factor = 0.6 -relaxation_factor = 0.35 -tolerance = 1e-6 -fsi_tolerance = 1e-4 - -num_cores = 2 - -# MODEL GEOMETRY -# beam -span_main = 3.0 -ea_main = 0.3 - -ea = 1e7 -ga = 1e5 -gj = 1e4 -eiy = 2e4 -eiz = 4e6 -m_bar_main = 0.75 -j_bar_main = 0.075 - -length_fuselage = 10 -offset_fuselage_vertical = 0 -offset_fuselage_wing = 4 -radius_fuselage = 1.3333333333333333/2 -list_cylinder_position_fuselage = [0.3, 0.7] # percent where fuselage has cylinder shape -sigma_fuselage = 10 -m_bar_fuselage = 0.2 -j_bar_fuselage = 0.08 - -# lumped masses -n_lumped_mass = 1 -lumped_mass_nodes = np.zeros((n_lumped_mass, ), dtype=int) -lumped_mass = np.zeros((n_lumped_mass, )) -lumped_mass[0] = 50 -lumped_mass_inertia = np.zeros((n_lumped_mass, 3, 3)) -lumped_mass_position = np.zeros((n_lumped_mass, 3)) -lumped_mass_position[0] = offset_fuselage_wing - -# aero -chord_main = 1.0 - -# DISCRETISATION -# spatial discretisation -# chordiwse panels -m = 4 -m_radial_elem_fuselage = 12 -# spanwise elements -n_elem_multiplier = 2 -n_elem_main = int(2*n_elem_multiplier) #int(4*n_elem_multiplier) -n_elem_fuselage = 21 -n_surfaces = 2 -n_nonlifting_bodies = 1 - -# temporal discretisation -physical_time = 1 -tstep_factor = 1. -dt = 1.0/m/u_inf*tstep_factor -n_tstep = round(physical_time/dt) - -# END OF INPUT----------------------------------------------------------------- - -# beam processing -n_node_elem = 3 - -# total number of elements -n_elem = 0 -n_elem += n_elem_main + n_elem_main -n_elem += n_elem_fuselage - -# number of nodes per part -n_node_main = n_elem_main*(n_node_elem - 1) + 1 -n_node_fuselage = n_elem_fuselage*(n_node_elem - 1) + 1 - -# total number of nodes -n_node = 0 -n_node += n_node_main + n_node_main - 1 -n_node += n_node_fuselage - 1 - -# stiffness and mass matrices -n_stiffness = 2 -base_stiffness_main = sigma*np.diag([ea, ga, ga, gj, eiy, eiz]) -base_stiffness_fuselage = base_stiffness_main.copy()*sigma_fuselage -base_stiffness_fuselage[4, 4] = base_stiffness_fuselage[5, 5] - -n_mass = 2 -base_mass_main = np.diag([m_bar_main, m_bar_main, m_bar_main, j_bar_main, 0.5*j_bar_main, 0.5*j_bar_main]) -base_mass_fuselage = np.diag([m_bar_fuselage, - m_bar_fuselage, - m_bar_fuselage, - j_bar_fuselage, - j_bar_fuselage*0.5, - j_bar_fuselage*0.5]) - -# PLACEHOLDERS -# beam -x = np.zeros((n_node, )) -y = np.zeros((n_node, )) -z = np.zeros((n_node, )) -beam_number = np.zeros((n_elem, ), dtype=int) -frame_of_reference_delta = np.zeros((n_elem, n_node_elem, 3)) -structural_twist = np.zeros((n_elem, 3)) -conn = np.zeros((n_elem, n_node_elem), dtype=int) -stiffness = np.zeros((n_stiffness, 6, 6)) -elem_stiffness = np.zeros((n_elem, ), dtype=int) -mass = np.zeros((n_mass, 6, 6)) -elem_mass = np.zeros((n_elem, ), dtype=int) -boundary_conditions = np.zeros((n_node, ), dtype=int) -app_forces = np.zeros((n_node, 6)) - - -# aero -airfoil_distribution = np.zeros((n_elem, n_node_elem), dtype=int) -surface_distribution = np.zeros((n_elem,), dtype=int) - 1 -surface_m = np.zeros((n_surfaces, ), dtype=int) -m_distribution = 'uniform' -aero_node = np.zeros((n_node,), dtype=bool) -nonlifting_body_node = np.zeros((n_node,), dtype=bool) -twist = np.zeros((n_elem, n_node_elem)) -sweep = np.zeros((n_elem, n_node_elem)) -chord = np.zeros((n_elem, n_node_elem,)) -elastic_axis = np.zeros((n_elem, n_node_elem,)) -boundary_conditions_aero = np.zeros((n_node, ), dtype=int) - -# nonlifting body -nonlifting_body_distribution = np.zeros((n_elem,), dtype=int) - 1 -nonlifting_body_m = np.zeros((n_nonlifting_bodies, ), dtype=int) -radius = np.zeros((n_node,)) - - -# FUNCTIONS------------------------------------------------------------- -def clean_test_files(): - list_file_extension = ['.fem.h5', '.dyn.h5', '.aero.h5', - '.nonlifting_body.h5', '.sharpy', '.flightcon.txt'] - for file_extension in list_file_extension: - file = route + '/' + case_name + file_extension - if os.path.isfile(file): - os.remove(file) - -def find_index_of_closest_entry(array_values, target_value): - return (np.abs(array_values - target_value)).argmin() - -def generate_fem(): - stiffness[0, ...] = base_stiffness_main - stiffness[1, ...] = base_stiffness_fuselage - - mass[0, ...] = base_mass_main - mass[1, ...] = base_mass_fuselage - - we = 0 - wn = 0 - - # inner right wing - beam_number[we:we + n_elem_main] = 0 - x[wn:wn + n_node_main] = offset_fuselage_wing - y[wn:wn + n_node_main] = np.linspace(0, span_main, n_node_main) - y[wn:wn + n_node_main] += radius_fuselage - for ielem in range(n_elem_main): - conn[we + ielem, :] = ((np.ones((3, ))*(we + ielem)*(n_node_elem - 1)) + - [0, 2, 1]) - for inode in range(n_node_elem): - frame_of_reference_delta[we + ielem, inode, :] = [-1.0, 0.0, 0.0] - - elem_stiffness[we:we + n_elem_main] = 0 - elem_mass[we:we + n_elem_main] = 0 - boundary_conditions[wn] = 1 - boundary_conditions[wn + n_elem_main] = -1 - we += n_elem_main - wn += n_node_main - - # inner left wing - beam_number[we:we + n_elem_main] = 1 - x[wn:wn + n_node_main] = offset_fuselage_wing - y[wn:wn + n_node_main] = np.linspace(0, -span_main, n_node_main) - y[wn:wn + n_node_main] -= radius_fuselage - for ielem in range(n_elem_main): - conn[we + ielem, :] = ((np.ones((3, ))*(we+ielem)*(n_node_elem - 1)) + - 1 + [0, 2, 1]) - for inode in range(n_node_elem): - frame_of_reference_delta[we + ielem, inode, :] = [1.0, 0.0, 0.0] - elem_stiffness[we:we + n_elem_main] = 0 - elem_mass[we:we + n_elem_main] = 0 - boundary_conditions[wn] = 1 - boundary_conditions[wn + n_elem_main] = -1 - - we += n_elem_main - wn += n_node_main - - # fuselage - beam_number[we:we + n_elem_fuselage] = 2 - x[wn:wn + n_node_fuselage] = np.linspace(0.0, length_fuselage, n_node_fuselage-2) - z[wn:wn + n_node_fuselage] = np.linspace(0.0, offset_fuselage_vertical, n_node_fuselage-2) - - # adjust node closes to fuselage wing junction to be in the same z-y-plane than wing nodes - idx_fuselage_wing_junction = find_index_of_closest_entry(x[wn:wn + n_node_fuselage - 3], offset_fuselage_wing) - z[idx_fuselage_wing_junction] = np.interp(offset_fuselage_wing, x[wn:wn + n_node_fuselage - 3], z[wn:wn + n_node_fuselage - 3]) - x[idx_fuselage_wing_junction] = offset_fuselage_wing - - for ielem in range(n_elem_fuselage-1): - conn[we + ielem, :] = ((np.ones((3,))*(we + ielem)*(n_node_elem - 1)) + - 2 + [0, 2, 1]) - for inode in range(n_node_elem): - frame_of_reference_delta[we + ielem, inode, :] = [0.0, 1.0, 0.0] - conn[we+n_elem_fuselage-1,:] = np.array([conn[0,0],conn[n_elem_main,0],idx_fuselage_wing_junction]) - for inode in range(n_node_elem): - # TO-DO: Correct reference frame for wing junction beam - frame_of_reference_delta[we+n_elem_fuselage-1, inode, :] = [0.0, 1.0, 0.0] - elem_stiffness[we:we + n_elem_fuselage] = 1 - elem_mass[we:we + n_elem_fuselage] = 1 - - boundary_conditions[wn] = -1 - boundary_conditions[idx_fuselage_wing_junction] = 1 - boundary_conditions[wn + n_elem_main] = -1 - - we += n_elem_fuselage - wn += n_node_fuselage - - with h5.File(route + '/' + case_name + '.fem.h5', 'a') as h5file: - coordinates = h5file.create_dataset('coordinates', data=np.column_stack((x, y, z))) - conectivities = h5file.create_dataset('connectivities', data=conn) - num_nodes_elem_handle = h5file.create_dataset( - 'num_node_elem', data=n_node_elem) - num_nodes_handle = h5file.create_dataset( - 'num_node', data=n_node) - num_elem_handle = h5file.create_dataset( - 'num_elem', data=n_elem) - stiffness_db_handle = h5file.create_dataset( - 'stiffness_db', data=stiffness) - stiffness_handle = h5file.create_dataset( - 'elem_stiffness', data=elem_stiffness) - mass_db_handle = h5file.create_dataset( - 'mass_db', data=mass) - mass_handle = h5file.create_dataset( - 'elem_mass', data=elem_mass) - frame_of_reference_delta_handle = h5file.create_dataset( - 'frame_of_reference_delta', data=frame_of_reference_delta) - structural_twist_handle = h5file.create_dataset( - 'structural_twist', data=structural_twist) - bocos_handle = h5file.create_dataset( - 'boundary_conditions', data=boundary_conditions) - beam_handle = h5file.create_dataset( - 'beam_number', data=beam_number) - app_forces_handle = h5file.create_dataset( - 'app_forces', data=app_forces) - lumped_mass_nodes_handle = h5file.create_dataset( - 'lumped_mass_nodes', data=lumped_mass_nodes) - lumped_mass_handle = h5file.create_dataset( - 'lumped_mass', data=lumped_mass) - lumped_mass_inertia_handle = h5file.create_dataset( - 'lumped_mass_inertia', data=lumped_mass_inertia) - lumped_mass_position_handle = h5file.create_dataset( - 'lumped_mass_position', data=lumped_mass_position) - -def generate_aero_file(): - global x, y, z - - we = 0 - wn = 0 - # right wing (surface 0, beam 0) - i_surf = 0 - boundary_conditions_aero[wn] = 1 # BC at fuselage junction that Zirkulation = Zirkulation - airfoil_distribution[we:we + n_elem_main, :] = 0 - surface_distribution[we:we + n_elem_main] = i_surf - surface_m[i_surf] = m - aero_node[wn:wn + n_node_main] = True - temp_chord = np.linspace(chord_main, chord_main, n_node_main) - temp_sweep = np.linspace(0.0, 0*np.pi/180, n_node_main) - node_counter = 0 - for i_elem in range(we, we + n_elem_main): - for i_local_node in range(n_node_elem): - if not i_local_node == 0: - node_counter += 1 - chord[i_elem, i_local_node] = temp_chord[node_counter] - elastic_axis[i_elem, i_local_node] = ea_main - sweep[i_elem, i_local_node] = temp_sweep[node_counter] - - we += n_elem_main - wn += n_node_main - - # left wing (surface 1, beam 1) - i_surf = 1 - boundary_conditions_aero[wn] = 1 # BC at fuselage junction - airfoil_distribution[we:we + n_elem_main, :] = 0 - surface_distribution[we:we + n_elem_main] = i_surf - surface_m[i_surf] = m - aero_node[wn:wn + n_node_main] = True - temp_chord = np.linspace(chord_main, chord_main, n_node_main) - node_counter = 0 - for i_elem in range(we, we + n_elem_main): - for i_local_node in range(n_node_elem): - if not i_local_node == 0: - node_counter += 1 - chord[i_elem, i_local_node] = temp_chord[node_counter] - elastic_axis[i_elem, i_local_node] = ea_main - sweep[i_elem, i_local_node] = -temp_sweep[node_counter] - - we += n_elem_main - wn += n_node_main - 1 - - # fuselage - we += n_elem_fuselage - wn += n_node_fuselage - 1 - - with h5.File(route + '/' + case_name + '.aero.h5', 'a') as h5file: - airfoils_group = h5file.create_group('airfoils') - # add one airfoil - naca_airfoil_main = airfoils_group.create_dataset('0', data=np.column_stack( - generate_naca_camber(P=0, M=0))) - - # chord - chord_input = h5file.create_dataset('chord', data=chord) - dim_attr = chord_input .attrs['units'] = 'm' - - # twist - twist_input = h5file.create_dataset('twist', data=twist) - dim_attr = twist_input.attrs['units'] = 'rad' - - # sweep - sweep_input = h5file.create_dataset('sweep', data=sweep) - dim_attr = sweep_input.attrs['units'] = 'rad' - - # airfoil distribution - airfoil_distribution_input = h5file.create_dataset('airfoil_distribution', data=airfoil_distribution) - - surface_distribution_input = h5file.create_dataset('surface_distribution', data=surface_distribution) - surface_m_input = h5file.create_dataset('surface_m', data=surface_m) - m_distribution_input = h5file.create_dataset('m_distribution', data=m_distribution.encode('ascii', 'ignore')) - - aero_node_input = h5file.create_dataset('aero_node', data=aero_node) - elastic_axis_input = h5file.create_dataset('elastic_axis', data=elastic_axis) - - bocos_handle = h5file.create_dataset( - 'boundary_conditions', data=boundary_conditions_aero) - -def generate_nonlifting_body_file(): - we = 0 - wn = 0 - - # right wing - nonlifting_body_node[wn:wn + n_node_main] = False - we += n_elem_main - wn += n_node_main - - # left wing - nonlifting_body_node[wn:wn + n_node_main] = False - we += n_elem_main - wn += n_node_main - - #fuselage (beam?, body ID = 0) - i_body = 0 - nonlifting_body_node[wn:wn + n_node_fuselage] = True - nonlifting_body_distribution[we:we + n_elem_fuselage] = i_body - nonlifting_body_m[i_body] = m_radial_elem_fuselage - radius[wn:wn + n_node_fuselage] = create_fuselage_geometry() - - with h5.File(route + '/' + case_name + '.nonlifting_body.h5', 'a') as h5file: - nonlifting_body_m_input = h5file.create_dataset('nonlifting_body_m', data=nonlifting_body_m) - nonlifting_body_node_input = h5file.create_dataset('nonlifting_body_node', data=nonlifting_body_node) - - nonlifting_body_distribution_input = h5file.create_dataset('nonlifting_body_distribution', data=nonlifting_body_distribution) - - # radius - radius_input = h5file.create_dataset('radius', data=radius) - dim_attr = radius_input.attrs['units'] = 'm' - - # right wing (surface 0, beam 0) -def generate_naca_camber(M=0, P=0): - mm = M*1e-2 - p = P*1e-1 - - def naca(x, mm, p): - if x < 1e-6: - return 0.0 - elif x < p: - return mm/(p*p)*(2*p*x - x*x) - elif x > p and x < 1+1e-6: - return mm/((1-p)*(1-p))*(1 - 2*p + 2*p*x - x*x) - - x_vec = np.linspace(0, 1, 1000) - y_vec = np.array([naca(x, mm, p) for x in x_vec]) - return x_vec, y_vec - -def find_index_of_closest_entry(array_values, target_value): - return (np.abs(array_values - target_value)).argmin() - -def create_ellipsoid(x_geom, a, b, flip): - x_geom -= x_geom.min() - y = b*np.sqrt(1-(x_geom/a)**2) - if flip: - y = np.flip(y.tolist()) - return y - -def add_nose_or_tail_shape(idx, array_x, nose = True): - if nose: - shape = create_ellipsoid(array_x[:idx], array_x[idx] - array_x[0], radius_fuselage, True) - if not nose: - #TO-DO: Add paraboloid shaped tail - shape = create_ellipsoid(array_x[idx:], array_x[-1]-array_x[idx], radius_fuselage, False) - return shape - -def create_fuselage_geometry(): - array_radius = np.zeros((sum(nonlifting_body_node))) - x_fuselage = x[nonlifting_body_node] - fuselage_length = max(x_fuselage)-min(x_fuselage) # useful?? - idx_cylinder_start = find_index_of_closest_entry(x_fuselage, list_cylinder_position_fuselage[0]*fuselage_length) - idx_cylinder_end = find_index_of_closest_entry(x_fuselage,list_cylinder_position_fuselage[1]*fuselage_length) - # set constant radius of cylinder - array_radius[idx_cylinder_start:idx_cylinder_end] = radius_fuselage - # set r(x) for nose and tail region - array_radius[:idx_cylinder_start] = add_nose_or_tail_shape(idx_cylinder_start, x_fuselage, nose = True) - array_radius[idx_cylinder_end:] = add_nose_or_tail_shape(idx_cylinder_end, x_fuselage, nose = False) - # ensure radius = 0 at nose/tail - array_radius[0] = 0 - array_radius[-1] = 0 - return array_radius - - -def generate_dyn_file(): - global dt - global n_tstep - global route - global case_name - global num_elem - global num_node_elem - global num_node - global amplitude - global period - global free_flight - - dynamic_forces_time = None - with_dynamic_forces = False - with_forced_vel = False - if not free_flight: - with_forced_vel = True - - if with_dynamic_forces: - f1 = 100 - dynamic_forces = np.zeros((num_node, 6)) - app_node = [int(num_node_main - 1), int(num_node_main)] - dynamic_forces[app_node, 2] = f1 - force_time = np.zeros((n_tstep, )) - limit = round(0.05/dt) - force_time[50:61] = 1 - - dynamic_forces_time = np.zeros((n_tstep, num_node, 6)) - for it in range(n_tstep): - dynamic_forces_time[it, :, :] = force_time[it]*dynamic_forces - - forced_for_vel = None - if with_forced_vel: - forced_for_vel = np.zeros((n_tstep, 6)) - forced_for_acc = np.zeros((n_tstep, 6)) - for it in range(n_tstep): - # if dt*it < period: - # forced_for_vel[it, 2] = 2*np.pi/period*amplitude*np.sin(2*np.pi*dt*it/period) - # forced_for_acc[it, 2] = (2*np.pi/period)**2*amplitude*np.cos(2*np.pi*dt*it/period) - - forced_for_vel[it, 3] = 2*np.pi/period*amplitude*np.sin(2*np.pi*dt*it/period) - forced_for_acc[it, 3] = (2*np.pi/period)**2*amplitude*np.cos(2*np.pi*dt*it/period) - - if with_dynamic_forces or with_forced_vel: - with h5.File(route + '/' + case_name + '.dyn.h5', 'a') as h5file: - if with_dynamic_forces: - h5file.create_dataset( - 'dynamic_forces', data=dynamic_forces_time) - if with_forced_vel: - h5file.create_dataset( - 'for_vel', data=forced_for_vel) - h5file.create_dataset( - 'for_acc', data=forced_for_acc) - h5file.create_dataset( - 'num_steps', data=n_tstep) - - -def generate_solver_file(): - file_name = route + '/' + case_name + '.sharpy' - settings = dict() - settings['SHARPy'] = {'case': case_name, - 'route': route, - 'flow': flow, - 'write_screen': 'on', - 'write_log': 'on', - 'log_folder': route + '/output/', - 'log_file': case_name + '.log'} - - settings['BeamLoader'] = {'unsteady': 'on', - 'orientation': algebra.euler2quat(np.array([roll, - alpha, - beta]))} - settings['AerogridLoader'] = {'unsteady': 'on', - 'aligned_grid': 'on', - 'mstar': int(20/tstep_factor), - 'freestream_dir': ['1', '0', '0']} - - settings['NonLinearStatic'] = {'print_info': 'off', - 'max_iterations': 150, - 'num_load_steps': 1, - 'delta_curved': 1e-1, - 'min_delta': tolerance, - 'gravity_on': gravity, - 'gravity': 9.81} - - settings['StaticUvlm'] = {'print_info': 'on', - 'horseshoe': 'off', - 'num_cores': num_cores, - 'n_rollup': 0, - 'rollup_dt': dt, - 'rollup_aic_refresh': 1, - 'rollup_tolerance': 1e-4, - 'velocity_field_generator': 'SteadyVelocityField', - 'velocity_field_input': {'u_inf': u_inf, - 'u_inf_direction': [1., 0, 0]}, - 'rho': rho} - - settings['StaticCoupled'] = {'print_info': 'off', - 'structural_solver': 'NonLinearStatic', - 'structural_solver_settings': settings['NonLinearStatic'], - 'aero_solver': 'StaticUvlm', - 'aero_solver_settings': settings['StaticUvlm'], - 'max_iter': 100, - 'n_load_steps': n_step, - 'tolerance': fsi_tolerance, - 'relaxation_factor': structural_relaxation_factor} - - settings['StaticTrim'] = {'solver': 'StaticCoupled', - 'solver_settings': settings['StaticCoupled'], - 'initial_alpha': alpha, - 'initial_deflection': 0, - 'initial_thrust': thrust} - - settings['NonLinearDynamicCoupledStep'] = {'print_info': 'off', - 'max_iterations': 950, - 'delta_curved': 1e-1, - 'min_delta': tolerance, - 'newmark_damp': 5e-3, - 'gravity_on': gravity, - 'gravity': 9.81, - 'num_steps': n_tstep, - 'dt': dt, - 'initial_velocity': u_inf} - - settings['NonLinearDynamicPrescribedStep'] = {'print_info': 'off', - 'max_iterations': 950, - 'delta_curved': 1e-1, - 'min_delta': tolerance, - 'newmark_damp': 5e-3, - 'gravity_on': gravity, - 'gravity': 9.81, - 'num_steps': n_tstep, - 'dt': dt, - 'initial_velocity': u_inf*int(free_flight)} - - relative_motion = 'off' - if not free_flight: - relative_motion = 'on' - settings['StepUvlm'] = {'print_info': 'off', - 'horseshoe': 'off', - 'num_cores': num_cores, - 'n_rollup': 0, - 'convection_scheme': 2, - 'rollup_dt': dt, - 'rollup_aic_refresh': 1, - 'rollup_tolerance': 1e-4, - 'gamma_dot_filtering': 6, - 'velocity_field_generator': 'GustVelocityField', - 'velocity_field_input': {'u_inf': int(not free_flight)*u_inf, - 'u_inf_direction': [1., 0, 0], - 'gust_shape': '1-cos', - 'gust_length': gust_length, - 'gust_intensity': gust_intensity*u_inf, - 'offset': gust_offset, - 'span': span_main, - 'relative_motion': relative_motion}, - 'rho': rho, - 'n_time_steps': n_tstep, - 'dt': dt} - - if free_flight: - solver = 'NonLinearDynamicCoupledStep' - else: - solver = 'NonLinearDynamicPrescribedStep' - settings['DynamicCoupled'] = {'structural_solver': solver, - 'structural_solver_settings': settings[solver], - 'aero_solver': 'StepUvlm', - 'aero_solver_settings': settings['StepUvlm'], - 'fsi_substeps': 200, - 'fsi_tolerance': fsi_tolerance, - 'relaxation_factor': relaxation_factor, - 'minimum_steps': 1, - 'relaxation_steps': 150, - 'final_relaxation_factor': 0.5, - 'n_time_steps': n_tstep, - 'dt': dt, - 'include_unsteady_force_contribution': 'on', - 'postprocessors': ['BeamLoads', 'BeamPlot', 'AerogridPlot'], - 'postprocessors_settings': {'BeamLoads': {'folder': route + '/output/', - 'csv_output': 'off'}, - 'BeamPlot': {'folder': route + '/output/', - 'include_rbm': 'on', - 'include_applied_forces': 'on'}, - 'AerogridPlot': { - 'folder': route + '/output/', - 'include_rbm': 'on', - 'include_applied_forces': 'on', - 'minus_m_star': 0}, - }} - - settings['BeamLoads'] = {'folder': route + '/output/', - 'csv_output': 'off'} - - settings['BeamPlot'] = {'folder': route + '/output/', - 'include_rbm': 'on', - 'include_applied_forces': 'on', - 'include_forward_motion': 'on'} - - settings['AerogridPlot'] = {'folder': route + '/output/', - 'include_rbm': 'on', - 'include_forward_motion': 'off', - 'include_applied_forces': 'on', - 'minus_m_star': 0, - 'u_inf': u_inf, - 'dt': dt} - - settings['Modal'] = {'print_info': True, - 'use_undamped_modes': True, - 'NumLambda': 30, - 'rigid_body_modes': True, - 'write_modes_vtk': 'on', - 'print_matrices': 'on', - 'write_data': 'on', - 'continuous_eigenvalues': 'off', - 'dt': dt, - 'plot_eigenvalues': False} - - settings['LinearAssembler'] = {'linear_system': 'LinearAeroelastic', - 'linear_system_settings': { - 'beam_settings': {'modal_projection': False, - 'inout_coords': 'nodes', - 'discrete_time': True, - 'newmark_damp': 0.05, - 'discr_method': 'newmark', - 'dt': dt, - 'proj_modes': 'undamped', - 'use_euler': 'off', - 'num_modes': 40, - 'print_info': 'on', - 'gravity': 'on', - 'remove_dofs': []}, - 'aero_settings': {'dt': dt, - 'integr_order': 2, - 'density': rho, - 'remove_predictor': False, - 'use_sparse': True, - 'rigid_body_motion': free_flight, - 'use_euler': False, - 'remove_inputs': ['u_gust']}, - 'rigid_body_motion': free_flight}} - - settings['AsymptoticStability'] = {'sys_id': 'LinearAeroelastic', - 'print_info': 'on', - 'modes_to_plot': [], - 'display_root_locus': 'off', - 'frequency_cutoff': 0, - 'export_eigenvalues': 'off', - 'num_evals': 40, - 'folder': route + '/output/'} - - - import configobj - config = configobj.ConfigObj() - config.filename = file_name - for k, v in settings.items(): - config[k] = v - config.write() - - - -clean_test_files() -generate_fem() -generate_aero_file() -generate_nonlifting_body_file() -generate_solver_file() -generate_dyn_file() - - From a5e07de9c63a78b1811f3b632ca619041c9c4b86 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stefanie=20D=C3=BCssler?= Date: Wed, 19 Jul 2023 11:22:22 +0800 Subject: [PATCH 122/232] fix [liftdistribution] file ending from postproc output file --- sharpy/postproc/liftdistribution.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sharpy/postproc/liftdistribution.py b/sharpy/postproc/liftdistribution.py index 596f64481..1aef82184 100644 --- a/sharpy/postproc/liftdistribution.py +++ b/sharpy/postproc/liftdistribution.py @@ -24,7 +24,7 @@ class LiftDistribution(BaseSolver): settings_description = dict() settings_types['text_file_name'] = 'str' - settings_default['text_file_name'] = 'lift_distribution' + settings_default['text_file_name'] = 'lift_distribution.txt' settings_description['text_file_name'] = 'Text file name' settings_default['coefficients'] = True From ca50e6fb18b62450ce1f60e3d6e957484dfb02e6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stefanie=20D=C3=BCssler?= Date: Wed, 19 Jul 2023 11:26:11 +0800 Subject: [PATCH 123/232] add [tests] unittest for phantom panel check --- .../nonlifting/define_simulation_settings.py | 1 + .../static/nonlifting/test_phantom_panels.py | 121 ++++++++++++++++++ 2 files changed, 122 insertions(+) create mode 100644 tests/uvlm/static/nonlifting/test_phantom_panels.py diff --git a/tests/uvlm/static/nonlifting/define_simulation_settings.py b/tests/uvlm/static/nonlifting/define_simulation_settings.py index 335eac25a..ee0c90911 100644 --- a/tests/uvlm/static/nonlifting/define_simulation_settings.py +++ b/tests/uvlm/static/nonlifting/define_simulation_settings.py @@ -63,6 +63,7 @@ def define_simulation_settings(flow, model, alpha_deg, u_inf, rho = 1.225, 'rho': rho, 'nonlifting_body_interactions': nonlifting_body_interactions, 'only_nonlifting': nonlifting_only, + 'phantom_wing_test': phantom_test } settings['StaticCoupled'] = {'print_info': 'off', diff --git a/tests/uvlm/static/nonlifting/test_phantom_panels.py b/tests/uvlm/static/nonlifting/test_phantom_panels.py new file mode 100644 index 000000000..ed308d0f1 --- /dev/null +++ b/tests/uvlm/static/nonlifting/test_phantom_panels.py @@ -0,0 +1,121 @@ +import unittest +import os +import numpy as np +from fuselage_wing_configuration.fuselage_wing_configuration import Fuselage_Wing_Configuration +from define_simulation_settings import define_simulation_settings + + + +class TestPhantomPanels(unittest.TestCase): + + route_test_dir = os.path.abspath(os.path.dirname(os.path.realpath(__file__))) + + + def test_phantom(self): + """ + run with and without phantom panels (should give same results). + """ + + # define model variables + half_wingspan = 10 + fuselage_junction = 0.2 + fuselage_length= 10 + offset_nose_wing = fuselage_length/2 - 0.25 + lifting_only = False + nonlifting_only = False + phantom_test = True + horseshoe = True + chord = 1. + u_inf = 10 + alpha_deg = 5 + n_elem_per_wing = 20 + n_elem_fuselage = 10 + ea_main = 0.5 + num_chordwise_panels = 8 + fuselage_shape = 'cylindrical' + + # define case name and folders + case_route = self.route_test_dir + '/cases/' + output_route = self.route_test_dir + '/output/' + if not os.path.exists(case_route): + os.makedirs(case_route) + + list_case_name = ['wing_only', 'phantom_wing'] + list_phantom_test = [False, True] + list_lifting_only = [True, False] + for icase in range(len(list_case_name)): + case_name = list_case_name[icase] + phantom_test = list_phantom_test[icase] + lifting_only = list_lifting_only[icase] + # generate ellipsoid model + phantom_wing = Fuselage_Wing_Configuration(case_name, case_route, output_route) + phantom_wing.init_aeroelastic(lifting_only=lifting_only, + elastic_axis=ea_main, + num_chordwise_panels=num_chordwise_panels, + max_radius=fuselage_junction, + half_wingspan=half_wingspan, + fuselage_length=fuselage_length, + offset_nose_wing=offset_nose_wing, + n_elem_per_wing=n_elem_per_wing, + n_elem_fuselage=n_elem_fuselage, + chord=chord, + fuselage_shape=fuselage_shape) + phantom_wing.generate() + # define settings + flow = ['BeamLoader', + 'AerogridLoader', + 'NonliftingbodygridLoader', + 'AerogridPlot', + 'StaticUvlm', + 'BeamLoads', + 'AerogridPlot', + 'BeamPlot', + 'LiftDistribution', + ] + if lifting_only: + flow.remove('NonliftingbodygridLoader') + settings = define_simulation_settings(flow, + phantom_wing, + alpha_deg, + u_inf, + lifting_only=lifting_only, + phantom_test=phantom_test, + nonlifting_only=nonlifting_only, + horseshoe=horseshoe) + phantom_wing.create_settings(settings) + + # run simulation + phantom_wing.run() + + # # postprocess + lift_distribution_wing_only = self.load_lift_distribution(output_route + '/' + list_case_name[0])[:phantom_wing.structure.n_node_right_wing,:] + lift_distribution_wing_phantom = self.load_lift_distribution(output_route + '/' + list_case_name[1])[:phantom_wing.structure.n_node_right_wing,:] + + # # check results + with self.subTest('lift distribution'): + # + np.testing.assert_array_almost_equal(lift_distribution_wing_only[3:], lift_distribution_wing_phantom[3:], decimal=3) + def load_lift_distribution(self, output_folder): + """ + Loads the resulting pressure coefficients saved in txt-files. + """ + lift_distribution = np.loadtxt(output_folder + '/lift_distribution.txt', delimiter=',') + y_coordinate = lift_distribution[:,1] + cl_distribution = lift_distribution[:,-1] + + return np.column_stack((y_coordinate, cl_distribution)) + + def tearDown(self): + """ + Removes all created files within this test. + """ + import shutil + folders = ['cases', 'output'] + for folder in folders: + shutil.rmtree(self.route_test_dir + '/' + folder) + + +if __name__ == '__main__': + import unittest + + unittest.main() From cfda1535f0a64133bce71f2a1d2f0095f544a918 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stefanie=20D=C3=BCssler?= Date: Wed, 19 Jul 2023 11:27:27 +0800 Subject: [PATCH 124/232] remove [cases] unnecessary savetxt when creating structure --- .../nonlifting/fuselage_wing_configuration/fwc_structure.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/tests/uvlm/static/nonlifting/fuselage_wing_configuration/fwc_structure.py b/tests/uvlm/static/nonlifting/fuselage_wing_configuration/fwc_structure.py index f4f48d62c..84838edd0 100644 --- a/tests/uvlm/static/nonlifting/fuselage_wing_configuration/fwc_structure.py +++ b/tests/uvlm/static/nonlifting/fuselage_wing_configuration/fwc_structure.py @@ -53,8 +53,6 @@ def generate(self): self.app_forces[0] = [0, self.thrust, 0, 0, 0, 0] self.set_beam_properties_fuselage() - - np.savetxt('./coordinates.csv', np.column_stack((self.x, self.y, self.z))) self.write_input_file() def set_element_and_nodes(self): @@ -202,8 +200,6 @@ def set_beam_properties_fuselage(self): self.boundary_conditions[self.n_node_wing_total] = -1 self.boundary_conditions[-1] = -1 - np.savetxt("./conn.csv", self.conn) - def find_index_of_closest_entry(self, array_values, target_value): return np.argmin(np.abs(array_values - target_value)) From 022b36779faad2e81d0365dc1efc815372a74666 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stefanie=20D=C3=BCssler?= Date: Sat, 22 Jul 2023 15:48:41 +0800 Subject: [PATCH 125/232] add [settings] dt as an input - better since parameters to calculate dt depend much on the attribute naming - and update correct settings for lift distribution settings --- .../static/nonlifting/define_simulation_settings.py | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/tests/uvlm/static/nonlifting/define_simulation_settings.py b/tests/uvlm/static/nonlifting/define_simulation_settings.py index ee0c90911..93b3d4207 100644 --- a/tests/uvlm/static/nonlifting/define_simulation_settings.py +++ b/tests/uvlm/static/nonlifting/define_simulation_settings.py @@ -1,7 +1,9 @@ import numpy as np import sharpy.utils.algebra as algebra -def define_simulation_settings(flow, model, alpha_deg, u_inf, rho = 1.225, +def define_simulation_settings(flow, model, alpha_deg, u_inf, + dt=1, + rho = 1.225, lifting_only=True, nonlifting_only=False, phantom_test=False, @@ -11,11 +13,9 @@ def define_simulation_settings(flow, model, alpha_deg, u_inf, rho = 1.225, wake_length = kwargs.get('wake_length', 10) # Other parameters if horseshoe: - dt = 1 mstar = 1 else: - dt = model.aero.chord_main / model.aero.m / u_inf - mstar = wake_length*model.aero.m + mstar = wake_length*model.aero.num_chordwise_panels # numerics n_step = kwargs.get('n_step', 5) structural_relaxation_factor = kwargs.get('structural_relaxation_factor', 0.6) @@ -43,8 +43,7 @@ def define_simulation_settings(flow, model, alpha_deg, u_inf, rho = 1.225, 0.]))} settings['BeamLoads'] = {} - settings['LiftDistribution'] = {'rho': rho, - 'coefficients': True} + settings['LiftDistribution'] = {'coefficients': True} settings['NonLinearStatic'] = {'print_info': 'off', 'max_iterations': 150, From b66da7e89fac8a4851b51589300f19a4c12b2727 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stefanie=20D=C3=BCssler?= Date: Sat, 22 Jul 2023 15:50:50 +0800 Subject: [PATCH 126/232] rename [unittest] test case and file name --- .../{test_ellipsoid.py => test_source_panel_method.py} | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) rename tests/uvlm/static/nonlifting/{test_ellipsoid.py => test_source_panel_method.py} (99%) diff --git a/tests/uvlm/static/nonlifting/test_ellipsoid.py b/tests/uvlm/static/nonlifting/test_source_panel_method.py similarity index 99% rename from tests/uvlm/static/nonlifting/test_ellipsoid.py rename to tests/uvlm/static/nonlifting/test_source_panel_method.py index 739cab95f..51257df55 100644 --- a/tests/uvlm/static/nonlifting/test_ellipsoid.py +++ b/tests/uvlm/static/nonlifting/test_source_panel_method.py @@ -5,11 +5,10 @@ from define_simulation_settings import define_simulation_settings +class TestSourcePanelMethod(unittest.TestCase): -class TestFuselage(unittest.TestCase): route_test_dir = os.path.abspath(os.path.dirname(os.path.realpath(__file__))) - def test_ellipsoid(self): """ Computes the pressure distribution over an ellipsoid. The results should From 51647d5cb1edc915d8924f6fd78c2b25994bafe8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stefanie=20D=C3=BCssler?= Date: Sat, 22 Jul 2023 20:56:25 +0800 Subject: [PATCH 127/232] refactor [unittest] phantom panel check to reuse function for next case --- .../static/nonlifting/test_phantom_panels.py | 191 +++++++++++------- 1 file changed, 114 insertions(+), 77 deletions(-) diff --git a/tests/uvlm/static/nonlifting/test_phantom_panels.py b/tests/uvlm/static/nonlifting/test_phantom_panels.py index ed308d0f1..fc1783935 100644 --- a/tests/uvlm/static/nonlifting/test_phantom_panels.py +++ b/tests/uvlm/static/nonlifting/test_phantom_panels.py @@ -3,107 +3,144 @@ import numpy as np from fuselage_wing_configuration.fuselage_wing_configuration import Fuselage_Wing_Configuration from define_simulation_settings import define_simulation_settings +import json class TestPhantomPanels(unittest.TestCase): - - route_test_dir = os.path.abspath(os.path.dirname(os.path.realpath(__file__))) - - def test_phantom(self): """ run with and without phantom panels (should give same results). """ + self.define_folder() + + model = 'phantom_wing' + fuselage_length = 10 + dict_geometry_parameters = self.get_geometry_parameters(model, + fuselage_length=fuselage_length) - # define model variables - half_wingspan = 10 - fuselage_junction = 0.2 - fuselage_length= 10 - offset_nose_wing = fuselage_length/2 - 0.25 - lifting_only = False - nonlifting_only = False - phantom_test = True - horseshoe = True - chord = 1. - u_inf = 10 + # Freestream Conditions alpha_deg = 5 - n_elem_per_wing = 20 - n_elem_fuselage = 10 - ea_main = 0.5 - num_chordwise_panels = 8 - fuselage_shape = 'cylindrical' - - # define case name and folders - case_route = self.route_test_dir + '/cases/' - output_route = self.route_test_dir + '/output/' - if not os.path.exists(case_route): - os.makedirs(case_route) - - list_case_name = ['wing_only', 'phantom_wing'] + u_inf = 10 + + # Discretization + dict_discretization = { + 'n_elem_per_wing': 20, + 'n_elem_fuselage': 10, + 'num_chordwise_panels': 8 + } + + # Simulation settings + horseshoe = True list_phantom_test = [False, True] - list_lifting_only = [True, False] - for icase in range(len(list_case_name)): - case_name = list_case_name[icase] - phantom_test = list_phantom_test[icase] - lifting_only = list_lifting_only[icase] - # generate ellipsoid model - phantom_wing = Fuselage_Wing_Configuration(case_name, case_route, output_route) - phantom_wing.init_aeroelastic(lifting_only=lifting_only, - elastic_axis=ea_main, - num_chordwise_panels=num_chordwise_panels, - max_radius=fuselage_junction, - half_wingspan=half_wingspan, - fuselage_length=fuselage_length, - offset_nose_wing=offset_nose_wing, - n_elem_per_wing=n_elem_per_wing, - n_elem_fuselage=n_elem_fuselage, - chord=chord, - fuselage_shape=fuselage_shape) - phantom_wing.generate() - # define settings - flow = ['BeamLoader', - 'AerogridLoader', - 'NonliftingbodygridLoader', - 'AerogridPlot', - 'StaticUvlm', - 'BeamLoads', - 'AerogridPlot', - 'BeamPlot', - 'LiftDistribution', + # Simlation Solver Flow + flow = ['BeamLoader', + 'AerogridLoader', + 'NonliftingbodygridLoader', + 'StaticUvlm', + 'BeamLoads', + 'LiftDistribution', ] + list_results_lift_distribution = [] + # define model variables + for icase in range(len(list_phantom_test)): + phantom_test = list_phantom_test[icase] + lifting_only = not phantom_test + case_name = model + '_coupled_{}'.format(int(phantom_test)) + + # generate ellipsoid model + phantom_wing = self.generate_model(case_name, + dict_geometry_parameters, + dict_discretization, + lifting_only) + # Adjust flow for case + flow_case = flow.copy() + if lifting_only: - flow.remove('NonliftingbodygridLoader') - settings = define_simulation_settings(flow, - phantom_wing, + flow_case.remove('NonliftingbodygridLoader') + print(flow_case) + self.generate_simulation_settings(flow_case, + phantom_wing, + alpha_deg, + u_inf, + lifting_only, + horseshoe=horseshoe, + phantom_test=phantom_test) + # run simulation + phantom_wing.run() + + # get results + list_results_lift_distribution.append(self.load_lift_distribution( + self.output_route + '/' + case_name, + phantom_wing.structure.n_node_right_wing + )) + + # check results + with self.subTest('lift distribution'): + np.testing.assert_array_almost_equal(list_results_lift_distribution[0][3:, 1], list_results_lift_distribution[1][3:, 1], decimal=3) + + + def get_geometry_parameters(self, model_name,fuselage_length=10): + + with open(self.route_test_dir + '/geometry_parameter_models.json', 'r') as fp: + parameter_models = json.load(fp)[model_name] + + geometry_parameters = { + 'fuselage_length': fuselage_length, + 'max_radius': fuselage_length/parameter_models['length_radius_ratio'], + 'fuselage_shape': parameter_models['fuselage_shape'], + } + geometry_parameters['chord']=geometry_parameters['max_radius']/parameter_models['radius_chord_ratio'] + geometry_parameters['half_wingspan'] = geometry_parameters['max_radius']/parameter_models['radius_half_wingspan_ratio'] + geometry_parameters['offset_nose_wing'] = parameter_models['length_offset_nose_to_wing_ratio'] / fuselage_length + + return geometry_parameters + + def generate_model(self, + case_name, + dict_geometry_parameters, + dict_discretisation, + lifting_only): + aircraft_model = Fuselage_Wing_Configuration(case_name, self.case_route, self.output_route) + aircraft_model.init_aeroelastic(lifting_only=lifting_only, + **dict_discretisation, + **dict_geometry_parameters) + aircraft_model.generate() + return aircraft_model + + def generate_simulation_settings(self, + flow, + aircraft_model, + alpha_deg, + u_inf, + lifting_only, + horseshoe=True, + nonlifting_only=False, + phantom_test=False): + settings = define_simulation_settings(flow, + aircraft_model, alpha_deg, u_inf, lifting_only=lifting_only, phantom_test=phantom_test, nonlifting_only=nonlifting_only, horseshoe=horseshoe) - phantom_wing.create_settings(settings) + aircraft_model.create_settings(settings) - # run simulation - phantom_wing.run() - - # # postprocess - lift_distribution_wing_only = self.load_lift_distribution(output_route + '/' + list_case_name[0])[:phantom_wing.structure.n_node_right_wing,:] - lift_distribution_wing_phantom = self.load_lift_distribution(output_route + '/' + list_case_name[1])[:phantom_wing.structure.n_node_right_wing,:] - - # # check results - with self.subTest('lift distribution'): - # - np.testing.assert_array_almost_equal(lift_distribution_wing_only[3:], lift_distribution_wing_phantom[3:], decimal=3) - def load_lift_distribution(self, output_folder): + def define_folder(self): + self.route_test_dir = os.path.abspath(os.path.dirname(os.path.realpath(__file__))) + self.case_route = self.route_test_dir + '/cases/' + self.output_route = self.route_test_dir + '/output/' + + if not os.path.exists(self.case_route): + os.makedirs(self.case_route) + + def load_lift_distribution(self, output_folder, n_node_wing): """ Loads the resulting pressure coefficients saved in txt-files. """ lift_distribution = np.loadtxt(output_folder + '/lift_distribution.txt', delimiter=',') - y_coordinate = lift_distribution[:,1] - cl_distribution = lift_distribution[:,-1] - - return np.column_stack((y_coordinate, cl_distribution)) + return lift_distribution[:n_node_wing,[1,-1]] def tearDown(self): """ From 281d24f01b84ac9acc61ae36ea2929eee9e23b11 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stefanie=20D=C3=BCssler?= Date: Sat, 22 Jul 2023 20:58:00 +0800 Subject: [PATCH 128/232] add [unittest] json file to load parameters from --- tests/uvlm/static/nonlifting/geometry_parameter_models.json | 1 + 1 file changed, 1 insertion(+) create mode 100644 tests/uvlm/static/nonlifting/geometry_parameter_models.json diff --git a/tests/uvlm/static/nonlifting/geometry_parameter_models.json b/tests/uvlm/static/nonlifting/geometry_parameter_models.json new file mode 100644 index 000000000..14a429e06 --- /dev/null +++ b/tests/uvlm/static/nonlifting/geometry_parameter_models.json @@ -0,0 +1 @@ +{"phantom_wing": {"length_radius_ratio": 50.0, "radius_chord_ratio": 0.2, "vertical_wing_position": 0, "radius_half_wingspan_ratio": 0.02, "length_offset_nose_to_wing_ratio": 5.0, "fuselage_shape": "cylindrical"}} \ No newline at end of file From a595e77cebb2309b3c65ea717bf5489043278e5d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stefanie=20D=C3=BCssler?= Date: Sat, 22 Jul 2023 20:58:09 +0800 Subject: [PATCH 129/232] rename [unittest] function for phantom test to make more general --- .../{test_phantom_panels.py => test_vlm_coupled_spm.py} | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) rename tests/uvlm/static/nonlifting/{test_phantom_panels.py => test_vlm_coupled_spm.py} (98%) diff --git a/tests/uvlm/static/nonlifting/test_phantom_panels.py b/tests/uvlm/static/nonlifting/test_vlm_coupled_spm.py similarity index 98% rename from tests/uvlm/static/nonlifting/test_phantom_panels.py rename to tests/uvlm/static/nonlifting/test_vlm_coupled_spm.py index fc1783935..83aaa9ea1 100644 --- a/tests/uvlm/static/nonlifting/test_phantom_panels.py +++ b/tests/uvlm/static/nonlifting/test_vlm_coupled_spm.py @@ -5,10 +5,9 @@ from define_simulation_settings import define_simulation_settings import json +class TestVlmCoupledWithSourcePanelMethod(unittest.TestCase): - -class TestPhantomPanels(unittest.TestCase): - def test_phantom(self): + def test_phantom_panels(self): """ run with and without phantom panels (should give same results). """ From ca724ed831144d63532010f0ae84098ac0430733 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stefanie=20D=C3=BCssler?= Date: Sat, 22 Jul 2023 21:42:42 +0800 Subject: [PATCH 130/232] add [unittest] documentation to phantam panel test --- .../static/nonlifting/test_vlm_coupled_spm.py | 28 ++++++++++++++++--- 1 file changed, 24 insertions(+), 4 deletions(-) diff --git a/tests/uvlm/static/nonlifting/test_vlm_coupled_spm.py b/tests/uvlm/static/nonlifting/test_vlm_coupled_spm.py index 83aaa9ea1..0d89d0517 100644 --- a/tests/uvlm/static/nonlifting/test_vlm_coupled_spm.py +++ b/tests/uvlm/static/nonlifting/test_vlm_coupled_spm.py @@ -7,9 +7,15 @@ class TestVlmCoupledWithSourcePanelMethod(unittest.TestCase): + def test_phantom_panels(self): """ - run with and without phantom panels (should give same results). + The lift distribution over a rectangular high-aspect ratio + wing is computed. First a wing_only configuration is considered, + while second, we activate the phantom panels created within the + fuselage although, the effect of the source panels is omitted. + With the defined interpolation scheme, the same lift distribution + must be obtained. """ self.define_folder() @@ -80,10 +86,14 @@ def test_phantom_panels(self): def get_geometry_parameters(self, model_name,fuselage_length=10): - + """ + Geometry parameters are loaded from json init file for the specified model. + Next, final geoemtry parameteres, depending on the fuselage length are + calculated and return within a dict. + """ with open(self.route_test_dir + '/geometry_parameter_models.json', 'r') as fp: parameter_models = json.load(fp)[model_name] - + geometry_parameters = { 'fuselage_length': fuselage_length, 'max_radius': fuselage_length/parameter_models['length_radius_ratio'], @@ -100,6 +110,10 @@ def generate_model(self, dict_geometry_parameters, dict_discretisation, lifting_only): + """ + Aircraft model object is generated and structural and aerodynamic (lifting and nonlifting) + input files are generated. + """ aircraft_model = Fuselage_Wing_Configuration(case_name, self.case_route, self.output_route) aircraft_model.init_aeroelastic(lifting_only=lifting_only, **dict_discretisation, @@ -116,6 +130,9 @@ def generate_simulation_settings(self, horseshoe=True, nonlifting_only=False, phantom_test=False): + """ + Simulation settings are defined and written to the ".sharpy" input file. + """ settings = define_simulation_settings(flow, aircraft_model, alpha_deg, @@ -126,7 +143,10 @@ def generate_simulation_settings(self, horseshoe=horseshoe) aircraft_model.create_settings(settings) - def define_folder(self): + def define_folder(self): + """ + Initializes all folder path needed and creates case folder. + """ self.route_test_dir = os.path.abspath(os.path.dirname(os.path.realpath(__file__))) self.case_route = self.route_test_dir + '/cases/' self.output_route = self.route_test_dir + '/output/' From 5c72003ffa44e9bd10d19a0542375c558e8bb9de Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stefanie=20D=C3=BCssler?= Date: Sat, 22 Jul 2023 21:43:13 +0800 Subject: [PATCH 131/232] fix [unittest] geometry parameter calculation --- tests/uvlm/static/nonlifting/test_vlm_coupled_spm.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/uvlm/static/nonlifting/test_vlm_coupled_spm.py b/tests/uvlm/static/nonlifting/test_vlm_coupled_spm.py index 0d89d0517..6a8090a33 100644 --- a/tests/uvlm/static/nonlifting/test_vlm_coupled_spm.py +++ b/tests/uvlm/static/nonlifting/test_vlm_coupled_spm.py @@ -101,7 +101,7 @@ def get_geometry_parameters(self, model_name,fuselage_length=10): } geometry_parameters['chord']=geometry_parameters['max_radius']/parameter_models['radius_chord_ratio'] geometry_parameters['half_wingspan'] = geometry_parameters['max_radius']/parameter_models['radius_half_wingspan_ratio'] - geometry_parameters['offset_nose_wing'] = parameter_models['length_offset_nose_to_wing_ratio'] / fuselage_length + geometry_parameters['offset_nose_wing'] = parameter_models['length_offset_nose_to_wing_ratio'] * fuselage_length return geometry_parameters From e0607d097ef0e528c09b53b5a125d54e591820ce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stefanie=20D=C3=BCssler?= Date: Sat, 22 Jul 2023 21:47:29 +0800 Subject: [PATCH 132/232] add [unittest] test for fuselage wing configuration (Static uvlm) - extend fuselage wing object class for vertical displaced wings -add test function to test for cases where vlm and source panel method have to be coupled --- .../fuselage_wing_configuration/fwc_aero.py | 9 ++- .../fwc_fuselage.py | 15 +++-- .../fwc_structure.py | 53 ++++++++++----- .../nonlifting/geometry_parameter_models.json | 2 +- .../static/nonlifting/test_vlm_coupled_spm.py | 66 ++++++++++++++++++- 5 files changed, 120 insertions(+), 25 deletions(-) diff --git a/tests/uvlm/static/nonlifting/fuselage_wing_configuration/fwc_aero.py b/tests/uvlm/static/nonlifting/fuselage_wing_configuration/fwc_aero.py index 55e5c2356..752837738 100644 --- a/tests/uvlm/static/nonlifting/fuselage_wing_configuration/fwc_aero.py +++ b/tests/uvlm/static/nonlifting/fuselage_wing_configuration/fwc_aero.py @@ -51,7 +51,12 @@ def initialize_attributes(self): self.elastic_axis = np.zeros_like(self.twist) self.junction_boundary_condition_aero = np.zeros((1, self.n_surfaces), dtype=int) - 1 - + + def get_y_junction(self): + if self.structure.vertical_wing_position == 0: + return self.radius_fuselage + else: + return np.sqrt(self.radius_fuselage**2-self.structure.vertical_wing_position**2) def set_wing_properties(self): """ Sets necessary parameters to define the lifting surfaces of one wing (right). @@ -59,7 +64,7 @@ def set_wing_properties(self): self.aero_node[:self.structure.n_node_wing_total] = True if not self.lifting_only: - self.aero_node[:self.structure.n_node_right_wing] = abs(self.structure.y[:self.structure.n_node_right_wing]) > self.radius_fuselage + self.aero_node[:self.structure.n_node_right_wing] = abs(self.structure.y[:self.structure.n_node_right_wing]) > self.get_y_junction() self.aero_node[self.structure.n_node_right_wing:self.structure.n_node_wing_total] = self.aero_node[1:self.structure.n_node_right_wing] self.chord[:2*self.structure.n_elem_per_wing, :] = self.chord_wing diff --git a/tests/uvlm/static/nonlifting/fuselage_wing_configuration/fwc_fuselage.py b/tests/uvlm/static/nonlifting/fuselage_wing_configuration/fwc_fuselage.py index 2a37f52a9..fd0d30a08 100644 --- a/tests/uvlm/static/nonlifting/fuselage_wing_configuration/fwc_fuselage.py +++ b/tests/uvlm/static/nonlifting/fuselage_wing_configuration/fwc_fuselage.py @@ -33,9 +33,10 @@ def generate(self): self.initialize_parameters() - self.nonlifting_body_node[0] = True - self.nonlifting_body_node[self.structure.n_node_wing_total:] = True - self.nonlifting_body_distribution[self.structure.n_elem_per_wing*2:] = 0 + self.nonlifting_body_node[self.structure.n_node_wing_total:self.structure.n_node_fuselage_tail] = True + if self.structure.vertical_wing_position == 0: + self.nonlifting_body_node[0] = True + self.nonlifting_body_distribution[self.structure.n_elem_per_wing*2:self.structure.n_elem_per_wing*2+self.structure.n_elem_fuselage] = 0 self.nonlifting_body_m[0] = self.num_radial_panels self.get_fuselage_geometry() @@ -61,9 +62,13 @@ def get_fuselage_geometry(self): radius_fuselage = self.get_radius_ellipsoid(x_coord_fuselage_sorted.copy(), self.structure.fuselage_length/2, self.max_radius) else: raise "ERROR Fuselage shape {} unknown.".format(self.fuselage_shape) - self.radius[0] = radius_fuselage[self.structure.idx_junction] - self.radius[self.structure.n_node_wing_total:] = np.delete(radius_fuselage, self.structure.idx_junction) + if self.structure.vertical_wing_position == 0: + self.radius[0] = radius_fuselage[self.structure.idx_junction] + self.radius[self.structure.n_node_wing_total:] =np.delete(radius_fuselage, self.structure.idx_junction) + else: + self.radius[self.structure.n_node_wing_total:self.structure.n_node_fuselage_tail] = radius_fuselage if self.flag_plot_radius: + self.plot_fuselage_radius(self.get_values_at_fuselage_nodes(self.structure.x), self.get_values_at_fuselage_nodes(self.radius)) self.plot_fuselage_radius(x_coord_fuselage_sorted, radius_fuselage) def get_values_at_fuselage_nodes(self, array): diff --git a/tests/uvlm/static/nonlifting/fuselage_wing_configuration/fwc_structure.py b/tests/uvlm/static/nonlifting/fuselage_wing_configuration/fwc_structure.py index 84838edd0..e23c3d2e0 100644 --- a/tests/uvlm/static/nonlifting/fuselage_wing_configuration/fwc_structure.py +++ b/tests/uvlm/static/nonlifting/fuselage_wing_configuration/fwc_structure.py @@ -22,7 +22,7 @@ def __init__(self, case_name, case_route, **kwargs): self.half_wingspan = kwargs.get('half_wingspan', 2) self.fuselage_length = kwargs.get('fuselage_length', 10) self.offset_nose_wing_beam = kwargs.get('offset_nose_wing', self.fuselage_length/2) - + self.vertical_wing_position = kwargs.get('vertical_wing_position', 0.) self.n_elem_per_wing = kwargs.get('n_elem_per_wing', 10) self.n_elem_fuselage = kwargs.get('n_elem_fuselage', 10) @@ -67,9 +67,14 @@ def set_element_and_nodes(self): self.n_node_left_wing = self.n_node_right_wing - 1 self.n_node_wing_total = self.n_node_right_wing + self.n_node_left_wing + self.n_node_fuselage_tail = self.n_node_wing_total + self.n_node_fuselage self.n_node = self.n_node_fuselage + self.n_node_wing_total self.n_elem =self.n_elem_fuselage + 2 * self.n_elem_per_wing + if not self.vertical_wing_position == 0: + self.n_elem += 1 + self.n_node += 1 + def initialize_parameters(self): self.x = np.zeros((self.n_node, )) self.y = np.zeros((self.n_node, )) @@ -126,7 +131,7 @@ def set_beam_properties_right_wing(self): and twist. """ self.y[:self.n_node_right_wing] = np.linspace(0, self.half_wingspan, self.n_node_right_wing) - + self.z[:self.n_node_right_wing] += self.vertical_wing_position for ielem in range(self.n_elem_per_wing): self.conn[ielem, :] = ((np.ones((3, )) * ielem * (self.n_node_elem - 1)) + [0, 2, 1]) @@ -155,27 +160,30 @@ def mirror_wing_beam(self): self.conn[self.n_elem_per_wing, 0] = 0 def set_x_coordinate_fuselage(self): + if self.vertical_wing_position == 0: + n_nodes_fuselage = self.n_node_fuselage + 1 + else: + n_nodes_fuselage = self.n_node_fuselage if self.fuselage_discretisation == 'uniform': - x_coord_fuselage = np.linspace(0, self.fuselage_length, self.n_node_fuselage + 1) - self.offset_nose_wing_beam - # print(self.x) + x_coord_fuselage = np.linspace(0, self.fuselage_length, n_nodes_fuselage) - self.offset_nose_wing_beam elif self.fuselage_discretisation == '1-cosine': - x_coord_fuselage = np.linspace(0, 1, self.n_node_fuselage + 1) + x_coord_fuselage = np.linspace(0, 1, n_nodes_fuselage) x_coord_fuselage = 0.5*(1.0 - np.cos(x_coord_fuselage*np.pi)) x_coord_fuselage *= self.fuselage_length x_coord_fuselage -= self.offset_nose_wing_beam else: raise "ERROR Specified fuselage discretisation '{}' unknown".format(self.fuselage_discretisation) self.idx_junction = self.find_index_of_closest_entry(x_coord_fuselage, self.x[0]) - if self.enforce_uniform_fuselage_discretisation: - self.x[:self.n_node_wing_total] += x_coord_fuselage[self.idx_junction] - - x_coord_fuselage = np.delete(x_coord_fuselage, self.idx_junction) - self.x[self.n_node_wing_total:] = x_coord_fuselage + + self.idx_junction_global = self.idx_junction + self.n_node_wing_total + if self.vertical_wing_position == 0: + if self.enforce_uniform_fuselage_discretisation: + self.x[:self.n_node_wing_total] += x_coord_fuselage[self.idx_junction] + x_coord_fuselage = np.delete(x_coord_fuselage, self.idx_junction) + self.x[self.n_node_wing_total:self.n_node_fuselage_tail] = x_coord_fuselage def adjust_fuselage_connectivities(self): - idx_junction_global = self.idx_junction + self.n_node_wing_total - idx_in_conn = np.where(self.conn == idx_junction_global) - + idx_in_conn = np.where(self.conn == self.idx_junction_global) self.conn[idx_in_conn[0][0]+1, :] -= 1 if idx_in_conn[1][0] == 2: # if middle node, correct end node of element @@ -184,6 +192,17 @@ def adjust_fuselage_connectivities(self): #several matches possible if junction node is not middle node self.conn[idx_in_conn[0][i_match], idx_in_conn[1][i_match]] = 0 + def add_additional_element_for_low_wing(self): + self.x[-1] = self.x[0] + self.y[-1] = self.y[0] + self.z[-1] = self.vertical_wing_position / 2 + self.conn[-1, 0] = 0 + self.conn[-1, 1] = self.idx_junction_global + self.conn[-1, 2] = self.n_node - 1 + self.elem_stiffness[-1] = 1 + self.elem_mass[-1] = 1 + + def set_beam_properties_fuselage(self): self.set_x_coordinate_fuselage() @@ -193,10 +212,12 @@ def set_beam_properties_fuselage(self): for ilocalnode in range(self.n_node_elem): self.frame_of_reference_delta[ielem, ilocalnode, :] = [0.0, 1.0, 0.0] self.elem_stiffness[self.n_elem_per_wing*2:] = 1 - self.elem_mass[self.n_elem_per_wing*2:] = 2 + self.elem_mass[self.n_elem_per_wing*2:] = 1 - - self.adjust_fuselage_connectivities() + if self.vertical_wing_position == 0: + self.adjust_fuselage_connectivities() + else: + self.add_additional_element_for_low_wing() self.boundary_conditions[self.n_node_wing_total] = -1 self.boundary_conditions[-1] = -1 diff --git a/tests/uvlm/static/nonlifting/geometry_parameter_models.json b/tests/uvlm/static/nonlifting/geometry_parameter_models.json index 14a429e06..06aae741b 100644 --- a/tests/uvlm/static/nonlifting/geometry_parameter_models.json +++ b/tests/uvlm/static/nonlifting/geometry_parameter_models.json @@ -1 +1 @@ -{"phantom_wing": {"length_radius_ratio": 50.0, "radius_chord_ratio": 0.2, "vertical_wing_position": 0, "radius_half_wingspan_ratio": 0.02, "length_offset_nose_to_wing_ratio": 5.0, "fuselage_shape": "cylindrical"}} \ No newline at end of file +{"phantom_wing": {"length_radius_ratio": 50.0, "radius_chord_ratio": 0.2, "vertical_wing_position": 0.0, "radius_half_wingspan_ratio": 0.02, "length_offset_nose_to_wing_ratio": 0.5, "fuselage_shape": "cylindrical"}, "low_wing": {"length_radius_ratio": 16.0, "radius_chord_ratio": 0.5, "vertical_wing_position": -0.5, "radius_half_wingspan_ratio": 0.222, "fuselage_shape": "cylindrical", "length_offset_nose_to_wing_ratio": 0.5}} \ No newline at end of file diff --git a/tests/uvlm/static/nonlifting/test_vlm_coupled_spm.py b/tests/uvlm/static/nonlifting/test_vlm_coupled_spm.py index 6a8090a33..edb65568d 100644 --- a/tests/uvlm/static/nonlifting/test_vlm_coupled_spm.py +++ b/tests/uvlm/static/nonlifting/test_vlm_coupled_spm.py @@ -85,6 +85,70 @@ def test_phantom_panels(self): np.testing.assert_array_almost_equal(list_results_lift_distribution[0][3:, 1], list_results_lift_distribution[1][3:, 1], decimal=3) + def test_fuselage_wing_configuration(self): + """ + Lift distribution on a low wing configuration is computed. The final + results are compared to a previous solution (backward compatibility) + that matches the experimental lift distribution for this case. + """ + self.define_folder() + model = 'low_wing' + fuselage_length = 10 + dict_geometry_parameters = self.get_geometry_parameters(model, + fuselage_length=fuselage_length) + + # Freestream Conditions + alpha_deg = 2.9 + u_inf = 10 + + # Discretization + dict_discretization = { + 'n_elem_per_wing': 10, + 'n_elem_fuselage': 30, + 'num_chordwise_panels': 8, + 'num_radial_panels': 36 + } + + # Simulation settings + horseshoe = True + phantom_test = False + lifting_only = False + # Simlation Solver Flow + flow = ['BeamLoader', + 'AerogridLoader', + 'NonliftingbodygridLoader', + 'StaticUvlm', + 'BeamLoads', + 'LiftDistribution', + 'AerogridPlot', + ] + case_name = model + wing_fuselage_model = self.generate_model(case_name, + dict_geometry_parameters, + dict_discretization, + lifting_only) + + self.generate_simulation_settings(flow, + wing_fuselage_model, + alpha_deg, + u_inf, + lifting_only, + horseshoe=horseshoe, + phantom_test=phantom_test) + # run simulation + wing_fuselage_model.run() + + # get results + lift_distribution = self.load_lift_distribution( + self.output_route + '/' + case_name, + wing_fuselage_model.structure.n_node_right_wing + ) + + # check results + lift_distribution_test = np.loadtxt(self.route_test_dir + "/test_data/results_{}.csv".format(model)) + with self.subTest('lift distribution'): + np.testing.assert_array_almost_equal(lift_distribution_test, lift_distribution, decimal=3) + def get_geometry_parameters(self, model_name,fuselage_length=10): """ Geometry parameters are loaded from json init file for the specified model. @@ -102,7 +166,7 @@ def get_geometry_parameters(self, model_name,fuselage_length=10): geometry_parameters['chord']=geometry_parameters['max_radius']/parameter_models['radius_chord_ratio'] geometry_parameters['half_wingspan'] = geometry_parameters['max_radius']/parameter_models['radius_half_wingspan_ratio'] geometry_parameters['offset_nose_wing'] = parameter_models['length_offset_nose_to_wing_ratio'] * fuselage_length - + geometry_parameters['vertical_wing_position'] = parameter_models['vertical_wing_position'] * geometry_parameters['max_radius'] return geometry_parameters def generate_model(self, From ba7537f4ee89b5958556e20316e0326327ecf674 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stefanie=20D=C3=BCssler?= Date: Sat, 22 Jul 2023 21:48:07 +0800 Subject: [PATCH 133/232] fix [unittest] remove prints used for debugging --- tests/uvlm/static/nonlifting/test_vlm_coupled_spm.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/uvlm/static/nonlifting/test_vlm_coupled_spm.py b/tests/uvlm/static/nonlifting/test_vlm_coupled_spm.py index edb65568d..d855bc872 100644 --- a/tests/uvlm/static/nonlifting/test_vlm_coupled_spm.py +++ b/tests/uvlm/static/nonlifting/test_vlm_coupled_spm.py @@ -63,7 +63,7 @@ def test_phantom_panels(self): if lifting_only: flow_case.remove('NonliftingbodygridLoader') - print(flow_case) + self.generate_simulation_settings(flow_case, phantom_wing, alpha_deg, From 6296f0fde919ebc850e120977fed7d72268a20bd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stefanie=20D=C3=BCssler?= Date: Sat, 22 Jul 2023 21:49:10 +0800 Subject: [PATCH 134/232] add [unittest] test data for compatibility test of fuselage wing test --- .../nonlifting/test_data/results_low_wing.csv | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 tests/uvlm/static/nonlifting/test_data/results_low_wing.csv diff --git a/tests/uvlm/static/nonlifting/test_data/results_low_wing.csv b/tests/uvlm/static/nonlifting/test_data/results_low_wing.csv new file mode 100644 index 000000000..0b4cd5480 --- /dev/null +++ b/tests/uvlm/static/nonlifting/test_data/results_low_wing.csv @@ -0,0 +1,21 @@ +0.000000000000000000e+00 0.000000000000000000e+00 +0.000000000000000000e+00 0.000000000000000000e+00 +0.000000000000000000e+00 0.000000000000000000e+00 +0.000000000000000000e+00 0.000000000000000000e+00 +5.630631000000000386e-01 2.615295000000000258e-01 +7.038288000000000322e-01 2.573984999999999745e-01 +8.445945999999999732e-01 2.555727999999999889e-01 +9.853604000000000251e-01 2.519395999999999858e-01 +1.126125999999999960e+00 2.473181999999999880e-01 +1.266891999999999907e+00 2.418879999999999919e-01 +1.407658000000000076e+00 2.356892999999999905e-01 +1.548423000000000105e+00 2.286781999999999981e-01 +1.689189000000000052e+00 2.207414000000000043e-01 +1.829954999999999998e+00 2.116945000000000077e-01 +1.970720999999999945e+00 2.012666999999999928e-01 +2.111486000000000196e+00 1.890651999999999888e-01 +2.252251999999999921e+00 1.745037999999999867e-01 +2.393018000000000089e+00 1.566439000000000026e-01 +2.533783999999999814e+00 1.337800000000000100e-01 +2.674549999999999983e+00 1.019320999999999977e-01 +2.815315000000000012e+00 8.357220999999999400e-02 From 839192bfe3b3a0cb88c2c7a00f2bd610f110196d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stefanie=20D=C3=BCssler?= Date: Sat, 22 Jul 2023 22:26:55 +0800 Subject: [PATCH 135/232] refactor [unittest] move and rename folder with source panel method related tests --- .../define_simulation_settings.py | 0 .../fuselage_wing_configuration.py | 0 .../fuselage_wing_configuration/fwc_aero.py | 0 .../fwc_fuselage.py | 0 .../fwc_structure.py | 9 +- .../geometry_parameter_models.json | 0 .../test_data/results_low_wing.csv | 0 .../test_source_panel_method.py | 0 .../test_vlm_coupled_spm.py | 146 +++++++++--------- 9 files changed, 82 insertions(+), 73 deletions(-) rename tests/{uvlm/static/nonlifting => sourcepanelmethod}/define_simulation_settings.py (100%) rename tests/{uvlm/static/nonlifting => sourcepanelmethod}/fuselage_wing_configuration/fuselage_wing_configuration.py (100%) rename tests/{uvlm/static/nonlifting => sourcepanelmethod}/fuselage_wing_configuration/fwc_aero.py (100%) rename tests/{uvlm/static/nonlifting => sourcepanelmethod}/fuselage_wing_configuration/fwc_fuselage.py (100%) rename tests/{uvlm/static/nonlifting => sourcepanelmethod}/fuselage_wing_configuration/fwc_structure.py (96%) rename tests/{uvlm/static/nonlifting => sourcepanelmethod}/geometry_parameter_models.json (100%) rename tests/{uvlm/static/nonlifting => sourcepanelmethod}/test_data/results_low_wing.csv (100%) rename tests/{uvlm/static/nonlifting => sourcepanelmethod}/test_source_panel_method.py (100%) rename tests/{uvlm/static/nonlifting => sourcepanelmethod}/test_vlm_coupled_spm.py (68%) diff --git a/tests/uvlm/static/nonlifting/define_simulation_settings.py b/tests/sourcepanelmethod/define_simulation_settings.py similarity index 100% rename from tests/uvlm/static/nonlifting/define_simulation_settings.py rename to tests/sourcepanelmethod/define_simulation_settings.py diff --git a/tests/uvlm/static/nonlifting/fuselage_wing_configuration/fuselage_wing_configuration.py b/tests/sourcepanelmethod/fuselage_wing_configuration/fuselage_wing_configuration.py similarity index 100% rename from tests/uvlm/static/nonlifting/fuselage_wing_configuration/fuselage_wing_configuration.py rename to tests/sourcepanelmethod/fuselage_wing_configuration/fuselage_wing_configuration.py diff --git a/tests/uvlm/static/nonlifting/fuselage_wing_configuration/fwc_aero.py b/tests/sourcepanelmethod/fuselage_wing_configuration/fwc_aero.py similarity index 100% rename from tests/uvlm/static/nonlifting/fuselage_wing_configuration/fwc_aero.py rename to tests/sourcepanelmethod/fuselage_wing_configuration/fwc_aero.py diff --git a/tests/uvlm/static/nonlifting/fuselage_wing_configuration/fwc_fuselage.py b/tests/sourcepanelmethod/fuselage_wing_configuration/fwc_fuselage.py similarity index 100% rename from tests/uvlm/static/nonlifting/fuselage_wing_configuration/fwc_fuselage.py rename to tests/sourcepanelmethod/fuselage_wing_configuration/fwc_fuselage.py diff --git a/tests/uvlm/static/nonlifting/fuselage_wing_configuration/fwc_structure.py b/tests/sourcepanelmethod/fuselage_wing_configuration/fwc_structure.py similarity index 96% rename from tests/uvlm/static/nonlifting/fuselage_wing_configuration/fwc_structure.py rename to tests/sourcepanelmethod/fuselage_wing_configuration/fwc_structure.py index e23c3d2e0..d201007f6 100644 --- a/tests/uvlm/static/nonlifting/fuselage_wing_configuration/fwc_structure.py +++ b/tests/sourcepanelmethod/fuselage_wing_configuration/fwc_structure.py @@ -155,7 +155,7 @@ def mirror_wing_beam(self): self.elem_mass[self.n_elem_per_wing:2*self.n_elem_per_wing] = self.elem_mass[:self.n_elem_per_wing] self.beam_number[self.n_elem_per_wing:2*self.n_elem_per_wing] = 1 - self.boundary_conditions[self.n_node_wing_total] = -1 # free tip + self.boundary_conditions[self.n_node_wing_total-1] = -1 # free tip self.conn[self.n_elem_per_wing:2*self.n_elem_per_wing, :] = self.conn[:self.n_elem_per_wing, :] + self.n_node_right_wing - 1 self.conn[self.n_elem_per_wing, 0] = 0 @@ -201,11 +201,12 @@ def add_additional_element_for_low_wing(self): self.conn[-1, 2] = self.n_node - 1 self.elem_stiffness[-1] = 1 self.elem_mass[-1] = 1 + self.beam_number[-1] = 3 def set_beam_properties_fuselage(self): self.set_x_coordinate_fuselage() - + self.beam_number[self.n_elem_per_wing*2:] = 2 for ielem in range(self.n_elem_per_wing * 2,self.n_elem): self.conn[ielem, :] = ((np.ones((3, )) * (ielem-self.n_elem_per_wing * 2) * (self.n_node_elem - 1)) + [0, 2, 1]) + self.n_node_wing_total @@ -232,6 +233,10 @@ def write_input_file(self): Writes previously defined parameters to an .h5 file which serves later as an input file for SHARPy. """ + np.savetxt('./conn.csv', self.conn) + np.savetxt('./bc.csv', self.boundary_conditions) + np.savetxt('./elem_stiffness.csv', self.elem_stiffness) + np.savetxt('./coordinates.csv', np.column_stack((self.x, self.y, self.z))) with h5.File(self.route + '/' + self.case_name + '.fem.h5', 'a') as h5file: h5file.create_dataset('coordinates', data=np.column_stack((self.x, self.y, self.z))) h5file.create_dataset('connectivities', data=self.conn) diff --git a/tests/uvlm/static/nonlifting/geometry_parameter_models.json b/tests/sourcepanelmethod/geometry_parameter_models.json similarity index 100% rename from tests/uvlm/static/nonlifting/geometry_parameter_models.json rename to tests/sourcepanelmethod/geometry_parameter_models.json diff --git a/tests/uvlm/static/nonlifting/test_data/results_low_wing.csv b/tests/sourcepanelmethod/test_data/results_low_wing.csv similarity index 100% rename from tests/uvlm/static/nonlifting/test_data/results_low_wing.csv rename to tests/sourcepanelmethod/test_data/results_low_wing.csv diff --git a/tests/uvlm/static/nonlifting/test_source_panel_method.py b/tests/sourcepanelmethod/test_source_panel_method.py similarity index 100% rename from tests/uvlm/static/nonlifting/test_source_panel_method.py rename to tests/sourcepanelmethod/test_source_panel_method.py diff --git a/tests/uvlm/static/nonlifting/test_vlm_coupled_spm.py b/tests/sourcepanelmethod/test_vlm_coupled_spm.py similarity index 68% rename from tests/uvlm/static/nonlifting/test_vlm_coupled_spm.py rename to tests/sourcepanelmethod/test_vlm_coupled_spm.py index d855bc872..7efae20b5 100644 --- a/tests/uvlm/static/nonlifting/test_vlm_coupled_spm.py +++ b/tests/sourcepanelmethod/test_vlm_coupled_spm.py @@ -8,81 +8,84 @@ class TestVlmCoupledWithSourcePanelMethod(unittest.TestCase): - def test_phantom_panels(self): - """ - The lift distribution over a rectangular high-aspect ratio - wing is computed. First a wing_only configuration is considered, - while second, we activate the phantom panels created within the - fuselage although, the effect of the source panels is omitted. - With the defined interpolation scheme, the same lift distribution - must be obtained. - """ - self.define_folder() + # def test_phantom_panels(self): + # """ + # The lift distribution over a rectangular high-aspect ratio + # wing is computed. First a wing_only configuration is considered, + # while second, we activate the phantom panels created within the + # fuselage although, the effect of the source panels is omitted. + # With the defined interpolation scheme, the same lift distribution + # must be obtained. + # """ + # self.define_folder() - model = 'phantom_wing' - fuselage_length = 10 - dict_geometry_parameters = self.get_geometry_parameters(model, - fuselage_length=fuselage_length) - - # Freestream Conditions - alpha_deg = 5 - u_inf = 10 - - # Discretization - dict_discretization = { - 'n_elem_per_wing': 20, - 'n_elem_fuselage': 10, - 'num_chordwise_panels': 8 - } - # Simulation settings - horseshoe = True - list_phantom_test = [False, True] - # Simlation Solver Flow - flow = ['BeamLoader', - 'AerogridLoader', - 'NonliftingbodygridLoader', - 'StaticUvlm', - 'BeamLoads', - 'LiftDistribution', - ] - list_results_lift_distribution = [] - # define model variables - for icase in range(len(list_phantom_test)): - phantom_test = list_phantom_test[icase] - lifting_only = not phantom_test - case_name = model + '_coupled_{}'.format(int(phantom_test)) + # model = 'phantom_wing' + # fuselage_length = 10 + # dict_geometry_parameters = self.get_geometry_parameters(model, + # fuselage_length=fuselage_length) + + # # Freestream Conditions + # alpha_deg = 5 + # u_inf = 10 + + # # Discretization + # dict_discretization = { + # 'n_elem_per_wing': 20, + # 'n_elem_fuselage': 10, + # 'num_chordwise_panels': 8 + # } + + # # Simulation settings + # horseshoe = True + # list_phantom_test = [False, True] + # # Simlation Solver Flow + # flow = ['BeamLoader', + # 'AerogridLoader', + # 'NonliftingbodygridLoader', + # 'StaticUvlm', + # 'StaticCoupled', + # 'BeamLoads', + # 'LiftDistribution', + # 'AerogridPlot', + # ] + # list_results_lift_distribution = [] + # # define model variables + # for icase in range(len(list_phantom_test)): + # phantom_test = list_phantom_test[icase] + # lifting_only = not phantom_test + # case_name = model + '_coupled_{}'.format(int(phantom_test)) - # generate ellipsoid model - phantom_wing = self.generate_model(case_name, - dict_geometry_parameters, - dict_discretization, - lifting_only) - # Adjust flow for case - flow_case = flow.copy() - - if lifting_only: - flow_case.remove('NonliftingbodygridLoader') + # # generate ellipsoid model + # phantom_wing = self.generate_model(case_name, + # dict_geometry_parameters, + # dict_discretization, + # lifting_only) + # # Adjust flow for case + # flow_case = flow.copy() + + # if lifting_only: + # flow_case.remove('NonliftingbodygridLoader') - self.generate_simulation_settings(flow_case, - phantom_wing, - alpha_deg, - u_inf, - lifting_only, - horseshoe=horseshoe, - phantom_test=phantom_test) - # run simulation - phantom_wing.run() - - # get results - list_results_lift_distribution.append(self.load_lift_distribution( - self.output_route + '/' + case_name, - phantom_wing.structure.n_node_right_wing - )) - - # check results - with self.subTest('lift distribution'): - np.testing.assert_array_almost_equal(list_results_lift_distribution[0][3:, 1], list_results_lift_distribution[1][3:, 1], decimal=3) + # self.generate_simulation_settings(flow_case, + # phantom_wing, + # alpha_deg, + # u_inf, + # lifting_only, + # horseshoe=horseshoe, + # phantom_test=phantom_test) + # # run simulation + # phantom_wing.run() + + # # get results + # list_results_lift_distribution.append(self.load_lift_distribution( + # self.output_route + '/' + case_name, + # phantom_wing.structure.n_node_right_wing + # )) + + # # check results + # with self.subTest('lift distribution'): + # np.testing.assert_array_almost_equal(list_results_lift_distribution[0][3:, 1], list_results_lift_distribution[1][3:, 1], decimal=3) def test_fuselage_wing_configuration(self): @@ -118,6 +121,7 @@ def test_fuselage_wing_configuration(self): 'AerogridLoader', 'NonliftingbodygridLoader', 'StaticUvlm', + 'StaticCoupled', 'BeamLoads', 'LiftDistribution', 'AerogridPlot', From 5186b0269dd866c04d5769e10402a05ddc85d157 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stefanie=20D=C3=BCssler?= Date: Sat, 22 Jul 2023 22:52:58 +0800 Subject: [PATCH 136/232] add [templates] fuselage wing configuration object - moved from unittest to more general location for wider usage --- sharpy/cases/templates/fuselage_wing_configuration/__init__.py | 0 .../fuselage_wing_configuration/fuselage_wing_configuration.py | 0 .../cases/templates}/fuselage_wing_configuration/fwc_aero.py | 0 .../templates}/fuselage_wing_configuration/fwc_fuselage.py | 0 .../templates}/fuselage_wing_configuration/fwc_structure.py | 0 tests/sourcepanelmethod/test_vlm_coupled_spm.py | 2 +- 6 files changed, 1 insertion(+), 1 deletion(-) create mode 100644 sharpy/cases/templates/fuselage_wing_configuration/__init__.py rename {tests/sourcepanelmethod => sharpy/cases/templates}/fuselage_wing_configuration/fuselage_wing_configuration.py (100%) rename {tests/sourcepanelmethod => sharpy/cases/templates}/fuselage_wing_configuration/fwc_aero.py (100%) rename {tests/sourcepanelmethod => sharpy/cases/templates}/fuselage_wing_configuration/fwc_fuselage.py (100%) rename {tests/sourcepanelmethod => sharpy/cases/templates}/fuselage_wing_configuration/fwc_structure.py (100%) diff --git a/sharpy/cases/templates/fuselage_wing_configuration/__init__.py b/sharpy/cases/templates/fuselage_wing_configuration/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/tests/sourcepanelmethod/fuselage_wing_configuration/fuselage_wing_configuration.py b/sharpy/cases/templates/fuselage_wing_configuration/fuselage_wing_configuration.py similarity index 100% rename from tests/sourcepanelmethod/fuselage_wing_configuration/fuselage_wing_configuration.py rename to sharpy/cases/templates/fuselage_wing_configuration/fuselage_wing_configuration.py diff --git a/tests/sourcepanelmethod/fuselage_wing_configuration/fwc_aero.py b/sharpy/cases/templates/fuselage_wing_configuration/fwc_aero.py similarity index 100% rename from tests/sourcepanelmethod/fuselage_wing_configuration/fwc_aero.py rename to sharpy/cases/templates/fuselage_wing_configuration/fwc_aero.py diff --git a/tests/sourcepanelmethod/fuselage_wing_configuration/fwc_fuselage.py b/sharpy/cases/templates/fuselage_wing_configuration/fwc_fuselage.py similarity index 100% rename from tests/sourcepanelmethod/fuselage_wing_configuration/fwc_fuselage.py rename to sharpy/cases/templates/fuselage_wing_configuration/fwc_fuselage.py diff --git a/tests/sourcepanelmethod/fuselage_wing_configuration/fwc_structure.py b/sharpy/cases/templates/fuselage_wing_configuration/fwc_structure.py similarity index 100% rename from tests/sourcepanelmethod/fuselage_wing_configuration/fwc_structure.py rename to sharpy/cases/templates/fuselage_wing_configuration/fwc_structure.py diff --git a/tests/sourcepanelmethod/test_vlm_coupled_spm.py b/tests/sourcepanelmethod/test_vlm_coupled_spm.py index 7efae20b5..944d9b346 100644 --- a/tests/sourcepanelmethod/test_vlm_coupled_spm.py +++ b/tests/sourcepanelmethod/test_vlm_coupled_spm.py @@ -1,7 +1,7 @@ import unittest import os import numpy as np -from fuselage_wing_configuration.fuselage_wing_configuration import Fuselage_Wing_Configuration +from sharpy.cases.templates.fuselage_wing_configuration.fuselage_wing_configuration import Fuselage_Wing_Configuration from define_simulation_settings import define_simulation_settings import json From 87355f5b187ff82dfd6053590599c2d691e85a6c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stefanie=20D=C3=BCssler?= Date: Sat, 22 Jul 2023 22:55:45 +0800 Subject: [PATCH 137/232] add [unittest] staticcoupled test for fuselage wing configuration --- tests/sourcepanelmethod/__init__.py | 0 ...ing.csv => results_low_wing_coupled_0.csv} | 0 .../test_data/results_low_wing_coupled_1.csv | 21 +++++++ .../sourcepanelmethod/test_vlm_coupled_spm.py | 59 +++++++++++-------- 4 files changed, 54 insertions(+), 26 deletions(-) create mode 100644 tests/sourcepanelmethod/__init__.py rename tests/sourcepanelmethod/test_data/{results_low_wing.csv => results_low_wing_coupled_0.csv} (100%) create mode 100644 tests/sourcepanelmethod/test_data/results_low_wing_coupled_1.csv diff --git a/tests/sourcepanelmethod/__init__.py b/tests/sourcepanelmethod/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/tests/sourcepanelmethod/test_data/results_low_wing.csv b/tests/sourcepanelmethod/test_data/results_low_wing_coupled_0.csv similarity index 100% rename from tests/sourcepanelmethod/test_data/results_low_wing.csv rename to tests/sourcepanelmethod/test_data/results_low_wing_coupled_0.csv diff --git a/tests/sourcepanelmethod/test_data/results_low_wing_coupled_1.csv b/tests/sourcepanelmethod/test_data/results_low_wing_coupled_1.csv new file mode 100644 index 000000000..f173785a9 --- /dev/null +++ b/tests/sourcepanelmethod/test_data/results_low_wing_coupled_1.csv @@ -0,0 +1,21 @@ +0.000000000000000000e+00 0.000000000000000000e+00 +0.000000000000000000e+00 0.000000000000000000e+00 +0.000000000000000000e+00 0.000000000000000000e+00 +0.000000000000000000e+00 0.000000000000000000e+00 +5.630627000000000271e-01 2.643929999999999891e-01 +7.038282999999999623e-01 2.623985999999999819e-01 +8.445937999999999501e-01 2.607677999999999940e-01 +9.853593000000000490e-01 2.573134000000000254e-01 +1.126125000000000043e+00 2.528622999999999843e-01 +1.266890000000000072e+00 2.475815000000000099e-01 +1.407656000000000018e+00 2.415008999999999906e-01 +1.548421000000000047e+00 2.345675999999999872e-01 +1.689186000000000076e+00 2.266600000000000004e-01 +1.829952000000000023e+00 2.175848999999999978e-01 +1.970717000000000052e+00 2.070622000000000018e-01 +2.111482000000000081e+00 1.946869999999999989e-01 +2.252247000000000110e+00 1.798578000000000121e-01 +2.393012999999999835e+00 1.616124999999999923e-01 +2.533777999999999864e+00 1.382088999999999956e-01 +2.674542999999999893e+00 1.055934000000000039e-01 +2.815309000000000061e+00 8.848820000000000296e-02 diff --git a/tests/sourcepanelmethod/test_vlm_coupled_spm.py b/tests/sourcepanelmethod/test_vlm_coupled_spm.py index 944d9b346..715344ff4 100644 --- a/tests/sourcepanelmethod/test_vlm_coupled_spm.py +++ b/tests/sourcepanelmethod/test_vlm_coupled_spm.py @@ -126,32 +126,39 @@ def test_fuselage_wing_configuration(self): 'LiftDistribution', 'AerogridPlot', ] - case_name = model - wing_fuselage_model = self.generate_model(case_name, - dict_geometry_parameters, - dict_discretization, - lifting_only) - - self.generate_simulation_settings(flow, - wing_fuselage_model, - alpha_deg, - u_inf, - lifting_only, - horseshoe=horseshoe, - phantom_test=phantom_test) - # run simulation - wing_fuselage_model.run() - - # get results - lift_distribution = self.load_lift_distribution( - self.output_route + '/' + case_name, - wing_fuselage_model.structure.n_node_right_wing - ) - - # check results - lift_distribution_test = np.loadtxt(self.route_test_dir + "/test_data/results_{}.csv".format(model)) - with self.subTest('lift distribution'): - np.testing.assert_array_almost_equal(lift_distribution_test, lift_distribution, decimal=3) + for static_coupled_solver in [False, True]: + case_name = '{}_coupled_{}'.format(model, int(static_coupled_solver)) + wing_fuselage_model = self.generate_model(case_name, + dict_geometry_parameters, + dict_discretization, + lifting_only) + + flow_case = flow.copy() + if static_coupled_solver: + flow_case.remove('StaticUvlm') + else: + flow_case.remove('StaticCoupled') + print(flow_case) + self.generate_simulation_settings(flow_case, + wing_fuselage_model, + alpha_deg, + u_inf, + lifting_only, + horseshoe=horseshoe, + phantom_test=phantom_test) + # run simulation + wing_fuselage_model.run() + # get results + lift_distribution = self.load_lift_distribution( + self.output_route + '/' + case_name, + wing_fuselage_model.structure.n_node_right_wing + ) + + # check results + lift_distribution_test = np.loadtxt(self.route_test_dir + "/test_data/results_{}.csv".format(case_name)) + with self.subTest('lift distribution and spanwise wing deformation'): + np.testing.assert_array_almost_equal(lift_distribution_test, lift_distribution, decimal=3) + def get_geometry_parameters(self, model_name,fuselage_length=10): """ From cdc7a5017f01a582f6d7da111d916676032ec636 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stefanie=20D=C3=BCssler?= Date: Sat, 22 Jul 2023 22:58:15 +0800 Subject: [PATCH 138/232] remove [fuselage wing template] debug savetxt leftovers --- .../templates/fuselage_wing_configuration/fwc_structure.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/sharpy/cases/templates/fuselage_wing_configuration/fwc_structure.py b/sharpy/cases/templates/fuselage_wing_configuration/fwc_structure.py index d201007f6..ac357fb3c 100644 --- a/sharpy/cases/templates/fuselage_wing_configuration/fwc_structure.py +++ b/sharpy/cases/templates/fuselage_wing_configuration/fwc_structure.py @@ -233,10 +233,6 @@ def write_input_file(self): Writes previously defined parameters to an .h5 file which serves later as an input file for SHARPy. """ - np.savetxt('./conn.csv', self.conn) - np.savetxt('./bc.csv', self.boundary_conditions) - np.savetxt('./elem_stiffness.csv', self.elem_stiffness) - np.savetxt('./coordinates.csv', np.column_stack((self.x, self.y, self.z))) with h5.File(self.route + '/' + self.case_name + '.fem.h5', 'a') as h5file: h5file.create_dataset('coordinates', data=np.column_stack((self.x, self.y, self.z))) h5file.create_dataset('connectivities', data=self.conn) From 63e66a4eec8d015bf0b77b14a763d4c02931d844 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stefanie=20D=C3=BCssler?= Date: Sat, 22 Jul 2023 23:00:34 +0800 Subject: [PATCH 139/232] update [unittest] backwards compability results for fuselage wing configuration - correct uvlm version with including phantom panels to force calculation --- .../test_data/results_low_wing_coupled_0.csv | 34 +++++++++---------- .../test_data/results_low_wing_coupled_1.csv | 34 +++++++++---------- 2 files changed, 34 insertions(+), 34 deletions(-) diff --git a/tests/sourcepanelmethod/test_data/results_low_wing_coupled_0.csv b/tests/sourcepanelmethod/test_data/results_low_wing_coupled_0.csv index 0b4cd5480..d7cd3e6e9 100644 --- a/tests/sourcepanelmethod/test_data/results_low_wing_coupled_0.csv +++ b/tests/sourcepanelmethod/test_data/results_low_wing_coupled_0.csv @@ -2,20 +2,20 @@ 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 -5.630631000000000386e-01 2.615295000000000258e-01 -7.038288000000000322e-01 2.573984999999999745e-01 -8.445945999999999732e-01 2.555727999999999889e-01 -9.853604000000000251e-01 2.519395999999999858e-01 -1.126125999999999960e+00 2.473181999999999880e-01 -1.266891999999999907e+00 2.418879999999999919e-01 -1.407658000000000076e+00 2.356892999999999905e-01 -1.548423000000000105e+00 2.286781999999999981e-01 -1.689189000000000052e+00 2.207414000000000043e-01 -1.829954999999999998e+00 2.116945000000000077e-01 -1.970720999999999945e+00 2.012666999999999928e-01 -2.111486000000000196e+00 1.890651999999999888e-01 -2.252251999999999921e+00 1.745037999999999867e-01 -2.393018000000000089e+00 1.566439000000000026e-01 -2.533783999999999814e+00 1.337800000000000100e-01 -2.674549999999999983e+00 1.019320999999999977e-01 -2.815315000000000012e+00 8.357220999999999400e-02 +5.630631000000000386e-01 2.637046000000000112e-01 +7.038288000000000322e-01 2.587818000000000063e-01 +8.445945999999999732e-01 2.560145999999999811e-01 +9.853604000000000251e-01 2.521720000000000073e-01 +1.126125999999999960e+00 2.474606000000000028e-01 +1.266891999999999907e+00 2.419823000000000113e-01 +1.407658000000000076e+00 2.357548999999999895e-01 +1.548423000000000105e+00 2.287254999999999983e-01 +1.689189000000000052e+00 2.207763000000000086e-01 +1.829954999999999998e+00 2.117207000000000117e-01 +1.970720999999999945e+00 2.012865000000000071e-01 +2.111486000000000196e+00 1.890803000000000067e-01 +2.252251999999999921e+00 1.745153000000000121e-01 +2.393018000000000089e+00 1.566525000000000001e-01 +2.533783999999999814e+00 1.337860999999999911e-01 +2.674549999999999983e+00 1.019361000000000017e-01 +2.815315000000000012e+00 8.357517000000000418e-02 diff --git a/tests/sourcepanelmethod/test_data/results_low_wing_coupled_1.csv b/tests/sourcepanelmethod/test_data/results_low_wing_coupled_1.csv index f173785a9..ee100945e 100644 --- a/tests/sourcepanelmethod/test_data/results_low_wing_coupled_1.csv +++ b/tests/sourcepanelmethod/test_data/results_low_wing_coupled_1.csv @@ -2,20 +2,20 @@ 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 -5.630627000000000271e-01 2.643929999999999891e-01 -7.038282999999999623e-01 2.623985999999999819e-01 -8.445937999999999501e-01 2.607677999999999940e-01 -9.853593000000000490e-01 2.573134000000000254e-01 -1.126125000000000043e+00 2.528622999999999843e-01 -1.266890000000000072e+00 2.475815000000000099e-01 -1.407656000000000018e+00 2.415008999999999906e-01 -1.548421000000000047e+00 2.345675999999999872e-01 -1.689186000000000076e+00 2.266600000000000004e-01 -1.829952000000000023e+00 2.175848999999999978e-01 -1.970717000000000052e+00 2.070622000000000018e-01 -2.111482000000000081e+00 1.946869999999999989e-01 -2.252247000000000110e+00 1.798578000000000121e-01 -2.393012999999999835e+00 1.616124999999999923e-01 -2.533777999999999864e+00 1.382088999999999956e-01 -2.674542999999999893e+00 1.055934000000000039e-01 -2.815309000000000061e+00 8.848820000000000296e-02 +5.630627999999999744e-01 2.663324000000000247e-01 +7.038284000000000207e-01 2.638707000000000136e-01 +8.445939000000000085e-01 2.612416000000000182e-01 +9.853593999999999964e-01 2.575641000000000180e-01 +1.126125000000000043e+00 2.530164999999999775e-01 +1.266890000000000072e+00 2.476839999999999875e-01 +1.407656000000000018e+00 2.415724999999999956e-01 +1.548421000000000047e+00 2.346193000000000028e-01 +1.689186000000000076e+00 2.266981999999999886e-01 +1.829952000000000023e+00 2.176136999999999933e-01 +1.970717000000000052e+00 2.070840999999999932e-01 +2.111482000000000081e+00 1.947038000000000102e-01 +2.252247999999999806e+00 1.798706999999999945e-01 +2.393012999999999835e+00 1.616222999999999965e-01 +2.533777999999999864e+00 1.382161000000000084e-01 +2.674544000000000032e+00 1.055984999999999979e-01 +2.815309000000000061e+00 8.849437000000000275e-02 From f04bbadbb649935d09197854ec9c7f14fc949266 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stefanie=20D=C3=BCssler?= Date: Sun, 23 Jul 2023 20:55:13 +0800 Subject: [PATCH 140/232] fix [unittest] structural model of fuselage wing model - a few bugs, like one fuselage node to much, wrong boundary condition locations, wrong aero node definition --- .../fuselage_wing_configuration/fwc_aero.py | 4 +- .../fwc_structure.py | 13 +- .../sourcepanelmethod/test_vlm_coupled_spm.py | 147 +++++++++--------- 3 files changed, 82 insertions(+), 82 deletions(-) diff --git a/sharpy/cases/templates/fuselage_wing_configuration/fwc_aero.py b/sharpy/cases/templates/fuselage_wing_configuration/fwc_aero.py index 752837738..a9e7674ba 100644 --- a/sharpy/cases/templates/fuselage_wing_configuration/fwc_aero.py +++ b/sharpy/cases/templates/fuselage_wing_configuration/fwc_aero.py @@ -62,11 +62,11 @@ def set_wing_properties(self): Sets necessary parameters to define the lifting surfaces of one wing (right). """ - self.aero_node[:self.structure.n_node_wing_total] = True if not self.lifting_only: self.aero_node[:self.structure.n_node_right_wing] = abs(self.structure.y[:self.structure.n_node_right_wing]) > self.get_y_junction() self.aero_node[self.structure.n_node_right_wing:self.structure.n_node_wing_total] = self.aero_node[1:self.structure.n_node_right_wing] - + else: + self.aero_node[:self.structure.n_node_wing_total] = True self.chord[:2*self.structure.n_elem_per_wing, :] = self.chord_wing self.elastic_axis[:2*self.structure.n_elem_per_wing, :] = self.ea_wing # surf distribution 0 for right and 1 for left wing diff --git a/sharpy/cases/templates/fuselage_wing_configuration/fwc_structure.py b/sharpy/cases/templates/fuselage_wing_configuration/fwc_structure.py index ac357fb3c..c6689e511 100644 --- a/sharpy/cases/templates/fuselage_wing_configuration/fwc_structure.py +++ b/sharpy/cases/templates/fuselage_wing_configuration/fwc_structure.py @@ -61,7 +61,7 @@ def set_element_and_nodes(self): component and the total number of elements and nodes are defined here. """ - self.n_node_fuselage = self.n_elem_fuselage*(self.n_node_elem - 1) + 1 + self.n_node_fuselage = self.n_elem_fuselage*(self.n_node_elem - 1) self.n_node_right_wing = self.n_elem_per_wing*(self.n_node_elem - 1) + 1 # the left wing beam has one node less than the right one, since they shares the center node self.n_node_left_wing = self.n_node_right_wing - 1 @@ -139,7 +139,7 @@ def set_beam_properties_right_wing(self): self.frame_of_reference_delta[ielem, ilocalnode, :] = [-1.0, 0.0, 0.0] self.boundary_conditions[0] = 1 - self.boundary_conditions[self.n_node_right_wing] = -1 # free tip + self.boundary_conditions[self.n_node_right_wing-1] = -1 # free tip def mirror_wing_beam(self): """ @@ -184,8 +184,9 @@ def set_x_coordinate_fuselage(self): def adjust_fuselage_connectivities(self): idx_in_conn = np.where(self.conn == self.idx_junction_global) - self.conn[idx_in_conn[0][0]+1, :] -= 1 - if idx_in_conn[1][0] == 2: + self.conn[idx_in_conn[0][0]+1:, :] -= 1 + + if idx_in_conn[0][0] == 2: # if middle node, correct end node of element self.conn[idx_in_conn[0][0], 1] -= 1 for i_match in range(np.shape(idx_in_conn)[1]): @@ -219,8 +220,8 @@ def set_beam_properties_fuselage(self): self.adjust_fuselage_connectivities() else: self.add_additional_element_for_low_wing() - self.boundary_conditions[self.n_node_wing_total] = -1 - self.boundary_conditions[-1] = -1 + self.boundary_conditions[self.n_node_wing_total] = -1 # fuselage nose + self.boundary_conditions[self.n_node_wing_total + self.n_node_fuselage - 1] = -1 # fuselage tail def find_index_of_closest_entry(self, array_values, target_value): return np.argmin(np.abs(array_values - target_value)) diff --git a/tests/sourcepanelmethod/test_vlm_coupled_spm.py b/tests/sourcepanelmethod/test_vlm_coupled_spm.py index 715344ff4..cc37e7711 100644 --- a/tests/sourcepanelmethod/test_vlm_coupled_spm.py +++ b/tests/sourcepanelmethod/test_vlm_coupled_spm.py @@ -8,84 +8,83 @@ class TestVlmCoupledWithSourcePanelMethod(unittest.TestCase): - # def test_phantom_panels(self): - # """ - # The lift distribution over a rectangular high-aspect ratio - # wing is computed. First a wing_only configuration is considered, - # while second, we activate the phantom panels created within the - # fuselage although, the effect of the source panels is omitted. - # With the defined interpolation scheme, the same lift distribution - # must be obtained. - # """ - # self.define_folder() + def test_phantom_panels(self): + """ + The lift distribution over a rectangular high-aspect ratio + wing is computed. First a wing_only configuration is considered, + while second, we activate the phantom panels created within the + fuselage although, the effect of the source panels is omitted. + With the defined interpolation scheme, the same lift distribution + must be obtained. + """ + self.define_folder() - # model = 'phantom_wing' - # fuselage_length = 10 - # dict_geometry_parameters = self.get_geometry_parameters(model, - # fuselage_length=fuselage_length) - - # # Freestream Conditions - # alpha_deg = 5 - # u_inf = 10 - - # # Discretization - # dict_discretization = { - # 'n_elem_per_wing': 20, - # 'n_elem_fuselage': 10, - # 'num_chordwise_panels': 8 - # } - - # # Simulation settings - # horseshoe = True - # list_phantom_test = [False, True] - # # Simlation Solver Flow - # flow = ['BeamLoader', - # 'AerogridLoader', - # 'NonliftingbodygridLoader', - # 'StaticUvlm', - # 'StaticCoupled', - # 'BeamLoads', - # 'LiftDistribution', - # 'AerogridPlot', - # ] - # list_results_lift_distribution = [] - # # define model variables - # for icase in range(len(list_phantom_test)): - # phantom_test = list_phantom_test[icase] - # lifting_only = not phantom_test - # case_name = model + '_coupled_{}'.format(int(phantom_test)) + model = 'phantom_wing' + fuselage_length = 10 + dict_geometry_parameters = self.get_geometry_parameters(model, + fuselage_length=fuselage_length) + + # Freestream Conditions + alpha_deg = 5 + u_inf = 10 + + # Discretization + dict_discretization = { + 'n_elem_per_wing': 20, + 'n_elem_fuselage': 10, + 'num_chordwise_panels': 8 + } + + # Simulation settings + horseshoe = True + list_phantom_test = [False, True] + # Simlation Solver Flow + flow = ['BeamLoader', + 'AerogridLoader', + 'NonliftingbodygridLoader', + 'StaticUvlm', + 'BeamLoads', + 'LiftDistribution', + 'AerogridPlot', + ] + list_results_lift_distribution = [] + # define model variables + for icase in range(len(list_phantom_test)): + phantom_test = list_phantom_test[icase] + lifting_only = not phantom_test + case_name = model + '_coupled_{}'.format(int(phantom_test)) - # # generate ellipsoid model - # phantom_wing = self.generate_model(case_name, - # dict_geometry_parameters, - # dict_discretization, - # lifting_only) - # # Adjust flow for case - # flow_case = flow.copy() - - # if lifting_only: - # flow_case.remove('NonliftingbodygridLoader') + # generate ellipsoid model + phantom_wing = self.generate_model(case_name, + dict_geometry_parameters, + dict_discretization, + lifting_only) + # Adjust flow for case + flow_case = flow.copy() + + if lifting_only: + flow_case.remove('NonliftingbodygridLoader') - # self.generate_simulation_settings(flow_case, - # phantom_wing, - # alpha_deg, - # u_inf, - # lifting_only, - # horseshoe=horseshoe, - # phantom_test=phantom_test) - # # run simulation - # phantom_wing.run() - - # # get results - # list_results_lift_distribution.append(self.load_lift_distribution( - # self.output_route + '/' + case_name, - # phantom_wing.structure.n_node_right_wing - # )) - - # # check results - # with self.subTest('lift distribution'): - # np.testing.assert_array_almost_equal(list_results_lift_distribution[0][3:, 1], list_results_lift_distribution[1][3:, 1], decimal=3) + self.generate_simulation_settings(flow_case, + phantom_wing, + alpha_deg, + u_inf, + lifting_only, + horseshoe=horseshoe, + phantom_test=phantom_test) + # run simulation + phantom_wing.run() + + # get results + list_results_lift_distribution.append(self.load_lift_distribution( + self.output_route + '/' + case_name, + phantom_wing.structure.n_node_right_wing + )) + + # check results + with self.subTest('lift distribution'): + np.testing.assert_array_almost_equal(list_results_lift_distribution[0][3:, 1], list_results_lift_distribution[1][3:, 1], decimal=3) def test_fuselage_wing_configuration(self): From b793a38b6583f2bd350a564e6b7ad414ee02732c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stefanie=20D=C3=BCssler?= Date: Mon, 24 Jul 2023 19:59:37 +0800 Subject: [PATCH 141/232] fix [aeroutils] retrieving aero settings for StaticTrim - Before, no aero settings could be retrieved if StaticTrim is the only solver used in flow. Therefore, we add Statictrim to the list of possible solvers, and get its solver settings which could include aero solver seettings. --- sharpy/aero/utils/utils.py | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/sharpy/aero/utils/utils.py b/sharpy/aero/utils/utils.py index 93757372f..e58ed20e9 100644 --- a/sharpy/aero/utils/utils.py +++ b/sharpy/aero/utils/utils.py @@ -132,8 +132,11 @@ def span_chord(i_node_surf, zeta): def find_aerodynamic_solver_settings(settings): """ - Retrieves the settings of the first aerodynamic solver used in the solution ``flow``. - + Retrieves the settings of the first aerodynamic solver used in the solution ``flow``. + + For coupled solvers, the aerodynamic solver is found in the aero solver settings. + The StaticTrim solver can either contain a coupled or aero solver in its solver + settings (making it into a possible 3-level Matryoshka). Args: settings (dict): SHARPy settings (usually found in ``data.settings`` ) @@ -142,10 +145,12 @@ def find_aerodynamic_solver_settings(settings): tuple: Aerodynamic solver settings """ flow = settings['SHARPy']['flow'] - for solver_name in ['StaticUvlm', 'StaticCoupled', 'DynamicCoupled', 'StepUvlm']: + for solver_name in ['StaticUvlm', 'StaticCoupled', 'StaticTrim', 'DynamicCoupled', 'StepUvlm']: if solver_name in flow: aero_solver_settings = settings[solver_name] - if 'aero_solver' in settings[solver_name].keys(): + if solver_name == 'StaticTrim': + aero_solver_settings = aero_solver_settings['solver_settings']['aero_solver_settings'] + elif 'aero_solver' in settings[solver_name].keys(): aero_solver_settings = aero_solver_settings['aero_solver_settings'] return aero_solver_settings From 3d529813b4c2cfae0d00a2f6968ac67ae0054399 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stefanie=20D=C3=BCssler?= Date: Mon, 24 Jul 2023 20:04:18 +0800 Subject: [PATCH 142/232] fix [savedata] also remove old linear data output file if it exists - Before errors were raised if the linear data file already exists, as it cannot be overwritten. As for the nonlinear data file, the old linear data file is removed before. - I stumbled across it when creating the udp tutorial as I had to erase the data file in an extra cell before generating the linear system. I could delete this part in the tutorial now, too. --- .../UDP_control/tutorial_udp_control.ipynb | 12 +----------- sharpy/postproc/savedata.py | 6 ++++-- 2 files changed, 5 insertions(+), 13 deletions(-) diff --git a/docs/source/content/example_notebooks/UDP_control/tutorial_udp_control.ipynb b/docs/source/content/example_notebooks/UDP_control/tutorial_udp_control.ipynb index 659d1b65c..b0205753d 100644 --- a/docs/source/content/example_notebooks/UDP_control/tutorial_udp_control.ipynb +++ b/docs/source/content/example_notebooks/UDP_control/tutorial_udp_control.ipynb @@ -2368,18 +2368,8 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "### Run SHARPy Simulation\n", - "Before running SHARPy, we remove the old linear system generated if existing. It would cause an error that still needs to be fixed (your first contribution?)." - ] - }, - { - "cell_type": "code", + "### Run SHARPy Simulation" "execution_count": 22, - "metadata": {}, - "outputs": [], - "source": [ - "if os.path.isfile(os.path.join(output_folder, pazy_model_ROM.case_name, 'savedata', pazy_model_ROM.case_name + '.linss.h5')):\n", - " os.remove(os.path.join(output_folder, pazy_model_ROM.case_name, 'savedata', pazy_model_ROM.case_name + '.linss.h5'))" ] }, { diff --git a/sharpy/postproc/savedata.py b/sharpy/postproc/savedata.py index 50719e0bf..083dbd4e8 100644 --- a/sharpy/postproc/savedata.py +++ b/sharpy/postproc/savedata.py @@ -152,8 +152,10 @@ def initialise(self, data, custom_settings=None, caller=None, restart=False): self.filename = self.folder + self.data.settings['SHARPy']['case'] + '.data.h5' self.filename_linear = self.folder + self.data.settings['SHARPy']['case'] + '.linss.h5' - if os.path.isfile(self.filename): - os.remove(self.filename) + # remove old file if it exists + for file_path in [self.filename, self.filename_linear]: + if os.path.isfile(file_path): + os.remove(file_path) # check that there is a linear system - else return setting to false if self.settings['save_linear'] or self.settings['save_linear_uvlm']: From e666b8e1a86e55a5bbbd73fd818df8c94b593fe3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stefanie=20D=C3=BCssler?= Date: Thu, 27 Jul 2023 18:53:18 +0800 Subject: [PATCH 143/232] fix syntax warnings regarding literals - warning message for this type of syntax use of "is" and "is not" starting from Python version 3.8 --- sharpy/generators/turbvelocityfield.py | 2 +- sharpy/linear/src/libss.py | 4 ++-- sharpy/linear/src/lingebm.py | 2 +- sharpy/rom/utils/librom.py | 2 +- sharpy/solvers/dynamiccoupled.py | 2 +- sharpy/solvers/staticcoupled.py | 2 +- 6 files changed, 7 insertions(+), 7 deletions(-) diff --git a/sharpy/generators/turbvelocityfield.py b/sharpy/generators/turbvelocityfield.py index 9ab86099a..6158a1dfb 100644 --- a/sharpy/generators/turbvelocityfield.py +++ b/sharpy/generators/turbvelocityfield.py @@ -113,7 +113,7 @@ def initialise(self, in_dict): _, self.extension = os.path.splitext(self.settings['turbulent_field']) - if self.extension is '.h5': + if self.extension == '.h5': self.read_btl(self.settings['turbulent_field']) if self.extension in '.xdmf': self.read_xdmf(self.settings['turbulent_field']) diff --git a/sharpy/linear/src/libss.py b/sharpy/linear/src/libss.py index 4df2428be..e6573168d 100644 --- a/sharpy/linear/src/libss.py +++ b/sharpy/linear/src/libss.py @@ -1782,12 +1782,12 @@ def Hnorm_from_freq_resp(gv, method): Warning: only use for SISO systems! For MIMO definitions are different """ - if method is 'H2': + if method == 'H2': Nk = len(gv) gvsq = gv * gv.conj() Gnorm = np.sqrt(np.trapz(gvsq / (Nk - 1.))) - elif method is 'Hinf': + elif method == 'Hinf': Gnorm = np.linalg.norm(gv, np.inf) if np.abs(Gnorm.imag / Gnorm.real) > 1e-16: diff --git a/sharpy/linear/src/lingebm.py b/sharpy/linear/src/lingebm.py index ea1fc3338..83cfefdc4 100644 --- a/sharpy/linear/src/lingebm.py +++ b/sharpy/linear/src/lingebm.py @@ -1337,7 +1337,7 @@ def update_matrices_time_scale(self, time_ref): def cont2disc(self, dt=None): """Convert continuous-time SS model into """ - assert self.discr_method is not 'newmark', \ + assert self.discr_method != 'newmark', \ 'For Newmark-beta discretisation, use assemble method directly.' if dt is not None: diff --git a/sharpy/rom/utils/librom.py b/sharpy/rom/utils/librom.py index c8e27d1cf..7b80d972f 100644 --- a/sharpy/rom/utils/librom.py +++ b/sharpy/rom/utils/librom.py @@ -1024,7 +1024,7 @@ def modred(SSb, N, method='residualisation'): C11 = SSb.C[:, :N] D = SSb.D - if method is 'truncation': + if method == 'truncation': SSrom = libss.StateSpace(A11, B11, C11, D, dt=SSb.dt) else: Nb = SSb.A.shape[0] diff --git a/sharpy/solvers/dynamiccoupled.py b/sharpy/solvers/dynamiccoupled.py index 5f54c88bd..dda9a1579 100644 --- a/sharpy/solvers/dynamiccoupled.py +++ b/sharpy/solvers/dynamiccoupled.py @@ -319,7 +319,7 @@ def initialise(self, data, custom_settings=None): 'FoR_vel(x)', 'FoR_vel(z)']) # Define the function to correct aerodynamic forces - if self.settings['correct_forces_method'] is not '': + if self.settings['correct_forces_method'] != '': self.correct_forces = True self.correct_forces_generator = gen_interface.generator_from_string(self.settings['correct_forces_method'])() self.correct_forces_generator.initialise(in_dict=self.settings['correct_forces_settings'], diff --git a/sharpy/solvers/staticcoupled.py b/sharpy/solvers/staticcoupled.py index 09149d807..ef4773889 100644 --- a/sharpy/solvers/staticcoupled.py +++ b/sharpy/solvers/staticcoupled.py @@ -128,7 +128,7 @@ def initialise(self, data, input_dict=None): self.residual_table.print_header(['iter', 'step', 'log10(res)', 'Fx', 'Fy', 'Fz', 'Mx', 'My', 'Mz']) # Define the function to correct aerodynamic forces - if self.settings['correct_forces_method'] is not '': + if self.settings['correct_forces_method'] != '': self.correct_forces = True self.correct_forces_generator = gen_interface.generator_from_string(self.settings['correct_forces_method'])() self.correct_forces_generator.initialise(in_dict=self.settings['correct_forces_settings'], From ec83e025caf9d5b3b5226e771d21a1fad59ad7e7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stefanie=20D=C3=BCssler?= Date: Thu, 27 Jul 2023 20:20:09 +0800 Subject: [PATCH 144/232] fix [setting utils] rename correctly - got messed up during merge --- sharpy/solvers/dynamiccoupled.py | 2 +- sharpy/solvers/stepuvlm.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/sharpy/solvers/dynamiccoupled.py b/sharpy/solvers/dynamiccoupled.py index a031d8bfd..1a3f62c42 100644 --- a/sharpy/solvers/dynamiccoupled.py +++ b/sharpy/solvers/dynamiccoupled.py @@ -183,7 +183,7 @@ class DynamicCoupled(BaseSolver): settings_default['nonlifting_body_interactions'] = False settings_description['nonlifting_body_interactions'] = 'Effect of Nonlifting Bodies on Lifting bodies are considered' - settings_table = settings.SettingsTable() + settings_table = settings_utils.SettingsTable() __doc__ += settings_table.generate(settings_types, settings_default, settings_description, settings_options) def __init__(self): diff --git a/sharpy/solvers/stepuvlm.py b/sharpy/solvers/stepuvlm.py index 95ae79fe2..3f56fbafd 100644 --- a/sharpy/solvers/stepuvlm.py +++ b/sharpy/solvers/stepuvlm.py @@ -147,7 +147,7 @@ class StepUvlm(BaseSolver): settings_default['centre_rot_g'] = [0., 0., 0.] settings_description['centre_rot_g'] = 'Centre of rotation in G FoR around which ``rbm_vel_g`` is applied' - settings_table = settings.SettingsTable() + settings_table = settings_utils.SettingsTable() __doc__ += settings_table.generate(settings_types, settings_default, settings_description, settings_options) def __init__(self): From 82d5c1877cc5ec130095e136850e507fe41bed02 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stefanie=20D=C3=BCssler?= Date: Thu, 27 Jul 2023 22:47:26 +0800 Subject: [PATCH 145/232] fix [aerogridloader] add restart option to function input --- sharpy/solvers/aerogridloader.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sharpy/solvers/aerogridloader.py b/sharpy/solvers/aerogridloader.py index eefd996be..84110f475 100644 --- a/sharpy/solvers/aerogridloader.py +++ b/sharpy/solvers/aerogridloader.py @@ -95,7 +95,7 @@ def __init__(self): self.aero = None self.wake_shape_generator = None - def initialise(self, data): + def initialise(self, data, restart=False): super().initialise(data) wake_shape_generator_type = gen_interface.generator_from_string( From 2409a1951b0545776b8d9e652e9bab5e918d1e60 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stefanie=20D=C3=BCssler?= Date: Thu, 27 Jul 2023 22:54:49 +0800 Subject: [PATCH 146/232] fix use updated version of SHARPy main --- sharpy/sharpy_main.py | 33 ++++----------------------------- 1 file changed, 4 insertions(+), 29 deletions(-) diff --git a/sharpy/sharpy_main.py b/sharpy/sharpy_main.py index 6bb4b4f5d..dd9c3d20f 100644 --- a/sharpy/sharpy_main.py +++ b/sharpy/sharpy_main.py @@ -87,32 +87,18 @@ def main(args=None, sharpy_input_dict=None): if args.input_filename == '': parser.error('input_filename is a required argument of SHARPy.') settings = input_arg.read_settings(args) - missing_solvers = False if args.restart is None: # run preSHARPy data = PreSharpy(settings) - solvers = dict() - restart = False else: try: with open(args.restart, 'rb') as restart_file: data = pickle.load(restart_file) - try: - solvers = pickle.load(restart_file) - except EOFError: - # For backwards compatibility - missing_solvers = True - solvers = dict() - cout.cout_wrap('Solvers not found in Pickle file. Using the settings in *.sharpy file.') - if "UpdatePickle" in solvers.keys(): - # For backwards compatibility - missing_solvers = True - solvers = dict() except FileNotFoundError: raise FileNotFoundError('The file specified for the snapshot \ restart (-r) does not exist. Please check.') - restart = True + # update the settings data.update_settings(settings) @@ -125,22 +111,11 @@ def main(args=None, sharpy_input_dict=None): # for it in range(self.num_steps): # data.structure.dynamic_input.append(dict()) - # Restart the solvers - old_solvers_list = list(solvers.keys()) - for old_solver_name in old_solvers_list: - if old_solver_name not in settings['SHARPy']['flow']: - del solvers[old_solver_name] - # Loop for the solvers specified in *.sharpy['SHARPy']['flow'] for solver_name in settings['SHARPy']['flow']: - if (args.restart is None) or (solver_name not in solvers.keys()) or (missing_solvers): - solvers[solver_name] = solver_interface.initialise_solver(solver_name) - if missing_solvers: - solvers[solver_name].initialise(data, restart=False) - else: - solvers[solver_name].initialise(data, restart=restart) - data = solvers[solver_name].run(solvers=solvers) - solvers[solver_name].teardown() + solver = solver_interface.initialise_solver(solver_name) + solver.initialise(data) + data = solver.run() cpu_time = time.process_time() - t wall_time = time.perf_counter() - t0_wall From afdbe1927c8580dba9af7191e3f3d0c3a4df7be9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stefanie=20D=C3=BCssler?= Date: Thu, 27 Jul 2023 22:55:15 +0800 Subject: [PATCH 147/232] fix [aeroforces] correct intend of line --- sharpy/postproc/aeroforcescalculator.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sharpy/postproc/aeroforcescalculator.py b/sharpy/postproc/aeroforcescalculator.py index f07223e39..633ab995f 100644 --- a/sharpy/postproc/aeroforcescalculator.py +++ b/sharpy/postproc/aeroforcescalculator.py @@ -157,7 +157,7 @@ def calculate_forces(self, ts): # Convert to forces in A frame steady_forces_a = self.data.structure.nodal_b_for_2_a_for(steady_forces_b, self.data.structure.timestep_info[ts]) - unsteady_forces_b = self.map_forces_beam_dof(ts, unsteady_force) + unsteady_forces_b = self.map_forces_beam_dof(ts, unsteady_force) steady_forces_a = self.data.structure.timestep_info[ts].nodal_b_for_2_a_for(steady_forces_b, self.data.structure) From de9bba373bd8aac861bb825b7b34186c77a91e5a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stefanie=20D=C3=BCssler?= Date: Thu, 27 Jul 2023 22:58:15 +0800 Subject: [PATCH 148/232] fix rename aero_dict to data_dict --- docs/source/content/example_notebooks/wind_turbine.ipynb | 2 +- sharpy/aero/models/aerogrid.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/source/content/example_notebooks/wind_turbine.ipynb b/docs/source/content/example_notebooks/wind_turbine.ipynb index be366343a..3d1722c5a 100644 --- a/docs/source/content/example_notebooks/wind_turbine.ipynb +++ b/docs/source/content/example_notebooks/wind_turbine.ipynb @@ -1010,7 +1010,7 @@ " inode_in_elem = sharpy_output.structure.node_master_elem[node_global_index, 1]\n", " CAB = algebra.crv2rotation(tstep.psi[ielem, inode_in_elem, :])\n", "\n", - " c[iblade][inode] = sharpy_output.aero.aero_dict['chord'][ielem,inode_in_elem]\n", + " c[iblade][inode] = sharpy_output.aero.data_dict['chord'][ielem,inode_in_elem]\n", "\n", " forces_AFoR = np.dot(CAB, forces[iblade][inode, 0:3])\n", "\n", diff --git a/sharpy/aero/models/aerogrid.py b/sharpy/aero/models/aerogrid.py index 844e09553..8cd84e574 100644 --- a/sharpy/aero/models/aerogrid.py +++ b/sharpy/aero/models/aerogrid.py @@ -161,8 +161,8 @@ def generate_zeta_timestep_info(self, structure_tstep, aero_tstep, beam, setting self.data_dict['sweep'] = np.zeros_like(self.data_dict['twist']) # Define first_twist for backwards compatibility - if 'first_twist' not in self.aero_dict: - self.aero_dict['first_twist'] = [True]*self.aero_dict['surface_m'].shape[0] + if 'first_twist' not in self.data_dict: + self.data_dict['first_twist'] = [True]*self.data_dict['surface_m'].shape[0] # one surface per element for i_elem in range(self.n_elem): From f18906607cd74c827bd590c25e20e13c202930d2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stefanie=20D=C3=BCssler?= Date: Fri, 28 Jul 2023 09:50:43 +0800 Subject: [PATCH 149/232] fix [merge] recover uptodate changes from develop branch --- sharpy/postproc/aeroforcescalculator.py | 4 ---- sharpy/utils/datastructures.py | 14 +++++--------- sharpy/utils/generate_cases.py | 8 +------- sharpy/utils/generator_interface.py | 7 ++----- 4 files changed, 8 insertions(+), 25 deletions(-) diff --git a/sharpy/postproc/aeroforcescalculator.py b/sharpy/postproc/aeroforcescalculator.py index 633ab995f..219b0f783 100644 --- a/sharpy/postproc/aeroforcescalculator.py +++ b/sharpy/postproc/aeroforcescalculator.py @@ -155,10 +155,6 @@ def calculate_forces(self, ts): except KeyError: unsteady_forces_b = self.map_forces_beam_dof(self.data.aero, ts, unsteady_force) # Convert to forces in A frame - steady_forces_a = self.data.structure.nodal_b_for_2_a_for(steady_forces_b, - self.data.structure.timestep_info[ts]) - unsteady_forces_b = self.map_forces_beam_dof(ts, unsteady_force) - steady_forces_a = self.data.structure.timestep_info[ts].nodal_b_for_2_a_for(steady_forces_b, self.data.structure) diff --git a/sharpy/utils/datastructures.py b/sharpy/utils/datastructures.py index 68886c69f..3c83569dd 100644 --- a/sharpy/utils/datastructures.py +++ b/sharpy/utils/datastructures.py @@ -670,15 +670,11 @@ def remove_ctypes_pointers(self): except AttributeError: pass - try: - del self.ct_p_wake_conv_vel - except AttributeError: - pass - - try: - del self.ct_p_flag_zeta_phantom - except AttributeError: - pass + for k in list(self.postproc_cell.keys()): + if 'ct_list' in k: + del self.postproc_cell[k] + elif 'ct_pointer' in k: + del self.postproc_cell[k] def init_matrix_structure(dimensions, with_dim_dimension, added_size=0): diff --git a/sharpy/utils/generate_cases.py b/sharpy/utils/generate_cases.py index 057b25953..bbcd295f3 100644 --- a/sharpy/utils/generate_cases.py +++ b/sharpy/utils/generate_cases.py @@ -1767,16 +1767,10 @@ def set_default_values(self): solver_name = 'SHARPy' else: solver_name = solver - self.solvers[solver_name] = {} + self.solvers[solver_name] = deepcopy(aux_solvers[solver]) if solver in ['GridLoader', 'NonliftingbodygridLoader']: # Skip this solver as no default values for GridLoader exist. continue - try: - aux_solver = solver_interface.solver_from_string(solver) - except: - aux_solver = generator_interface.generator_from_string(solver) - # TODO: remove this try/except when generators are rewriten as solvers with class attributes instead of instance attributes - aux_solver.__init__(aux_solver) def check(self): diff --git a/sharpy/utils/generator_interface.py b/sharpy/utils/generator_interface.py index ff3b827f9..717dae296 100644 --- a/sharpy/utils/generator_interface.py +++ b/sharpy/utils/generator_interface.py @@ -28,10 +28,7 @@ def print_available_generators(): class BaseGenerator(metaclass=ABCMeta): - - def teardown(self): - pass - + pass def generator_from_string(string): return dict_of_generators[string] @@ -41,7 +38,7 @@ def generator_list_from_path(cwd): onlyfiles = [f for f in os.listdir(cwd) if os.path.isfile(os.path.join(cwd, f))] for i_file in range(len(onlyfiles)): - if onlyfiles[i_file].split('.')[-1] == 'py': # support autosaved files in the folder + if ".py" in onlyfiles[i_file]: if onlyfiles[i_file] == "__init__.py": onlyfiles[i_file] = "" continue From c51a4dff1c81e5b261a937591405ea92d2cee8af Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stefanie=20D=C3=BCssler?= Date: Fri, 28 Jul 2023 09:52:44 +0800 Subject: [PATCH 150/232] update [postproc] lift distribution script with all force dimensions - plus folder and option to save every timestep --- sharpy/postproc/liftdistribution.py | 74 +++++++++++------------------ 1 file changed, 29 insertions(+), 45 deletions(-) diff --git a/sharpy/postproc/liftdistribution.py b/sharpy/postproc/liftdistribution.py index c623a4193..a44358a8a 100644 --- a/sharpy/postproc/liftdistribution.py +++ b/sharpy/postproc/liftdistribution.py @@ -2,21 +2,19 @@ import numpy as np -import sharpy.utils.cout_utils as cout -import sharpy.utils.algebra as algebra from sharpy.utils.solver_interface import solver, BaseSolver import sharpy.utils.settings as settings_utils -from sharpy.utils.datastructures import init_matrix_structure, standalone_ctypes_pointer import sharpy.aero.utils.mapping as mapping +import sharpy.utils.algebra as algebra import sharpy.aero.utils.utils as aeroutils @solver class LiftDistribution(BaseSolver): """LiftDistribution - + Calculates and exports the lift distribution on lifting surfaces - + """ solver_id = 'LiftDistribution' solver_classification = 'post-processor' @@ -26,7 +24,7 @@ class LiftDistribution(BaseSolver): settings_description = dict() settings_types['text_file_name'] = 'str' - settings_default['text_file_name'] = 'lift_distribution.txt' + settings_default['text_file_name'] = 'lift_distribution.csv' settings_description['text_file_name'] = 'Text file name' settings_default['coefficients'] = True @@ -46,30 +44,18 @@ def __init__(self): self.folder = None self.caller = None - def initialise(self, data, custom_settings=None, caller=None, restart=False): + def initialise(self, data, caller=None): self.data = data - if custom_settings is None: - self.settings = data.settings[self.solver_id] - else: - self.settings = custom_settings + self.settings = data.settings[self.solver_id] settings_utils.to_custom_types(self.settings, self.settings_types, self.settings_default) - self.ts_max = len(self.data.structure.timestep_info) self.caller = caller - self.folder = data.output_folder + self.folder = data.output_folder + '/liftdistribution/' if not os.path.exists(self.folder): os.makedirs(self.folder) - def run(self, **kwargs): - - online = settings_utils.set_value_or_default(kwargs, 'online', False) - - if not online: - for self.ts in range(self.ts_max): - self.lift_distribution() - cout.cout_wrap('...Finished', 1) - else: - self.ts = len(self.data.structure.timestep_info) - 1 - self.lift_distribution() + def run(self, online=False): + self.lift_distribution(self.data.structure.timestep_info[self.data.ts], + self.data.aero.timestep_info[self.data.ts]) return self.data def lift_distribution(self, struct_tstep, aero_tstep): @@ -83,27 +69,26 @@ def lift_distribution(self, struct_tstep, aero_tstep): self.data.structure.node_master_elem, self.data.structure.connectivities, struct_tstep.cag(), - self.data.aero.data_dict) - # Prepare output matrix and file + self.data.aero.aero_dict) + # Prepare output matrix and file N_nodes = self.data.structure.num_node - numb_col = 4 - header = "x,y,z,fz" + numb_col = 6 + header = "x,y,z,fx,fy,fz" # get aero forces - lift_distribution = np.zeros((N_nodes, numb_col)) # get rotation matrix cga = algebra.quat2rotation(struct_tstep.quat) if self.settings["coefficients"]: # TODO: add nondimensional spanwise column y/s - header += ", y/s, cl" - numb_col += 2 - lift_distribution = np.concatenate((lift_distribution, np.zeros((N_nodes, 2))), axis=1) + header += ", cfx, cfy, cfz" + numb_col += 3 + lift_distribution = np.zeros((N_nodes, numb_col)) for inode in range(N_nodes): - if self.data.aero.data_dict['aero_node'][inode]: + if self.data.aero.aero_dict['aero_node'][inode]: local_node = self.data.aero.struct2aero_mapping[inode][0]["i_n"] ielem, inode_in_elem = self.data.structure.node_master_elem[inode] i_surf = int(self.data.aero.surface_distribution[ielem]) - # get c_gb + # get c_gb cab = algebra.crv2rotation(struct_tstep.psi[ielem, inode_in_elem, :]) cgb = np.dot(cga, cab) # Get c_bs @@ -116,23 +101,22 @@ def lift_distribution(self, struct_tstep, aero_tstep): dir_span, span, dir_chord, chord = aeroutils.span_chord(local_node, aero_tstep.zeta[i_surf]) # Stability axes - projects forces in B onto S c_bs = aeroutils.local_stability_axes(cgb.T.dot(dir_urel), cgb.T.dot(dir_chord)) - lift_force = c_bs.T.dot(forces[inode, :3])[2] + aero_forces = c_bs.T.dot(forces[inode, :3]) # Store data in export matrix - lift_distribution[inode, 3] = lift_force + lift_distribution[inode, 3:6] = aero_forces lift_distribution[inode, 2] = struct_tstep.pos[inode, 2] # z lift_distribution[inode, 1] = struct_tstep.pos[inode, 1] # y lift_distribution[inode, 0] = struct_tstep.pos[inode, 0] # x if self.settings["coefficients"]: - # Get non-dimensional spanwise coordinate y/s - lift_distribution[inode, 4] = lift_distribution[inode, 1]/span # Get lift coefficient - lift_distribution[inode, 5] = np.sign(lift_force) * np.linalg.norm(lift_force) \ - / (0.5 * self.settings['rho'] \ - * np.linalg.norm(urel) ** 2 * span * chord) # strip_area[i_surf][local_node]) - # Check if shared nodes from different surfaces exist (e.g. two wings joining at symmetry plane) - # Leads to error since panel area just donates for half the panel size while lift forces is summed up - lift_distribution[inode, 5] /= len(self.data.aero.struct2aero_mapping[inode]) + for idim in range(3): + lift_distribution[inode, 6+idim] = np.sign(aero_forces[idim]) * np.linalg.norm(aero_forces[idim]) \ + / (0.5 * self.settings['rho'] \ + * np.linalg.norm(urel) ** 2 * span * chord) + # Check if shared nodes from different surfaces exist (e.g. two wings joining at symmetry plane) + # Leads to error since panel area just donates for half the panel size while lift forces is summed up + lift_distribution[inode, 6+idim] /= len(self.data.aero.struct2aero_mapping[inode]) # Export lift distribution data - np.savetxt(os.path.join(self.folder, self.settings['text_file_name']), lift_distribution, + np.savetxt(os.path.join(self.folder, 'ts_' + str(self.data.ts) + self.settings['text_file_name']), lift_distribution, fmt='%10e,' * (numb_col - 1) + '%10e', delimiter=", ", header=header) From 28567b6ea309827c3c9d133c789898351b1741e3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stefanie=20D=C3=BCssler?= Date: Fri, 28 Jul 2023 10:13:18 +0800 Subject: [PATCH 151/232] fix [unittest] library imports for source panel method tests --- tests/sourcepanelmethod/test_source_panel_method.py | 3 ++- tests/sourcepanelmethod/test_vlm_coupled_spm.py | 5 +++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/tests/sourcepanelmethod/test_source_panel_method.py b/tests/sourcepanelmethod/test_source_panel_method.py index 51257df55..11e54927d 100644 --- a/tests/sourcepanelmethod/test_source_panel_method.py +++ b/tests/sourcepanelmethod/test_source_panel_method.py @@ -1,7 +1,8 @@ import unittest import os import numpy as np -from fuselage_wing_configuration.fuselage_wing_configuration import Fuselage_Wing_Configuration +from sharpy.cases.templates.fuselage_wing_configuration.fuselage_wing_configuration import Fuselage_Wing_Configuration + from define_simulation_settings import define_simulation_settings diff --git a/tests/sourcepanelmethod/test_vlm_coupled_spm.py b/tests/sourcepanelmethod/test_vlm_coupled_spm.py index cc37e7711..0f080be45 100644 --- a/tests/sourcepanelmethod/test_vlm_coupled_spm.py +++ b/tests/sourcepanelmethod/test_vlm_coupled_spm.py @@ -2,14 +2,15 @@ import os import numpy as np from sharpy.cases.templates.fuselage_wing_configuration.fuselage_wing_configuration import Fuselage_Wing_Configuration -from define_simulation_settings import define_simulation_settings +from .define_simulation_settings import define_simulation_settings import json -class TestVlmCoupledWithSourcePanelMethod(unittest.TestCase): +class TestUvlmCoupledWithSourcePanelMethod(unittest.TestCase): def test_phantom_panels(self): """ + Phantom Panel Test (Steady and Dynamic) The lift distribution over a rectangular high-aspect ratio wing is computed. First a wing_only configuration is considered, while second, we activate the phantom panels created within the From a4005f9730e5c80a8edb6fa89370f14cfc30ab6f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stefanie=20D=C3=BCssler?= Date: Fri, 28 Jul 2023 16:30:49 +0800 Subject: [PATCH 152/232] refactor [datastructures] remove all cpointers by using getattributes --- sharpy/utils/datastructures.py | 102 ++------------------------------- 1 file changed, 5 insertions(+), 97 deletions(-) diff --git a/sharpy/utils/datastructures.py b/sharpy/utils/datastructures.py index 3c83569dd..29222ad3d 100644 --- a/sharpy/utils/datastructures.py +++ b/sharpy/utils/datastructures.py @@ -232,42 +232,13 @@ def generate_ctypes_pointers(self): def remove_ctypes_pointers(self): """ - Removes the pointers to aerodynamic variables used to interface the C++ library ``uvlmlib`` + Removes the pointers to aerodynamic variables used to interface the C++ library ``uvlmlib`` """ - try: - del self.ct_p_dimensions - except AttributeError: - pass - - try: - del self.ct_p_zeta - except AttributeError: - pass - try: - del self.ct_p_zeta_dot - except AttributeError: - pass - - try: - del self.ct_p_u_ext - except AttributeError: - pass - - try: - del self.ct_p_normals - except AttributeError: - pass - - try: - del self.ct_p_forces - except AttributeError: - pass - - try: - del self.ct_p_dynamic_forces - except AttributeError: - pass + list_class_attributes = list(self.__dict__.keys()).copy() + for name_attribute in list_class_attributes: + if "ct_p_" in name_attribute: + self.__delattr__(name_attribute) for k in list(self.postproc_cell.keys()): if 'ct_list' in k: @@ -398,26 +369,6 @@ def generate_ctypes_pointers(self): self.ct_p_pressure_coefficients = ((ct.POINTER(ct.c_double)*len(self.ct_pressure_coefficients_list)) (* [np.ctypeslib.as_ctypes(array) for array in self.ct_pressure_coefficients_list])) - def remove_ctypes_pointers(self): - """ - Removes the pointers to aerodynamic variables used to interface the C++ library ``uvlmlib`` - """ - super().remove_ctypes_pointers() - try: - del self.ct_p_sigma - except AttributeError: - pass - - try: - del self.ct_p_sigma_dot - except AttributeError: - pass - - try: - del self.ct_p_pressure_coefficients - except AttributeError: - pass - class AeroTimeStepInfo(TimeStepInfo): @@ -633,49 +584,6 @@ def generate_ctypes_pointers(self): self.ct_p_flag_zeta_phantom = np.ctypeslib.as_ctypes(self.ct_flag_zeta_phantom_list) - def remove_ctypes_pointers(self): - super().remove_ctypes_pointers() - try: - del self.ct_p_dimensions_star - except AttributeError: - pass - - try: - del self.ct_p_zeta_star - except AttributeError: - pass - - try: - del self.ct_p_u_ext_star - except AttributeError: - pass - - try: - del self.ct_p_gamma - except AttributeError: - pass - - try: - del self.ct_p_gamma_dot - except AttributeError: - pass - - try: - del self.ct_p_gamma_star - except AttributeError: - pass - - try: - del self.ct_p_dist_to_orig - except AttributeError: - pass - - for k in list(self.postproc_cell.keys()): - if 'ct_list' in k: - del self.postproc_cell[k] - elif 'ct_pointer' in k: - del self.postproc_cell[k] - def init_matrix_structure(dimensions, with_dim_dimension, added_size=0): matrix = [] From 6d9730d343226327e339507f94e6a66a8bdde875 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stefanie=20D=C3=BCssler?= Date: Fri, 11 Aug 2023 07:44:10 +0200 Subject: [PATCH 153/232] remove [uvlmlib] unused options --- lib/UVLM | 2 +- sharpy/aero/utils/uvlmlib.py | 48 +++--------------------------------- 2 files changed, 4 insertions(+), 46 deletions(-) diff --git a/lib/UVLM b/lib/UVLM index 5a9a84861..d5b2adb0c 160000 --- a/lib/UVLM +++ b/lib/UVLM @@ -1 +1 @@ -Subproject commit 5a9a84861fc4cd72d195967db55f68e8ea384949 +Subproject commit d5b2adb0c9205444d159fda2f2a232603315dc7b diff --git a/sharpy/aero/utils/uvlmlib.py b/sharpy/aero/utils/uvlmlib.py index c8029e978..448735adc 100644 --- a/sharpy/aero/utils/uvlmlib.py +++ b/sharpy/aero/utils/uvlmlib.py @@ -34,11 +34,8 @@ class VMopts(ct.Structure): unsigned int NumCores; unsigned int NumSurfaces; unsigned int NumSurfacesNonlifting; - bool cfl1; double vortex_radius; double vortex_radius_wake_ind; - double* centre_rot_g[3]; - double* rbm_vel_g[6]; }; """ _fields_ = [("ImageMethod", ct.c_bool), @@ -61,11 +58,8 @@ class VMopts(ct.Structure): ("iterative_solver", ct.c_bool), ("iterative_tol", ct.c_double), ("iterative_precond", ct.c_bool), - ("cfl1", ct.c_bool), ("vortex_radius", ct.c_double), - ("vortex_radius_wake_ind", ct.c_double), - ("centre_rot_g", ct.c_double * 3), - ("rbm_vel_g", ct.c_double * 6)] + ("vortex_radius_wake_ind", ct.c_double)] @@ -90,15 +84,12 @@ def __init__(self): self.iterative_solver = ct.c_bool(False) self.iterative_tol = ct.c_double(0) self.iterative_precond = ct.c_bool(False) - self.cfl1 = ct.c_bool(True) self.vortex_radius = ct.c_double(vortex_radius_def) self.vortex_radius_wake_ind = ct.c_double(vortex_radius_def) - self.centre_rot_g = np.ctypeslib.as_ctypes(np.zeros((3))) - self.rbm_vel_g = np.ctypeslib.as_ctypes(np.zeros((6))) self.phantom_wing_test = ct.c_bool(False) - def set_options(self, options, n_surfaces = 0, n_surfaces_nonlifting = 0, rbm_vel_g = np.zeros(6)): + def set_options(self, options, n_surfaces = 0, n_surfaces_nonlifting = 0): self.Steady = ct.c_bool(True) self.NumSurfaces = ct.c_uint(n_surfaces) self.NumSurfacesNonlifting = ct.c_uint(n_surfaces_nonlifting) @@ -111,7 +102,6 @@ def set_options(self, options, n_surfaces = 0, n_surfaces_nonlifting = 0, rbm_ve self.iterative_solver = ct.c_bool(options['iterative_solver']) self.iterative_tol = ct.c_double(options['iterative_tol']) self.iterative_precond = ct.c_bool(options['iterative_precond']) - self.cfl1 = ct.c_bool(options['cfl1']) self.vortex_radius = ct.c_double(options['vortex_radius']) self.vortex_radius_wake_ind = ct.c_double(options['vortex_radius_wake_ind']) @@ -119,10 +109,6 @@ def set_options(self, options, n_surfaces = 0, n_surfaces_nonlifting = 0, rbm_ve self.only_lifting = ct.c_bool(not options["nonlifting_body_interactions"]) self.phantom_wing_test = ct.c_bool(options["phantom_wing_test"]) - for i in range(len(options["centre_rot_g"])): - self.centre_rot_g[i] = ct.c_double(options["centre_rot_g"][i]) - for i in range(len(rbm_vel_g)): - self.rbm_vel_g[i] = ct.c_double(rbm_vel_g[i]) class UVMopts(ct.Structure): # TODO: add set_options function @@ -133,12 +119,8 @@ class UVMopts(ct.Structure): ("NumSurfacesNonlifting", ct.c_uint), ("only_lifting", ct.c_bool), ("only_nonlifting", ct.c_bool), - ("phantom_wing_test", ct.c_bool), - # ("steady_n_rollup", ct.c_uint), - # ("steady_rollup_tolerance", ct.c_double), - # ("steady_rollup_aic_refresh", ct.c_uint), + ("phantom_wing_test", ct.c_bool), ("convection_scheme", ct.c_uint), - # ("Mstar", ct.c_uint), ("ImageMethod", ct.c_bool), ("iterative_solver", ct.c_bool), ("iterative_tol", ct.c_double), @@ -152,8 +134,6 @@ class UVMopts(ct.Structure): ("interp_method", ct.c_uint), ("yaw_slerp", ct.c_double), ("quasi_steady", ct.c_bool), - ("centre_rot_g", ct.c_double * 3), - ("rbm_vel_g", ct.c_double * 6), ("num_spanwise_panels_wo_induced_velocity", ct.c_uint)] def __init__(self): @@ -163,7 +143,6 @@ def __init__(self): self.NumSurfaces = ct.c_uint(1) self.NumSurfacesNonlifting = ct.c_uint(1) self.convection_scheme = ct.c_uint(2) - # self.Mstar = ct.c_uint(10) self.ImageMethod = ct.c_bool(False) self.iterative_solver = ct.c_bool(False) self.iterative_tol = ct.c_double(0) @@ -174,8 +153,6 @@ def __init__(self): self.vortex_radius_wake_ind = ct.c_double(vortex_radius_def) self.yaw_slerp = ct.c_double(0.) self.quasi_steady = ct.c_bool(False) - self.centre_rot_g = np.ctypeslib.as_ctypes(np.zeros((3))) - self.rbm_vel_g = np.ctypeslib.as_ctypes(np.zeros((6))) self.num_spanwise_panels_wo_induced_velocity = ct.c_uint(0) self.phantom_wing_test = ct.c_bool(False) @@ -185,8 +162,6 @@ def set_options(self, n_surfaces_nonlifting = 0, dt = None, convect_wake = False, - rbm_vel_g = np.zeros(6), - image_method = False, n_span_panels_wo_u_ind = 0): if dt is None: self.dt = ct.c_double(options["dt"]) @@ -215,10 +190,6 @@ def set_options(self, self.phantom_wing_test = ct.c_bool(options["phantom_wing_test"]) self.num_spanwise_panels_wo_induced_velocity = n_span_panels_wo_u_ind - for i in range(len(options["centre_rot_g"])): - self.centre_rot_g[i] = ct.c_double(options["centre_rot_g"][i]) - for i in range(len(rbm_vel_g)): - self.rbm_vel_g[i] = ct.c_double(rbm_vel_g[i]) class FlightConditions(ct.Structure): _fields_ = [("uinf", ct.c_double), @@ -229,17 +200,6 @@ class FlightConditions(ct.Structure): def __init__(self): ct.Structure.__init__(self) - # def __init__(self, fc_dict): - # ct.Structure.__init__(self) - # self.uinf = fc_dict['FlightCon']['u_inf'] - # alpha = fc_dict['FlightCon']['alpha'] - # beta = fc_dict['FlightCon']['beta'] - # uinf_direction_temp = np.array([1, 0, 0], dtype=ct.c_double) - # self.uinf_direction = np.ctypeslib.as_ctypes(uinf_direction_temp) - # self.rho = fc_dict['FlightCon']['rho_inf'] - # self.c_ref = fc_dict['FlightCon']['c_ref'] - - # type for 2d integer matrix t_2int = ct.POINTER(ct.c_int)*2 @@ -383,14 +343,12 @@ def uvlm_solver(i_iter, ts_info, struct_ts_info, options, convect_wake=True, dt= ts_info.ct_p_gamma, ts_info.ct_p_gamma_star, ts_info.ct_p_dist_to_orig, - # previous_ts_info.ct_p_gamma, ts_info.ct_p_normals, ts_info.ct_p_forces, ts_info.ct_p_dynamic_forces, p_rbm_vel, p_centre_rot) ts_info.remove_ctypes_pointers() - # previous_ts_info.remove_ctypes_pointers() def uvlm_solver_lifting_and_nonlifting(i_iter, ts_info, ts_info_nonlifting, struct_ts_info, options, convect_wake=True, dt=None): rbm_vel = struct_ts_info.for_vel.copy() From 58afc1af0532db2c76037e93e186e5f9ac95f1ff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stefanie=20D=C3=BCssler?= Date: Fri, 11 Aug 2023 07:47:09 +0200 Subject: [PATCH 154/232] fix [postproc] lift distribution - rename aero_dict to data_dict - adjust filename --- sharpy/postproc/liftdistribution.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/sharpy/postproc/liftdistribution.py b/sharpy/postproc/liftdistribution.py index a44358a8a..dadda480c 100644 --- a/sharpy/postproc/liftdistribution.py +++ b/sharpy/postproc/liftdistribution.py @@ -24,7 +24,7 @@ class LiftDistribution(BaseSolver): settings_description = dict() settings_types['text_file_name'] = 'str' - settings_default['text_file_name'] = 'lift_distribution.csv' + settings_default['text_file_name'] = 'liftdistribution' settings_description['text_file_name'] = 'Text file name' settings_default['coefficients'] = True @@ -69,7 +69,7 @@ def lift_distribution(self, struct_tstep, aero_tstep): self.data.structure.node_master_elem, self.data.structure.connectivities, struct_tstep.cag(), - self.data.aero.aero_dict) + self.data.aero.data_dict) # Prepare output matrix and file N_nodes = self.data.structure.num_node numb_col = 6 @@ -84,7 +84,7 @@ def lift_distribution(self, struct_tstep, aero_tstep): lift_distribution = np.zeros((N_nodes, numb_col)) for inode in range(N_nodes): - if self.data.aero.aero_dict['aero_node'][inode]: + if self.data.aero.data_dict['aero_node'][inode]: local_node = self.data.aero.struct2aero_mapping[inode][0]["i_n"] ielem, inode_in_elem = self.data.structure.node_master_elem[inode] i_surf = int(self.data.aero.surface_distribution[ielem]) @@ -118,5 +118,5 @@ def lift_distribution(self, struct_tstep, aero_tstep): lift_distribution[inode, 6+idim] /= len(self.data.aero.struct2aero_mapping[inode]) # Export lift distribution data - np.savetxt(os.path.join(self.folder, 'ts_' + str(self.data.ts) + self.settings['text_file_name']), lift_distribution, + np.savetxt(os.path.join(self.folder, self.settings['text_file_name'] + '_ts{}'.format(str(self.data.ts)) + '.txt'), lift_distribution, fmt='%10e,' * (numb_col - 1) + '%10e', delimiter=", ", header=header) From 1bfdd0a62c01e815e3a4b458b6e58fdcf1ca0c40 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stefanie=20D=C3=BCssler?= Date: Fri, 11 Aug 2023 07:48:19 +0200 Subject: [PATCH 155/232] remove unnecessary print and comment statements - basically debugging left overs --- sharpy/postproc/aerogridplot.py | 1 - sharpy/solvers/nonlineardynamiccoupledstep.py | 3 +-- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/sharpy/postproc/aerogridplot.py b/sharpy/postproc/aerogridplot.py index 43e076fbf..969289d54 100644 --- a/sharpy/postproc/aerogridplot.py +++ b/sharpy/postproc/aerogridplot.py @@ -140,7 +140,6 @@ def run(self, **kwargs): self.plot_body() self.plot_wake() if self.settings['plot_nonlifting_surfaces']: - print("Plot Nonlifting Surface") self.plot_nonlifting_surfaces() cout.cout_wrap('...Finished', 1) elif (self.data.ts % self.settings['stride'] == 0): diff --git a/sharpy/solvers/nonlineardynamiccoupledstep.py b/sharpy/solvers/nonlineardynamiccoupledstep.py index 621686ba5..c6a665b83 100644 --- a/sharpy/solvers/nonlineardynamiccoupledstep.py +++ b/sharpy/solvers/nonlineardynamiccoupledstep.py @@ -77,8 +77,7 @@ def initialise(self, data, custom_settings=None, restart=False): def run(self, **kwargs): structural_step = settings_utils.set_value_or_default(kwargs, 'structural_step', self.data.structure.timestep_info[-1]) - # TODO: previous_structural_step never used - previous_structural_step = settings_utils.set_value_or_default(kwargs, 'previous_structural_step', self.data.structure.timestep_info[-1]) + dt= settings_utils.set_value_or_default(kwargs, 'dt', self.settings['dt']) xbeamlib.xbeam_step_couplednlndyn(self.data.structure, From dabc03292b1aacba39d0601bd0d7996a05a0c761 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stefanie=20D=C3=BCssler?= Date: Fri, 11 Aug 2023 07:52:19 +0200 Subject: [PATCH 156/232] refactor [staticcoupled] create function to remove old timestep --- sharpy/solvers/staticcoupled.py | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/sharpy/solvers/staticcoupled.py b/sharpy/solvers/staticcoupled.py index fc4b9e163..177ab284e 100644 --- a/sharpy/solvers/staticcoupled.py +++ b/sharpy/solvers/staticcoupled.py @@ -154,17 +154,20 @@ def increase_ts(self): def cleanup_timestep_info(self): if max(len(self.data.aero.timestep_info), len(self.data.structure.timestep_info)) > 1: - # copy last info to first - self.data.aero.timestep_info[0] = self.data.aero.timestep_info[-1].copy() - self.data.structure.timestep_info[0] = self.data.structure.timestep_info[-1].copy() - # delete all the rest - while len(self.data.aero.timestep_info) - 1: - del self.data.aero.timestep_info[-1] - while len(self.data.structure.timestep_info) - 1: - del self.data.structure.timestep_info[-1] + self.remove_old_timestep_info(self.data.structure.timestep_info) + self.remove_old_timestep_info(self.data.aero.timestep_info) + if self.settings['nonlifting_body_interactions']: + self.remove_old_timestep_info(self.data.nonlifting_body.timestep_info) self.data.ts = 0 + def remove_old_timestep_info(self, tstep_info): + # copy last info to first + tstep_info[0] = tstep_info[-1].copy() + # delete all the rest + while len(tstep_info) - 1: + del tstep_info[-1] + def run(self, **kwargs): for i_step in range(self.settings['n_load_steps'] + 1): if (i_step == self.settings['n_load_steps'] and @@ -185,7 +188,6 @@ def run(self, **kwargs): self.data = self.aero_solver.run() # map force - # TODO: case if nonlifting body only (coupling solvers do not make sense anyway for this case) struct_forces = mapping.aero2struct_force_mapping( self.data.aero.timestep_info[self.data.ts].forces, self.data.aero.struct2aero_mapping, From e37eb80553d7ab04e6729eaba2ce234c1c3286b9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stefanie=20D=C3=BCssler?= Date: Fri, 11 Aug 2023 08:50:39 +0200 Subject: [PATCH 157/232] refactor [dynamiccoupled] function to delete timestep --- sharpy/solvers/dynamiccoupled.py | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/sharpy/solvers/dynamiccoupled.py b/sharpy/solvers/dynamiccoupled.py index 1a3f62c42..9ea1e56d9 100644 --- a/sharpy/solvers/dynamiccoupled.py +++ b/sharpy/solvers/dynamiccoupled.py @@ -370,17 +370,20 @@ def initialise(self, data, custom_settings=None, restart=False): def cleanup_timestep_info(self): if max(len(self.data.aero.timestep_info), len(self.data.structure.timestep_info)) > 1: - # copy last info to first - self.data.aero.timestep_info[0] = self.data.aero.timestep_info[-1] - self.data.structure.timestep_info[0] = self.data.structure.timestep_info[-1] - # delete all the rest - while len(self.data.aero.timestep_info) - 1: - del self.data.aero.timestep_info[-1] - while len(self.data.structure.timestep_info) - 1: - del self.data.structure.timestep_info[-1] + self.remove_old_timestep_info(self.data.structure.timestep_info) + self.remove_old_timestep_info(self.data.aero.timestep_info) + if self.settings['nonlifting_body_interactions']: + self.remove_old_timestep_info(self.data.nonlifting_body.timestep_info) self.data.ts = 0 + def remove_old_timestep_info(self, tstep_info): + # copy last info to first + tstep_info[0] = tstep_info[-1].copy() + # delete all the rest + while len(tstep_info) - 1: + del tstep_info[-1] + def process_controller_output(self, controlled_state): """ This function modified the solver properties and parameters as From e0d43bc6e546d279c30a1f5d0ff4e306f9e01951 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stefanie=20D=C3=BCssler?= Date: Fri, 11 Aug 2023 08:50:59 +0200 Subject: [PATCH 158/232] fix [uvlmlib] remove function inputs unused inputs --- sharpy/aero/utils/uvlmlib.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/sharpy/aero/utils/uvlmlib.py b/sharpy/aero/utils/uvlmlib.py index 448735adc..9b2ca9dac 100644 --- a/sharpy/aero/utils/uvlmlib.py +++ b/sharpy/aero/utils/uvlmlib.py @@ -314,8 +314,6 @@ def uvlm_solver(i_iter, ts_info, struct_ts_info, options, convect_wake=True, dt= n_surfaces_nonlifting = 0, dt = dt, convect_wake = convect_wake, - rbm_vel_g = rbm_vel, - image_method = False, n_span_panels_wo_u_ind=0) @@ -363,8 +361,6 @@ def uvlm_solver_lifting_and_nonlifting(i_iter, ts_info, ts_info_nonlifting, stru n_surfaces_nonlifting = ts_info_nonlifting.n_surf, dt = dt, convect_wake = convect_wake, - rbm_vel_g = rbm_vel, - image_method = False, n_span_panels_wo_u_ind=4) uvmopts.only_lifting = ct.c_bool(False) run_UVLM = UvlmLib.run_UVLM_lifting_and_nonlifting From 03e6f4be39d90a11c404cef5e22dde1316ceb811 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stefanie=20D=C3=BCssler?= Date: Fri, 11 Aug 2023 08:54:35 +0200 Subject: [PATCH 159/232] fix [solvers] merge issues and minor adjustments --- sharpy/solvers/dynamiccoupled.py | 23 +++-------------- sharpy/solvers/staticcoupled.py | 2 +- sharpy/solvers/staticuvlm.py | 43 +++++++++++++++++++++++++++++--- sharpy/solvers/stepuvlm.py | 17 ++++--------- 4 files changed, 49 insertions(+), 36 deletions(-) diff --git a/sharpy/solvers/dynamiccoupled.py b/sharpy/solvers/dynamiccoupled.py index 9ea1e56d9..466916d59 100644 --- a/sharpy/solvers/dynamiccoupled.py +++ b/sharpy/solvers/dynamiccoupled.py @@ -178,7 +178,6 @@ class DynamicCoupled(BaseSolver): 'The dictionary values are dictionaries with the settings ' \ 'needed by each generator.' - settings_types['nonlifting_body_interactions'] = 'bool' settings_default['nonlifting_body_interactions'] = False settings_description['nonlifting_body_interactions'] = 'Effect of Nonlifting Bodies on Lifting bodies are considered' @@ -681,8 +680,8 @@ def time_loop(self, in_queue=None, out_queue=None, finish_event=None, solvers=No coeff=coeff) self.data = self.structural_solver.run( - structural_step=structural_kstep) #, - # dt=self.substep_dt) + structural_step=structural_kstep, + dt=self.substep_dt) self.time_struc += time.perf_counter() - ini_time_struc @@ -782,8 +781,8 @@ def convergence(self, k, tstep, previous_tstep, return False # Check the special case of no aero and no runtime generators - if (aero_solver.lower() == "noaero"\ - or struct_solver.lower() == "nostructural")\ + if (aero_solver.solver_id.lower() == "noaero"\ + or struct_solver.solver_id.lower() == "nostructural")\ and not with_runtime_generators: return True @@ -861,7 +860,6 @@ def map_forces(self, aero_kstep, structural_kstep, nl_body_kstep = None, unstead structural_kstep.postproc_node['aero_steady_forces'] = struct_forces structural_kstep.postproc_node['aero_unsteady_forces'] = dynamic_struct_forces -<<<<<<< # if self.settings['nonlifting_body_interactions']: # struct_forces += mapping.aero2struct_force_mapping( # nl_body_kstep.forces, @@ -874,23 +872,10 @@ def map_forces(self, aero_kstep, structural_kstep, nl_body_kstep = None, unstead # structural_kstep.cag(), # self.data.nonlifting_body.data_dict) # prescribed forces + aero forces - structural_kstep.steady_applied_forces = ( - (struct_forces + self.data.structure.ini_info.steady_applied_forces). - astype(dtype=ct.c_double, order='F', copy=True)) - try: - structural_kstep.unsteady_applied_forces = ( - (dynamic_struct_forces + self.data.structure.dynamic_input[max(self.data.ts - 1, 0)]['dynamic_forces'] + - structural_kstep.runtime_generated_forces). - astype(dtype=ct.c_double, order='F', copy=True)) - except KeyError: - structural_kstep.unsteady_applied_forces = (dynamic_struct_forces + - structural_kstep.runtime_generated_forces) -======= # prescribed forces + aero forces + runtime generated structural_kstep.steady_applied_forces += struct_forces structural_kstep.steady_applied_forces += self.data.structure.ini_info.steady_applied_forces structural_kstep.steady_applied_forces += structural_kstep.runtime_steady_forces ->>>>>>> structural_kstep.unsteady_applied_forces += dynamic_struct_forces if len(self.data.structure.dynamic_input) > 0: diff --git a/sharpy/solvers/staticcoupled.py b/sharpy/solvers/staticcoupled.py index 177ab284e..e3fde1bae 100644 --- a/sharpy/solvers/staticcoupled.py +++ b/sharpy/solvers/staticcoupled.py @@ -59,7 +59,6 @@ class StaticCoupled(BaseSolver): settings_default['relaxation_factor'] = 0. settings_description['relaxation_factor'] = 'Relaxation parameter in the FSI iteration. 0 is no relaxation and -> 1 is very relaxed' - settings_types['correct_forces_method'] = 'str' settings_default['correct_forces_method'] = '' settings_description['correct_forces_method'] = 'Function used to correct aerodynamic forces. ' \ @@ -206,6 +205,7 @@ def run(self, **kwargs): struct_forces=struct_forces, ts=0) + # map nonlifting forces to structural nodes if self.settings['nonlifting_body_interactions']: struct_forces += mapping.aero2struct_force_mapping( self.data.nonlifting_body.timestep_info[self.data.ts].forces, diff --git a/sharpy/solvers/staticuvlm.py b/sharpy/solvers/staticuvlm.py index df0d19faa..b76085add 100644 --- a/sharpy/solvers/staticuvlm.py +++ b/sharpy/solvers/staticuvlm.py @@ -148,23 +148,58 @@ def initialise(self, data, custom_settings=None, restart=False): self.velocity_generator = velocity_generator_type() self.velocity_generator.initialise(self.settings['velocity_field_input'], restart=restart) - def run(self): + def add_step(self): + self.data.aero.add_timestep() + if self.settings['nonlifting_body_interactions']: + self.data.nonlifting_body.add_timestep() + + + def update_grid(self, beam): + + if not self.settings['only_nonlifting']: + self.data.aero.generate_zeta(beam, + self.data.aero.aero_settings, + -1, + beam_ts=-1) + if self.settings['nonlifting_body_interactions'] or self.settings['only_nonlifting']: + self.data.nonlifting_body.generate_zeta(beam, + self.data.nonlifting_body.aero_settings, + -1, + beam_ts=-1) + + def update_custom_grid(self, structure_tstep, aero_tstep, nonlifting_tstep=None): + self.data.aero.generate_zeta_timestep_info(structure_tstep, + aero_tstep, + self.data.structure, + self.data.aero.aero_settings, + dt=self.settings['rollup_dt']) + if self.settings['nonlifting_body_interactions']: + self.data.nonlifting_body.generate_zeta_timestep_info(structure_tstep, + nonlifting_tstep, + self.data.structure, + self.data.nonlifting_body.aero_settings) + + def run(self, **kwargs): + + structure_tstep = settings_utils.set_value_or_default(kwargs, 'structural_step', self.data.structure.timestep_info[self.data.ts]) + if not self.settings['only_nonlifting']: + aero_tstep = settings_utils.set_value_or_default(kwargs, 'aero_step', self.data.aero.timestep_info[self.data.ts]) if not self.data.aero.timestep_info[self.data.ts].zeta: return self.data # generate the wake because the solid shape might change - aero_tstep = self.data.aero.timestep_info[self.data.ts] self.data.aero.wake_shape_generator.generate({'zeta': aero_tstep.zeta, 'zeta_star': aero_tstep.zeta_star, 'gamma': aero_tstep.gamma, 'gamma_star': aero_tstep.gamma_star, 'dist_to_orig': aero_tstep.dist_to_orig}) + if self.settings['nonlifting_body_interactions']: # generate uext self.velocity_generator.generate({'zeta': self.data.nonlifting_body.timestep_info[self.data.ts].zeta, 'override': True, - 'for_pos': self.data.structure.timestep_info[self.data.ts].for_pos[0:3]}, + 'for_pos': structure_tstep.for_pos[0:3]}, self.data.nonlifting_body.timestep_info[self.data.ts].u_ext) # generate uext self.velocity_generator.generate({'zeta': self.data.aero.timestep_info[self.data.ts].zeta, @@ -192,7 +227,7 @@ def run(self): 'for_pos': self.data.structure.timestep_info[self.data.ts].for_pos[0:3]}, self.data.nonlifting_body.timestep_info[self.data.ts].u_ext) uvlmlib.vlm_solver_nonlifting_body(self.data.nonlifting_body.timestep_info[self.data.ts], - self.settings) + self.settings) return self.data diff --git a/sharpy/solvers/stepuvlm.py b/sharpy/solvers/stepuvlm.py index 3f56fbafd..84e814cce 100644 --- a/sharpy/solvers/stepuvlm.py +++ b/sharpy/solvers/stepuvlm.py @@ -198,14 +198,7 @@ def initialise(self, data, custom_settings=None, restart=False): self.settings['velocity_field_input'], restart=restart) - def run(self, - aero_tstep=None, - structure_tstep=None, - convect_wake=True, - dt=None, - t=None, - unsteady_contribution=False, - nl_body_tstep = None): + def run(self, **kwargs): """ Runs a step of the aerodynamics as implemented in UVLM. """ @@ -264,9 +257,9 @@ def run(self, uvlmlib.uvlm_solver(self.data.ts, aero_tstep, structure_tstep, - self.settings, - convect_wake=convect_wake, - dt=dt) + self.settings, + convect_wake=convect_wake, + dt=dt) if unsteady_contribution and not self.settings['quasi_steady']: # calculate unsteady (added mass) forces: @@ -320,7 +313,7 @@ def update_custom_grid(self, structure_tstep, aero_tstep, nl_body_tstep = None): self.data.nonlifting_body.generate_zeta_timestep_info(structure_tstep, nl_body_tstep, self.data.structure, - self.data.aero.aero_settings, + self.data.nonlifting_body.aero_settings, dt = self.settings['dt']) @staticmethod From 51d32188ae2f1a1629090b8f9c578490c67df6bc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stefanie=20D=C3=BCssler?= Date: Fri, 11 Aug 2023 08:55:01 +0200 Subject: [PATCH 160/232] refactor [dynamiccoupled] avoid unnecssary if statements - nl body tstep is defined as none previously anyway --- sharpy/solvers/dynamiccoupled.py | 31 ++++++++++--------------------- 1 file changed, 10 insertions(+), 21 deletions(-) diff --git a/sharpy/solvers/dynamiccoupled.py b/sharpy/solvers/dynamiccoupled.py index 466916d59..719a58380 100644 --- a/sharpy/solvers/dynamiccoupled.py +++ b/sharpy/solvers/dynamiccoupled.py @@ -615,18 +615,11 @@ def time_loop(self, in_queue=None, out_queue=None, finish_event=None, solvers=No # run the solver ini_time_aero = time.perf_counter() - if self.settings['nonlifting_body_interactions']: - self.data = self.aero_solver.run(aero_kstep, - structural_kstep, - convect_wake=True, - unsteady_contribution=unsteady_contribution, - nl_body_tstep = nl_body_kstep) - - else: - self.data = self.aero_solver.run(aero_kstep, - structural_kstep, - convect_wake=True, - unsteady_contribution=unsteady_contribution) + self.data = self.aero_solver.run(aero_step=aero_kstep, + structural_step=structural_kstep, + convect_wake=True, + unsteady_contribution=unsteady_contribution, + nl_body_tstep = nl_body_kstep) self.time_aero += time.perf_counter() - ini_time_aero previous_kstep = structural_kstep.copy() @@ -641,15 +634,11 @@ def time_loop(self, in_queue=None, out_queue=None, finish_event=None, solvers=No structural_kstep, aero_kstep, nl_body_kstep) - if self.settings['nonlifting_body_interactions']: - self.map_forces(aero_kstep, - structural_kstep, - nl_body_kstep = nl_body_kstep, - unsteady_forces_coeff = force_coeff) - else: - self.map_forces(aero_kstep, - structural_kstep, - unsteady_forces_coeff = force_coeff) + + self.map_forces(aero_kstep, + structural_kstep, + nl_body_kstep = nl_body_kstep, + unsteady_forces_coeff = force_coeff) # relaxation relax_factor = self.relaxation_factor(k) From 78ca03a0a67d432545e09b7dd177dc7e355cc425 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stefanie=20D=C3=BCssler?= Date: Fri, 11 Aug 2023 08:55:55 +0200 Subject: [PATCH 161/232] fix [unittest] dynamic test - add __main__ to enable individual test call - no setup classes --- tests/coupled/dynamic/test_dynamic.py | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/tests/coupled/dynamic/test_dynamic.py b/tests/coupled/dynamic/test_dynamic.py index 99cbca807..9c80dddce 100644 --- a/tests/coupled/dynamic/test_dynamic.py +++ b/tests/coupled/dynamic/test_dynamic.py @@ -12,13 +12,6 @@ class TestCoupledDynamic(unittest.TestCase): - Gust response of the hale aircraft """ - @classmethod - def setUpClass(cls): - # run all the cases generators - case = 'hale' - mod = importlib.import_module('tests.coupled.dynamic.' + case + '.generate_' + case) - pass - def test_hale_dynamic(self): """ Case and results from: @@ -27,7 +20,8 @@ def test_hale_dynamic(self): :return: """ import sharpy.sharpy_main - + import hale.generate_hale + case_name = 'hale' route_test_dir = os.path.abspath(os.path.dirname(os.path.realpath(__file__))) cases_folder = os.path.join(route_test_dir, case_name) @@ -66,3 +60,7 @@ def tearDownClass(cls): for extension in list_file_extensions: os.remove(os.path.join(file_path, case + extension)) pass + + +if __name__ == '__main__': + unittest.main() \ No newline at end of file From da7a0173cc727fad86faec0c41af11c5051007ea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stefanie=20D=C3=BCssler?= Date: Fri, 11 Aug 2023 08:59:08 +0200 Subject: [PATCH 162/232] move [case] get settings function for fuselage wing configuration relocate get setting function for model from test folder to case folder --- .../fwc_get_settings.py | 45 ++++++++++++++++++- .../test_source_panel_method.py | 2 +- .../sourcepanelmethod/test_vlm_coupled_spm.py | 2 +- 3 files changed, 46 insertions(+), 3 deletions(-) rename tests/sourcepanelmethod/define_simulation_settings.py => sharpy/cases/templates/fuselage_wing_configuration/fwc_get_settings.py (63%) diff --git a/tests/sourcepanelmethod/define_simulation_settings.py b/sharpy/cases/templates/fuselage_wing_configuration/fwc_get_settings.py similarity index 63% rename from tests/sourcepanelmethod/define_simulation_settings.py rename to sharpy/cases/templates/fuselage_wing_configuration/fwc_get_settings.py index 93b3d4207..0ee7557bc 100644 --- a/tests/sourcepanelmethod/define_simulation_settings.py +++ b/sharpy/cases/templates/fuselage_wing_configuration/fwc_get_settings.py @@ -20,7 +20,7 @@ def define_simulation_settings(flow, model, alpha_deg, u_inf, n_step = kwargs.get('n_step', 5) structural_relaxation_factor = kwargs.get('structural_relaxation_factor', 0.6) tolerance = kwargs.get('tolerance', 1e-6) - fsi_tolerance = kwargs.get('fsi_tolerance', 1e-4) + fsi_tolerance = kwargs.get('fsi_tolerance', 1e-6) num_cores = kwargs.get('num_cores',2) if not lifting_only: @@ -101,5 +101,48 @@ def define_simulation_settings(flow, model, alpha_deg, u_inf, settings['AerogridPlot'] = {'plot_nonlifting_surfaces': nonlifting_body_interactions} settings['BeamPlot'] = {} + settings['NoStructural'] = {} + settings['NonLinearDynamicPrescribedStep'] = {'print_info': 'off', + 'max_iterations': 950, + 'delta_curved': 1e-1, + 'min_delta': 1e-6, + 'newmark_damp': 1e-4, + 'gravity_on': gravity, + 'gravity': 9.81, + 'num_steps': kwargs.get('n_tstep',10), + 'dt': dt, + } + settings['StepUvlm'] = {'print_info': 'on', + 'num_cores': 4, + 'convection_scheme': 3, + 'velocity_field_input': {'u_inf': u_inf, + 'u_inf_direction': [1., 0., 0.]}, + 'rho': rho, + 'n_time_steps': kwargs.get('n_tstep',10), + 'dt': dt, + 'phantom_wing_test': phantom_test, + 'nonlifting_body_interactions': not lifting_only, + 'gamma_dot_filtering': 3} + dynamic_structural_solver = kwargs.get('structural_solver','NonLinearDynamicPrescribedStep') + settings['DynamicCoupled'] = {'structural_solver': dynamic_structural_solver, + 'structural_solver_settings': settings[dynamic_structural_solver], + 'aero_solver': 'StepUvlm', + 'aero_solver_settings': settings['StepUvlm'], + 'fsi_substeps': 200, + 'fsi_tolerance': fsi_tolerance, + 'relaxation_factor': 0.1, + 'minimum_steps': 1, + 'relaxation_steps': 150, + 'final_relaxation_factor': 0.05, + 'n_time_steps': kwargs.get('n_tstep',10), + 'dt': dt, + 'nonlifting_body_interactions': not lifting_only, + 'include_unsteady_force_contribution': kwargs.get('unsteady_force_distribution', True), + 'postprocessors': ['BeamLoads'], + 'postprocessors_settings': { + 'BeamLoads': {'csv_output': 'off'}, + }, + } + return settings \ No newline at end of file diff --git a/tests/sourcepanelmethod/test_source_panel_method.py b/tests/sourcepanelmethod/test_source_panel_method.py index 11e54927d..cc37a486a 100644 --- a/tests/sourcepanelmethod/test_source_panel_method.py +++ b/tests/sourcepanelmethod/test_source_panel_method.py @@ -2,8 +2,8 @@ import os import numpy as np from sharpy.cases.templates.fuselage_wing_configuration.fuselage_wing_configuration import Fuselage_Wing_Configuration +from sharpy.cases.templates.fuselage_wing_configuration.fwc_get_settings import define_simulation_settings -from define_simulation_settings import define_simulation_settings class TestSourcePanelMethod(unittest.TestCase): diff --git a/tests/sourcepanelmethod/test_vlm_coupled_spm.py b/tests/sourcepanelmethod/test_vlm_coupled_spm.py index 0f080be45..a802de10a 100644 --- a/tests/sourcepanelmethod/test_vlm_coupled_spm.py +++ b/tests/sourcepanelmethod/test_vlm_coupled_spm.py @@ -2,7 +2,7 @@ import os import numpy as np from sharpy.cases.templates.fuselage_wing_configuration.fuselage_wing_configuration import Fuselage_Wing_Configuration -from .define_simulation_settings import define_simulation_settings +from sharpy.cases.templates.fuselage_wing_configuration.fwc_get_settings import define_simulation_settings import json class TestUvlmCoupledWithSourcePanelMethod(unittest.TestCase): From d723338c57a5e200b4a79ee3a215a45e9ac51c7d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stefanie=20D=C3=BCssler?= Date: Fri, 11 Aug 2023 10:53:25 +0200 Subject: [PATCH 163/232] fix [unittest] imports for dynamic coupled test --- tests/coupled/dynamic/test_dynamic.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/tests/coupled/dynamic/test_dynamic.py b/tests/coupled/dynamic/test_dynamic.py index 9c80dddce..d677f3740 100644 --- a/tests/coupled/dynamic/test_dynamic.py +++ b/tests/coupled/dynamic/test_dynamic.py @@ -1,8 +1,6 @@ import numpy as np -import importlib import unittest import os -import sharpy.utils.cout_utils as cout class TestCoupledDynamic(unittest.TestCase): @@ -20,7 +18,10 @@ def test_hale_dynamic(self): :return: """ import sharpy.sharpy_main - import hale.generate_hale + try: + import hale.generate_hale + except: + import tests.coupled.dynamic.hale.generate_hale case_name = 'hale' route_test_dir = os.path.abspath(os.path.dirname(os.path.realpath(__file__))) From 90df1b813125f670d977f17acc40157d41d31b26 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stefanie=20D=C3=BCssler?= Date: Sun, 13 Aug 2023 15:37:44 +0200 Subject: [PATCH 164/232] fix [solver] get nonlifting timestep with same method --- sharpy/solvers/stepuvlm.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sharpy/solvers/stepuvlm.py b/sharpy/solvers/stepuvlm.py index 84e814cce..9cea8ab1e 100644 --- a/sharpy/solvers/stepuvlm.py +++ b/sharpy/solvers/stepuvlm.py @@ -235,8 +235,8 @@ def run(self, **kwargs): 'is_wake': True}, aero_tstep.u_ext_star) if self.settings['nonlifting_body_interactions']: - if nl_body_tstep is None: - nl_body_tstep = self.data.nonlifting_body.timestep_info[-1] + + nl_body_tstep = settings_utils.set_value_or_default(kwargs, 'nl_body_tstep', self.data.nonlifting_body.timestep_info[-1]) self.velocity_generator.generate({'zeta': nl_body_tstep.zeta, 'override': True, 'ts': self.data.ts, From c15e1814d974d9f00c59a84f86e71ab16fee1c65 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stefanie=20D=C3=BCssler?= Date: Mon, 14 Aug 2023 16:32:49 -0400 Subject: [PATCH 165/232] fix [aircraft model] stiffness multiplier for fuselage wing configuration --- .../cases/templates/fuselage_wing_configuration/fwc_aero.py | 5 ++++- .../templates/fuselage_wing_configuration/fwc_structure.py | 5 ++--- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/sharpy/cases/templates/fuselage_wing_configuration/fwc_aero.py b/sharpy/cases/templates/fuselage_wing_configuration/fwc_aero.py index a9e7674ba..24660932e 100644 --- a/sharpy/cases/templates/fuselage_wing_configuration/fwc_aero.py +++ b/sharpy/cases/templates/fuselage_wing_configuration/fwc_aero.py @@ -19,6 +19,7 @@ def __init__(self, structure, case_name, case_route, **kwargs): self.ea_wing = kwargs.get('elastic_axis',0.5) self.num_chordwise_panels = kwargs.get('num_chordwise_panels', 4) self.chord_wing = kwargs.get('chord', 1.) + self.alpha_zero_deg = kwargs.get('alpha_zero_deg', 0.) self.n_surfaces = 2 self.radius_fuselage = kwargs.get('max_radius', 0.5) self.lifting_only = kwargs.get('lifting_only', True) @@ -63,12 +64,14 @@ def set_wing_properties(self): """ if not self.lifting_only: - self.aero_node[:self.structure.n_node_right_wing] = abs(self.structure.y[:self.structure.n_node_right_wing]) > self.get_y_junction() + print(self.structure.y[:self.structure.n_node_right_wing]) + self.aero_node[:self.structure.n_node_right_wing] = abs(self.structure.y[:self.structure.n_node_right_wing]) > self.get_y_junction() - 0.05 self.aero_node[self.structure.n_node_right_wing:self.structure.n_node_wing_total] = self.aero_node[1:self.structure.n_node_right_wing] else: self.aero_node[:self.structure.n_node_wing_total] = True self.chord[:2*self.structure.n_elem_per_wing, :] = self.chord_wing self.elastic_axis[:2*self.structure.n_elem_per_wing, :] = self.ea_wing + self.twist [:2*self.structure.n_elem_per_wing, :] = -np.deg2rad(self.alpha_zero_deg) # surf distribution 0 for right and 1 for left wing self.surface_distribution[self.structure.n_elem_per_wing:2*self.structure.n_elem_per_wing] = 1 diff --git a/sharpy/cases/templates/fuselage_wing_configuration/fwc_structure.py b/sharpy/cases/templates/fuselage_wing_configuration/fwc_structure.py index c6689e511..c58a31117 100644 --- a/sharpy/cases/templates/fuselage_wing_configuration/fwc_structure.py +++ b/sharpy/cases/templates/fuselage_wing_configuration/fwc_structure.py @@ -9,7 +9,6 @@ class FWC_Structure: and discretisation and makes it accessible for SHARPy. """ def __init__(self, case_name, case_route, **kwargs): - self.sigma = kwargs.get('sigma', 1) self.n_elem_multiplier = kwargs.get('n_elem_multiplier', 1) self.route = case_route @@ -26,8 +25,8 @@ def __init__(self, case_name, case_route, **kwargs): self.n_elem_per_wing = kwargs.get('n_elem_per_wing', 10) self.n_elem_fuselage = kwargs.get('n_elem_fuselage', 10) - self.sigma = kwargs.get('sigma', 1) - self.sigma_fuselage = kwargs.get('sigma_fuselage', 10.) + self.sigma = kwargs.get('sigma', 10) + self.sigma_fuselage = kwargs.get('sigma_fuselage', 100.) From 4e4fcd03ae14240f3065dd4d35a5c561524948a9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stefanie=20D=C3=BCssler?= Date: Tue, 15 Aug 2023 05:10:48 -0400 Subject: [PATCH 166/232] rename data_dict to aero_dict - aero_dict was original parameter name - trivial change to data_dict caused many unnecessary changes --- .../example_notebooks/wind_turbine.ipynb | 2 +- sharpy/aero/models/aerogrid.py | 84 +++++++++---------- sharpy/aero/models/grid.py | 22 ++--- sharpy/aero/models/nonliftingbodygrid.py | 14 ++-- sharpy/aero/utils/mapping.py | 4 +- sharpy/generators/polaraeroforces.py | 20 ++--- .../assembler/lincontrolsurfacedeflector.py | 12 +-- sharpy/linear/src/libss.py | 42 +++++----- sharpy/postproc/liftdistribution.py | 4 +- sharpy/postproc/stallcheck.py | 2 +- sharpy/solvers/aerogridloader.py | 4 +- sharpy/solvers/beamloader.py | 30 +++---- sharpy/solvers/dynamiccoupled.py | 6 +- sharpy/solvers/gridloader.py | 6 +- sharpy/solvers/lindynamicsim.py | 8 +- sharpy/solvers/nonliftingbodygridloader.py | 4 +- sharpy/solvers/staticcoupled.py | 6 +- sharpy/solvers/trim.py | 2 +- sharpy/utils/h5utils.py | 2 +- sharpy/utils/multibody.py | 10 +-- .../statespace/test_variable_tracker.py | 4 +- 21 files changed, 144 insertions(+), 144 deletions(-) diff --git a/docs/source/content/example_notebooks/wind_turbine.ipynb b/docs/source/content/example_notebooks/wind_turbine.ipynb index 3d1722c5a..be366343a 100644 --- a/docs/source/content/example_notebooks/wind_turbine.ipynb +++ b/docs/source/content/example_notebooks/wind_turbine.ipynb @@ -1010,7 +1010,7 @@ " inode_in_elem = sharpy_output.structure.node_master_elem[node_global_index, 1]\n", " CAB = algebra.crv2rotation(tstep.psi[ielem, inode_in_elem, :])\n", "\n", - " c[iblade][inode] = sharpy_output.aero.data_dict['chord'][ielem,inode_in_elem]\n", + " c[iblade][inode] = sharpy_output.aero.aero_dict['chord'][ielem,inode_in_elem]\n", "\n", " forces_AFoR = np.dot(CAB, forces[iblade][inode, 0:3])\n", "\n", diff --git a/sharpy/aero/models/aerogrid.py b/sharpy/aero/models/aerogrid.py index 8cd84e574..5f263e7ec 100644 --- a/sharpy/aero/models/aerogrid.py +++ b/sharpy/aero/models/aerogrid.py @@ -34,8 +34,8 @@ def __init__(self): self.cs_generators = [] - def generate(self, data_dict, beam, settings, ts): - super().generate(data_dict, beam, settings, ts) + def generate(self, aero_dict, beam, settings, ts): + super().generate(aero_dict, beam, settings, ts) # write grid info to screen self.output_info() @@ -49,10 +49,10 @@ def generate(self, data_dict, beam, settings, ts): for i_elem in range(self.n_elem): for i_local_node in range(self.beam.num_node_elem): try: - self.airfoil_db[self.data_dict['airfoil_distribution'][i_elem, i_local_node]] + self.airfoil_db[self.aero_dict['airfoil_distribution'][i_elem, i_local_node]] except KeyError: - airfoil_coords = self.data_dict['airfoils'][str(self.data_dict['airfoil_distribution'][i_elem, i_local_node])] - self.airfoil_db[self.data_dict['airfoil_distribution'][i_elem, i_local_node]] = ( + airfoil_coords = self.aero_dict['airfoils'][str(self.aero_dict['airfoil_distribution'][i_elem, i_local_node])] + self.airfoil_db[self.aero_dict['airfoil_distribution'][i_elem, i_local_node]] = ( scipy.interpolate.interp1d(airfoil_coords[:, 0], airfoil_coords[:, 1], kind='quadratic', @@ -60,7 +60,7 @@ def generate(self, data_dict, beam, settings, ts): fill_value='extrapolate', assume_sorted=True)) try: - self.n_control_surfaces = np.sum(np.unique(self.data_dict['control_surface']) >= 0) + self.n_control_surfaces = np.sum(np.unique(self.aero_dict['control_surface']) >= 0) except KeyError: pass @@ -85,7 +85,7 @@ def generate(self, data_dict, beam, settings, ts): else: cout.cout_wrap('Initialising Control Surface {:g} generator'.format(i_cs), 1) # check that the control surface is not static - if self.data_dict['control_surface_type'][i_cs] == 0: + if self.aero_dict['control_surface_type'][i_cs] == 0: raise TypeError('Control surface {:g} is defined as static but there is a control surface generator' 'associated with it'.format(i_cs)) generator_type = gen_interface.generator_from_string( @@ -107,13 +107,13 @@ def generate(self, data_dict, beam, settings, ts): self.generate_mapping() self.generate_zeta(self.beam, self.aero_settings, ts) - if 'polars' in self.data_dict: + if 'polars' in self.aero_dict: import sharpy.aero.utils.airfoilpolars as ap self.polars = [] - nairfoils = np.amax(self.data_dict['airfoil_distribution']) + 1 + nairfoils = np.amax(self.aero_dict['airfoil_distribution']) + 1 for iairfoil in range(nairfoils): new_polar = ap.Polar() - new_polar.initialise(data_dict['polars'][str(iairfoil)]) + new_polar.initialise(aero_dict['polars'][str(iairfoil)]) self.polars.append(new_polar) def output_info(self): @@ -149,24 +149,24 @@ def generate_zeta_timestep_info(self, structure_tstep, aero_tstep, beam, setting # check that we have control surface information try: - self.data_dict['control_surface'] + self.aero_dict['control_surface'] with_control_surfaces = True except KeyError: with_control_surfaces = False # check that we have sweep information try: - self.data_dict['sweep'] + self.aero_dict['sweep'] except KeyError: - self.data_dict['sweep'] = np.zeros_like(self.data_dict['twist']) + self.aero_dict['sweep'] = np.zeros_like(self.aero_dict['twist']) # Define first_twist for backwards compatibility - if 'first_twist' not in self.data_dict: - self.data_dict['first_twist'] = [True]*self.data_dict['surface_m'].shape[0] + if 'first_twist' not in self.aero_dict: + self.aero_dict['first_twist'] = [True]*self.aero_dict['surface_m'].shape[0] # one surface per element for i_elem in range(self.n_elem): - i_surf = self.data_dict['surface_distribution'][i_elem] + i_surf = self.aero_dict['surface_distribution'][i_elem] # check if we have to generate a surface here if i_surf == -1: continue @@ -175,7 +175,7 @@ def generate_zeta_timestep_info(self, structure_tstep, aero_tstep, beam, setting i_global_node = self.beam.elements[i_elem].global_connectivities[i_local_node] # i_global_node = self.beam.elements[i_elem].global_connectivities[ # self.beam.elements[i_elem].ordering[i_local_node]] - if not self.data_dict['aero_node'][i_global_node]: + if not self.aero_dict['aero_node'][i_global_node]: continue if i_global_node in global_node_in_surface[i_surf]: continue @@ -203,23 +203,23 @@ def generate_zeta_timestep_info(self, structure_tstep, aero_tstep, beam, setting control_surface_info = None if with_control_surfaces: # 1) check that this node and elem have a control surface - if self.data_dict['control_surface'][i_elem, i_local_node] >= 0: - i_control_surface = self.data_dict['control_surface'][i_elem, i_local_node] + if self.aero_dict['control_surface'][i_elem, i_local_node] >= 0: + i_control_surface = self.aero_dict['control_surface'][i_elem, i_local_node] # 2) type of control surface + write info control_surface_info = dict() - if self.data_dict['control_surface_type'][i_control_surface] == 0: + if self.aero_dict['control_surface_type'][i_control_surface] == 0: control_surface_info['type'] = 'static' - control_surface_info['deflection'] = self.data_dict['control_surface_deflection'][i_control_surface] - control_surface_info['chord'] = self.data_dict['control_surface_chord'][i_control_surface] + control_surface_info['deflection'] = self.aero_dict['control_surface_deflection'][i_control_surface] + control_surface_info['chord'] = self.aero_dict['control_surface_chord'][i_control_surface] try: - control_surface_info['hinge_coords'] = self.data_dict['control_surface_hinge_coords'][i_control_surface] + control_surface_info['hinge_coords'] = self.aero_dict['control_surface_hinge_coords'][i_control_surface] except KeyError: control_surface_info['hinge_coords'] = None - elif self.data_dict['control_surface_type'][i_control_surface] == 1: + elif self.aero_dict['control_surface_type'][i_control_surface] == 1: control_surface_info['type'] = 'dynamic' - control_surface_info['chord'] = self.data_dict['control_surface_chord'][i_control_surface] + control_surface_info['chord'] = self.aero_dict['control_surface_chord'][i_control_surface] try: - control_surface_info['hinge_coords'] = self.data_dict['control_surface_hinge_coords'][i_control_surface] + control_surface_info['hinge_coords'] = self.aero_dict['control_surface_hinge_coords'][i_control_surface] except KeyError: control_surface_info['hinge_coords'] = None @@ -227,7 +227,7 @@ def generate_zeta_timestep_info(self, structure_tstep, aero_tstep, beam, setting control_surface_info['deflection'], control_surface_info['deflection_dot'] = \ self.cs_generators[i_control_surface](params) - elif self.data_dict['control_surface_type'][i_control_surface] == 2: + elif self.aero_dict['control_surface_type'][i_control_surface] == 2: control_surface_info['type'] = 'controlled' try: @@ -236,12 +236,12 @@ def generate_zeta_timestep_info(self, structure_tstep, aero_tstep, beam, setting try: old_deflection = aero_tstep.control_surface_deflection[i_control_surface] except IndexError: - old_deflection = self.data_dict['control_surface_deflection'][i_control_surface] + old_deflection = self.aero_dict['control_surface_deflection'][i_control_surface] try: control_surface_info['deflection'] = aero_tstep.control_surface_deflection[i_control_surface] except IndexError: - control_surface_info['deflection'] = self.data_dict['control_surface_deflection'][i_control_surface] + control_surface_info['deflection'] = self.aero_dict['control_surface_deflection'][i_control_surface] if dt is not None: control_surface_info['deflection_dot'] = ( @@ -249,14 +249,14 @@ def generate_zeta_timestep_info(self, structure_tstep, aero_tstep, beam, setting else: control_surface_info['deflection_dot'] = 0.0 - control_surface_info['chord'] = self.data_dict['control_surface_chord'][i_control_surface] + control_surface_info['chord'] = self.aero_dict['control_surface_chord'][i_control_surface] try: - control_surface_info['hinge_coords'] = self.data_dict['control_surface_hinge_coords'][i_control_surface] + control_surface_info['hinge_coords'] = self.aero_dict['control_surface_hinge_coords'][i_control_surface] except KeyError: control_surface_info['hinge_coords'] = None else: - raise NotImplementedError(str(self.data_dict['control_surface_type'][i_control_surface]) + + raise NotImplementedError(str(self.aero_dict['control_surface_type'][i_control_surface]) + ' control surfaces are not yet implemented') @@ -264,13 +264,13 @@ def generate_zeta_timestep_info(self, structure_tstep, aero_tstep, beam, setting node_info = dict() node_info['i_node'] = i_global_node node_info['i_local_node'] = i_local_node - node_info['chord'] = self.data_dict['chord'][i_elem, i_local_node] - node_info['eaxis'] = self.data_dict['elastic_axis'][i_elem, i_local_node] - node_info['twist'] = self.data_dict['twist'][i_elem, i_local_node] - node_info['sweep'] = self.data_dict['sweep'][i_elem, i_local_node] + node_info['chord'] = self.aero_dict['chord'][i_elem, i_local_node] + node_info['eaxis'] = self.aero_dict['elastic_axis'][i_elem, i_local_node] + node_info['twist'] = self.aero_dict['twist'][i_elem, i_local_node] + node_info['sweep'] = self.aero_dict['sweep'][i_elem, i_local_node] node_info['M'] = self.dimensions[i_surf, 0] - node_info['M_distribution'] = self.data_dict['m_distribution'].decode('ascii') - node_info['airfoil'] = self.data_dict['airfoil_distribution'][i_elem, i_local_node] + node_info['M_distribution'] = self.aero_dict['m_distribution'].decode('ascii') + node_info['airfoil'] = self.aero_dict['airfoil_distribution'][i_elem, i_local_node] node_info['control_surface'] = control_surface_info node_info['beam_coord'] = structure_tstep.pos[i_global_node, :] node_info['pos_dot'] = structure_tstep.pos_dot[i_global_node, :] @@ -282,7 +282,7 @@ def generate_zeta_timestep_info(self, structure_tstep, aero_tstep, beam, setting node_info['cga'] = structure_tstep.cga() if node_info['M_distribution'].lower() == 'user_defined': ielem_in_surf = i_elem - np.sum(self.surface_distribution < i_surf) - node_info['user_defined_m_distribution'] = self.data_dict['user_defined_m_distribution'][str(i_surf)][:, ielem_in_surf, i_local_node] + node_info['user_defined_m_distribution'] = self.aero_dict['user_defined_m_distribution'][str(i_surf)][:, ielem_in_surf, i_local_node] (aero_tstep.zeta[i_surf][:, :, i_n], aero_tstep.zeta_dot[i_surf][:, :, i_n]) = ( generate_strip(node_info, @@ -291,14 +291,14 @@ def generate_zeta_timestep_info(self, structure_tstep, aero_tstep, beam, setting orientation_in=self.aero_settings['freestream_dir'], calculate_zeta_dot=True)) # set junction boundary conditions for later phantom cell creation in UVLM - if "junction_boundary_condition" in self.data_dict: - if np.any(self.data_dict["junction_boundary_condition"] >= 0): + if "junction_boundary_condition" in self.aero_dict: + if np.any(self.aero_dict["junction_boundary_condition"] >= 0): self.generate_phantom_panels_at_junction(aero_tstep) def generate_phantom_panels_at_junction(self, aero_tstep): for i_surf in range(self.n_surf): - aero_tstep.flag_zeta_phantom[0, i_surf] = self.data_dict["junction_boundary_condition"][0,i_surf] + aero_tstep.flag_zeta_phantom[0, i_surf] = self.aero_dict["junction_boundary_condition"][0,i_surf] diff --git a/sharpy/aero/models/grid.py b/sharpy/aero/models/grid.py index 176a7a5e6..d50af7748 100644 --- a/sharpy/aero/models/grid.py +++ b/sharpy/aero/models/grid.py @@ -20,7 +20,7 @@ class Grid(object): """ def __init__(self): - self.data_dict = None + self.aero_dict = None self.beam = None self.aero_settings = None self.timestep_info = [] @@ -41,27 +41,27 @@ def __init__(self): self.aero2struct_mapping = [] - def generate(self, data_dict, beam, aero_settings, ts): + def generate(self, aero_dict, beam, aero_settings, ts): - self.data_dict = data_dict + self.aero_dict = aero_dict self.beam = beam self.aero_settings = aero_settings # key words = safe in aero_settings? --> grid_type # number of total nodes (structural + aero&struc) - self.n_node = len(data_dict[self.grid_type + '_node']) # gridtype + '_node' + self.n_node = len(aero_dict[self.grid_type + '_node']) # gridtype + '_node' # number of elements - self.n_elem = len(data_dict['surface_distribution']) + self.n_elem = len(aero_dict['surface_distribution']) # surface distribution - self.surface_distribution = data_dict['surface_distribution'] + self.surface_distribution = aero_dict['surface_distribution'] # number of surfaces - temp = set(data_dict['surface_distribution']) + temp = set(aero_dict['surface_distribution']) #TO-DO: improve: avoid for loops self.n_surf = sum(1 for i in temp if i >= 0) # number of chordwise panels - self.surface_m = data_dict['surface_m'] + self.surface_m = aero_dict['surface_m'] # number of aero nodes - self.n_aero_node = sum(data_dict[self.grid_type + '_node']) + self.n_aero_node = sum(aero_dict[self.grid_type + '_node']) # get N per surface @@ -96,7 +96,7 @@ def calculate_dimensions(self): continue else: nodes_in_surface[i_surf].append(i_global_node) - if self.data_dict[self.grid_type + '_node'][i_global_node]: + if self.aero_dict[self.grid_type + '_node'][i_global_node]: self.dimensions[i_surf, 1] += 1 # accounting for N+1 nodes -> N panels @@ -131,7 +131,7 @@ def generate_mapping(self): if i_surf == -1: continue for i_global_node in self.beam.elements[i_elem].reordered_global_connectivities: - if not self.data_dict[self.grid_type + '_node'][i_global_node]: + if not self.aero_dict[self.grid_type + '_node'][i_global_node]: continue if i_global_node in nodes_in_surface[i_surf]: diff --git a/sharpy/aero/models/nonliftingbodygrid.py b/sharpy/aero/models/nonliftingbodygrid.py index 465b87d68..d21a211cb 100644 --- a/sharpy/aero/models/nonliftingbodygrid.py +++ b/sharpy/aero/models/nonliftingbodygrid.py @@ -22,8 +22,8 @@ def __init__(self): self.grid_type = 'nonlifting_body' - def generate(self, data_dict, beam, nonlifting_body_settings, ts): ##input? - super().generate(data_dict, beam, nonlifting_body_settings, ts) + def generate(self, aero_dict, beam, nonlifting_body_settings, ts): ##input? + super().generate(aero_dict, beam, nonlifting_body_settings, ts) # allocating initial grid storage self.ini_info = NonliftingBodyTimeStepInfo(self.dimensions) @@ -53,10 +53,10 @@ def get_zeta_and_zeta_dot(self,i_surf, structure_tstep): for node_counter, i_global_node in enumerate(self.aero2struct_mapping[i_surf]): # TODO: Adjust for ellipse input - if self.data_dict["shape"] == 'specific': - a_ellipse = self.data_dict["a_ellipse"][i_global_node] - b_ellipse = self.data_dict["b_ellipse"][i_global_node] - z_0 =self.data_dict["z_0_ellipse"][i_global_node] + if self.aero_dict["shape"] == 'specific': + a_ellipse = self.aero_dict["a_ellipse"][i_global_node] + b_ellipse = self.aero_dict["b_ellipse"][i_global_node] + z_0 =self.aero_dict["z_0_ellipse"][i_global_node] if a_ellipse == 0. or b_ellipse == 0.: radius = 0 else: @@ -64,7 +64,7 @@ def get_zeta_and_zeta_dot(self,i_surf, structure_tstep): (b_ellipse*array_cos_phi)**2 +(a_ellipse*array_sin_phi)**2) else: - radius = self.data_dict["radius"][i_global_node] + radius = self.aero_dict["radius"][i_global_node] z_0 = 0 diff --git a/sharpy/aero/utils/mapping.py b/sharpy/aero/utils/mapping.py index c7792ef3d..736d6abb6 100644 --- a/sharpy/aero/utils/mapping.py +++ b/sharpy/aero/utils/mapping.py @@ -11,7 +11,7 @@ def aero2struct_force_mapping(aero_forces, master, conn, cag=np.eye(3), - data_dict=None, + aero_dict=None, skip_moments_generated_by_forces = False): r""" Maps the aerodynamic forces at the lattice to the structural nodes @@ -40,7 +40,7 @@ def aero2struct_force_mapping(aero_forces, master: Unused conn (np.ndarray): Connectivities matrix cag (np.ndarray): Transformation matrix between inertial and body-attached reference ``A`` - data_dict (dict): Dictionary containing the grid's information. + aero_dict (dict): Dictionary containing the grid's information. Returns: np.ndarray: structural forces in an ``n_node x 6`` vector diff --git a/sharpy/generators/polaraeroforces.py b/sharpy/generators/polaraeroforces.py index b94c368d7..c3e471cf7 100644 --- a/sharpy/generators/polaraeroforces.py +++ b/sharpy/generators/polaraeroforces.py @@ -155,7 +155,7 @@ def generate(self, **params): list_aoa_induced = [] - data_dict = aerogrid.data_dict + aero_dict = aerogrid.aero_dict if aerogrid.polars is None: return struct_forces new_struct_forces = np.zeros_like(struct_forces) @@ -168,9 +168,9 @@ def generate(self, **params): for inode in range(nnode): new_struct_forces[inode, :] = struct_forces[inode, :].copy() - if data_dict['aero_node'][inode]: + if aero_dict['aero_node'][inode]: ielem, inode_in_elem = structure.node_master_elem[inode] - iairfoil = data_dict['airfoil_distribution'][ielem, inode_in_elem] + iairfoil = aero_dict['airfoil_distribution'][ielem, inode_in_elem] isurf = aerogrid.struct2aero_mapping[inode][0]['i_surf'] if isurf not in self.settings['skip_surfaces']: i_n = aerogrid.struct2aero_mapping[inode][0]['i_n'] @@ -179,7 +179,7 @@ def generate(self, **params): cgb = np.dot(cga, cab) if not self.cd_from_cl: - airfoil = str(data_dict['airfoil_distribution'][ielem, inode_in_elem]) + airfoil = str(aero_dict['airfoil_distribution'][ielem, inode_in_elem]) aoa_0cl = self.list_aoa_cl0[int(airfoil)] # computing surface area of panels contributing to force @@ -317,7 +317,7 @@ def check_for_special_cases(self, aerogrid): # check if outboard node of aerosurface self.flag_shared_node_by_surfaces = np.zeros((self.n_node,1)) for inode in range(self.n_node): - if aerogrid.data_dict['aero_node'][inode]: + if aerogrid.aero_dict['aero_node'][inode]: i_n = aerogrid.struct2aero_mapping[inode][0]['i_n'] isurf = aerogrid.struct2aero_mapping[inode][0]['i_surf'] N = aerogrid.dimensions[isurf, 1] @@ -350,9 +350,9 @@ def compute_aoa_cl0_from_airfoil_data(self, aerogrid): """ Computes the angle of attack for which zero lift is achieved for every airfoil """ - self.list_aoa_cl0 = np.zeros((len(aerogrid.data_dict['airfoils']),1)) - for i, airfoil in enumerate(aerogrid.data_dict['airfoils']): - airfoil_coords = aerogrid.data_dict['airfoils'][airfoil] + self.list_aoa_cl0 = np.zeros((len(aerogrid.aero_dict['airfoils']),1)) + for i, airfoil in enumerate(aerogrid.aero_dict['airfoils']): + airfoil_coords = aerogrid.aero_dict['airfoils'][airfoil] self.list_aoa_cl0[i] = get_aoacl0_from_camber(airfoil_coords[:, 0], airfoil_coords[:, 1]) @generator_interface.generator @@ -401,11 +401,11 @@ def generate(self, **params): n_node = self.structure.num_node n_elem = self.structure.num_elem - data_dict = self.aero.data_dict + aero_dict = self.aero.aero_dict new_struct_forces = np.zeros_like(struct_forces) # load airfoil efficiency (if it exists); else set to one (to avoid multiple ifs in the loops) - airfoil_efficiency = data_dict['airfoil_efficiency'] + airfoil_efficiency = aero_dict['airfoil_efficiency'] # force efficiency dimensions [n_elem, n_node_elem, 2, [fx, fy, fz]] - all defined in B frame force_efficiency = np.zeros((n_elem, 3, 2, 3)) force_efficiency[:, :, 0, :] = 1. diff --git a/sharpy/linear/assembler/lincontrolsurfacedeflector.py b/sharpy/linear/assembler/lincontrolsurfacedeflector.py index 2f99fda4b..e1c2007dc 100644 --- a/sharpy/linear/assembler/lincontrolsurfacedeflector.py +++ b/sharpy/linear/assembler/lincontrolsurfacedeflector.py @@ -82,7 +82,7 @@ def generate(self): tsstruct0 = self.tsstruct0 # Find the vertices corresponding to a control surface from beam coordinates to aerogrid - data_dict = aero.data_dict + aero_dict = aero.aero_dict n_surf = tsaero0.n_surf n_control_surfaces = self.n_control_surfaces @@ -120,7 +120,7 @@ def generate(self): # Although a node may be part of 2 aerodynamic surfaces, we need to ensure that the current # element for the given node is indeed part of that surface. - elems_in_surf = np.where(data_dict['surface_distribution'] == i_surf)[0] + elems_in_surf = np.where(aero_dict['surface_distribution'] == i_surf)[0] if i_elem not in elems_in_surf: continue @@ -131,18 +131,18 @@ def generate(self): K_zeta_start = 3 * sum(linuvlm.MS.KKzeta[:i_surf]) shape_zeta = (3, M + 1, N + 1) - i_control_surface = data_dict['control_surface'][i_elem, i_local_node] + i_control_surface = aero_dict['control_surface'][i_elem, i_local_node] if i_control_surface >= 0: if not with_control_surface: i_start_of_cs = i_node_span.copy() with_control_surface = True - control_surface_chord = data_dict['control_surface_chord'][i_control_surface] + control_surface_chord = aero_dict['control_surface_chord'][i_control_surface] try: control_surface_hinge_coord = \ - data_dict['control_surface_hinge_coord'][i_control_surface] * \ - data_dict['chord'][i_elem, i_local_node] + aero_dict['control_surface_hinge_coord'][i_control_surface] * \ + aero_dict['chord'][i_elem, i_local_node] except KeyError: control_surface_hinge_coord = None diff --git a/sharpy/linear/src/libss.py b/sharpy/linear/src/libss.py index 7d5736a59..c2f4ad082 100644 --- a/sharpy/linear/src/libss.py +++ b/sharpy/linear/src/libss.py @@ -428,22 +428,22 @@ def load_from_h5(cls, h5_file_name): """ with h5py.File(h5_file_name, 'r') as f: - data_dict = h5utils.load_h5_in_dict(f) + aero_dict = h5utils.load_h5_in_dict(f) - new_ss = cls(data_dict['a'], - data_dict['b'], - data_dict['c'], - data_dict['d'], - dt=data_dict.get('dt')) + new_ss = cls(aero_dict['a'], + aero_dict['b'], + aero_dict['c'], + aero_dict['d'], + dt=aero_dict.get('dt')) - input_variables = data_dict.get('InputVariable') + input_variables = aero_dict.get('InputVariable') if input_variables is not None: new_ss.input_variables = LinearVector.load_from_h5_file('InputVariable', - data_dict['InputVariable']) + aero_dict['InputVariable']) new_ss.output_variables = LinearVector.load_from_h5_file('OutputVariable', - data_dict['OutputVariable']) + aero_dict['OutputVariable']) new_ss.state_variables = LinearVector.load_from_h5_file('StateVariable', - data_dict['StateVariable']) + aero_dict['StateVariable']) return new_ss else: @@ -692,35 +692,35 @@ def load_from_h5(cls, h5_file_name): Gain: instance of a Gain """ with h5py.File(h5_file_name, 'r') as f: - data_dict = h5utils.load_h5_in_dict(f) + aero_dict = h5utils.load_h5_in_dict(f) - return cls.load_from_dict(data_dict) + return cls.load_from_dict(aero_dict) @classmethod - def load_from_dict(cls, data_dict): + def load_from_dict(cls, aero_dict): """ Returns a Gain from a dictionary of data, useful for loading from a group of gains in a single .h5 file Args: - data_dict (dict): Dictionary with keys: ``gain`` and (if available) ``InputVariable`` + aero_dict (dict): Dictionary with keys: ``gain`` and (if available) ``InputVariable`` and ``OutputVariable``. Returns: Gain: instance of Gain """ - input_variables = data_dict.get('InputVariable') + input_variables = aero_dict.get('InputVariable') if input_variables is not None: input_variables = LinearVector.load_from_h5_file('InputVariable', - data_dict['InputVariable']) + aero_dict['InputVariable']) output_variables = LinearVector.load_from_h5_file('OutputVariable', - data_dict['OutputVariable']) + aero_dict['OutputVariable']) - return cls(data_dict['gain'], input_vars=input_variables, + return cls(aero_dict['gain'], input_vars=input_variables, output_vars=output_variables) else: - return cls(data_dict['gain']) + return cls(aero_dict['gain']) @classmethod def save_multiple_gains(cls, h5_file_name, *gains_names_tuple): @@ -749,10 +749,10 @@ def load_multiple_gains(cls, h5_file_name): dict: Dictionary of loaded gains in a gain_name: Gain dictionary """ with h5py.File(h5_file_name, 'r') as f: - data_dict = h5utils.load_h5_in_dict(f) + aero_dict = h5utils.load_h5_in_dict(f) out_gains = {} - for gain_name, gain_data in data_dict.items(): + for gain_name, gain_data in aero_dict.items(): out_gains[gain_name] = cls.load_from_dict(gain_data) return out_gains diff --git a/sharpy/postproc/liftdistribution.py b/sharpy/postproc/liftdistribution.py index dadda480c..f660a5af6 100644 --- a/sharpy/postproc/liftdistribution.py +++ b/sharpy/postproc/liftdistribution.py @@ -69,7 +69,7 @@ def lift_distribution(self, struct_tstep, aero_tstep): self.data.structure.node_master_elem, self.data.structure.connectivities, struct_tstep.cag(), - self.data.aero.data_dict) + self.data.aero.aero_dict) # Prepare output matrix and file N_nodes = self.data.structure.num_node numb_col = 6 @@ -84,7 +84,7 @@ def lift_distribution(self, struct_tstep, aero_tstep): lift_distribution = np.zeros((N_nodes, numb_col)) for inode in range(N_nodes): - if self.data.aero.data_dict['aero_node'][inode]: + if self.data.aero.aero_dict['aero_node'][inode]: local_node = self.data.aero.struct2aero_mapping[inode][0]["i_n"] ielem, inode_in_elem = self.data.structure.node_master_elem[inode] i_surf = int(self.data.aero.surface_distribution[ielem]) diff --git a/sharpy/postproc/stallcheck.py b/sharpy/postproc/stallcheck.py index 690cfe43e..e75fbff29 100644 --- a/sharpy/postproc/stallcheck.py +++ b/sharpy/postproc/stallcheck.py @@ -103,7 +103,7 @@ def check_stall(self): for i_elem in range(self.data.structure.num_elem): for i_local_node in range(self.data.structure.num_node_elem): - airfoil_id = self.data.aero.data_dict['airfoil_distribution'][i_elem, i_local_node] + airfoil_id = self.data.aero.aero_dict['airfoil_distribution'][i_elem, i_local_node] if self.settings['airfoil_stall_angles']: i_global_node = self.data.structure.connectivities[i_elem, i_local_node] for i_dict in self.data.aero.struct2aero_mapping[i_global_node]: diff --git a/sharpy/solvers/aerogridloader.py b/sharpy/solvers/aerogridloader.py index 84110f475..801994eef 100644 --- a/sharpy/solvers/aerogridloader.py +++ b/sharpy/solvers/aerogridloader.py @@ -39,7 +39,7 @@ class AerogridLoader(GridLoader): data (ProblemData): class structure file_name (str): name of the ``.aero.h5`` HDF5 file aero: empty attribute - data_dict (dict): key-value pairs of aerodynamic data + aero_dict (dict): key-value pairs of aerodynamic data wake_shape_generator (class): Wake shape generator """ @@ -107,7 +107,7 @@ def initialise(self, data, restart=False): def run(self): self.data.aero = aerogrid.Aerogrid() - self.data.aero.generate(self.data_dict, + self.data.aero.generate(self.aero_dict, self.data.structure, self.settings, self.data.ts) diff --git a/sharpy/solvers/beamloader.py b/sharpy/solvers/beamloader.py index a30ee6086..97b1e8dcd 100644 --- a/sharpy/solvers/beamloader.py +++ b/sharpy/solvers/beamloader.py @@ -25,8 +25,8 @@ class BeamLoader(BaseSolver): data (ProblemData): class containing the data for the problem fem_file_name (str): name of the ``.fem.h5`` HDF5 file dyn_file_name (str): name of the ``.dyn.h5`` HDF5 file - fem_data_dict (dict): key-value pairs of FEM data - dyn_data_dict (dict): key-value pairs of data for dynamic problems + fem_aero_dict (dict): key-value pairs of FEM data + dyn_aero_dict (dict): key-value pairs of data for dynamic problems structure (None): Empty attribute Notes: @@ -68,9 +68,9 @@ def __init__(self): self.fem_file_name = '' self.dyn_file_name = '' # storage of file contents - self.fem_data_dict = dict() - self.dyn_data_dict = dict() - self.mb_data_dict = dict() + self.fem_aero_dict = dict() + self.dyn_aero_dict = dict() + self.mb_aero_dict = dict() # structure storage self.structure = None @@ -101,13 +101,13 @@ def read_files(self): # read and store the hdf5 files with h5.File(self.fem_file_name, 'r') as fem_file_handle: # store files in dictionary - self.fem_data_dict = h5utils.load_h5_in_dict(fem_file_handle) + self.fem_aero_dict = h5utils.load_h5_in_dict(fem_file_handle) # TODO implement fem file validation # self.validate_fem_file() if self.settings['unsteady']: with h5.File(self.dyn_file_name, 'r') as dyn_file_handle: # store files in dictionary - self.dyn_data_dict = h5utils.load_h5_in_dict(dyn_file_handle) + self.dyn_aero_dict = h5utils.load_h5_in_dict(dyn_file_handle) # TODO implement dyn file validation # self.validate_dyn_file() @@ -116,13 +116,13 @@ def read_files(self): if os.path.isfile(self.mb_file_name): # h5utils.check_file_exists(self.mb_file_name) with h5.File(self.mb_file_name, 'r') as mb_file_handle: - self.mb_data_dict = h5utils.load_h5_in_dict(mb_file_handle) + self.mb_aero_dict = h5utils.load_h5_in_dict(mb_file_handle) # Need to redefine strings to remove the "b" at the beginning - for iconstraint in range(self.mb_data_dict['num_constraints']): - self.mb_data_dict["constraint_%02d" % iconstraint]['behaviour'] = self.mb_data_dict["constraint_%02d" % iconstraint]['behaviour'].decode() - for ibody in range(self.mb_data_dict['num_bodies']): - self.mb_data_dict["body_%02d" % ibody]['FoR_movement'] = self.mb_data_dict["body_%02d" % ibody]['FoR_movement'].decode() + for iconstraint in range(self.mb_aero_dict['num_constraints']): + self.mb_aero_dict["constraint_%02d" % iconstraint]['behaviour'] = self.mb_aero_dict["constraint_%02d" % iconstraint]['behaviour'].decode() + for ibody in range(self.mb_aero_dict['num_bodies']): + self.mb_aero_dict["body_%02d" % ibody]['FoR_movement'] = self.mb_aero_dict["body_%02d" % ibody]['FoR_movement'].decode() def validate_fem_file(self): raise NotImplementedError('validation of the fem file in beamloader is not yet implemented!') @@ -132,9 +132,9 @@ def validate_dyn_file(self): def run(self, **kwargs): self.data.structure = beam.Beam() - self.data.structure.ini_mb_dict = self.mb_data_dict - self.data.structure.generate(self.fem_data_dict, self.settings) - self.data.structure.dyn_dict = self.dyn_data_dict + self.data.structure.ini_mb_dict = self.mb_aero_dict + self.data.structure.generate(self.fem_aero_dict, self.settings) + self.data.structure.dyn_dict = self.dyn_aero_dict # Change the beam description to the local FoR for multibody # if (self.data.structure.num_bodies > 1): diff --git a/sharpy/solvers/dynamiccoupled.py b/sharpy/solvers/dynamiccoupled.py index 719a58380..9fdb61324 100644 --- a/sharpy/solvers/dynamiccoupled.py +++ b/sharpy/solvers/dynamiccoupled.py @@ -826,7 +826,7 @@ def map_forces(self, aero_kstep, structural_kstep, nl_body_kstep = None, unstead self.data.structure.node_master_elem, self.data.structure.connectivities, structural_kstep.cag(), - self.data.aero.data_dict) + self.data.aero.aero_dict) dynamic_struct_forces = unsteady_forces_coeff*mapping.aero2struct_force_mapping( aero_kstep.dynamic_forces, self.data.aero.struct2aero_mapping, @@ -836,7 +836,7 @@ def map_forces(self, aero_kstep, structural_kstep, nl_body_kstep = None, unstead self.data.structure.node_master_elem, self.data.structure.connectivities, structural_kstep.cag(), - self.data.aero.data_dict) + self.data.aero.aero_dict) if self.correct_forces: struct_forces = \ @@ -859,7 +859,7 @@ def map_forces(self, aero_kstep, structural_kstep, nl_body_kstep = None, unstead # self.data.structure.node_master_elem, # self.data.structure.connectivities, # structural_kstep.cag(), - # self.data.nonlifting_body.data_dict) + # self.data.nonlifting_body.aero_dict) # prescribed forces + aero forces # prescribed forces + aero forces + runtime generated structural_kstep.steady_applied_forces += struct_forces diff --git a/sharpy/solvers/gridloader.py b/sharpy/solvers/gridloader.py index ee5803faa..610b16d50 100644 --- a/sharpy/solvers/gridloader.py +++ b/sharpy/solvers/gridloader.py @@ -24,7 +24,7 @@ class GridLoader(BaseSolver): data (ProblemData): class structure afile_name (str): name of the HDF5 file, e.g. ``.aero.h5`` aero: empty attribute - data_dict (dict): key-value pairs of aerodynamic data + aero_dict (dict): key-value pairs of aerodynamic data """ solver_id = 'GridLoader' @@ -39,7 +39,7 @@ def __init__(self): self.data = None self.settings = None self.file_name = '' - self.data_dict = dict() + self.aero_dict = dict() def initialise(self, data): self.data = data @@ -61,4 +61,4 @@ def read_input_files(self): # read and store the hdf5 file in dictionary with h5.File(self.file_name, 'r') as file_handle: - self.data_dict = h5utils.load_h5_in_dict(file_handle) \ No newline at end of file + self.aero_dict = h5utils.load_h5_in_dict(file_handle) \ No newline at end of file diff --git a/sharpy/solvers/lindynamicsim.py b/sharpy/solvers/lindynamicsim.py index 8538e0bab..e4a52375a 100644 --- a/sharpy/solvers/lindynamicsim.py +++ b/sharpy/solvers/lindynamicsim.py @@ -83,7 +83,7 @@ def __init__(self): self.postprocessors = dict() self.with_postprocessors = False - self.input_data_dict = dict() + self.input_aero_dict = dict() self.input_file_name = "" self.folder = None @@ -154,11 +154,11 @@ def run(self, **kwargs): ss = self.data.linear.ss n_steps = self.settings['n_tsteps'] - x0 = self.input_data_dict.get('x0', np.zeros(ss.states)) + x0 = self.input_aero_dict.get('x0', np.zeros(ss.states)) if len(self.settings['input_generators']) != 0: u = self.input_vector(ss) else: - u = self.input_data_dict['u'] + u = self.input_aero_dict['u'] if len(x0) != ss.states: warnings.warn('Number of states in the initial state vector not equal to the number of states') @@ -254,7 +254,7 @@ def read_files(self): h5utils.check_file_exists(self.input_file_name) # Read and store with h5.File(self.input_file_name, 'r') as input_file_handle: - self.input_data_dict = h5utils.load_h5_in_dict(input_file_handle) + self.input_aero_dict = h5utils.load_h5_in_dict(input_file_handle) except FileNotFoundError: pass diff --git a/sharpy/solvers/nonliftingbodygridloader.py b/sharpy/solvers/nonliftingbodygridloader.py index 75a73d74d..0b6ce89dc 100644 --- a/sharpy/solvers/nonliftingbodygridloader.py +++ b/sharpy/solvers/nonliftingbodygridloader.py @@ -21,7 +21,7 @@ class NonliftingbodygridLoader(GridLoader): data (ProblemData): class structure file_name (str): name of the ``.nonlifting_body.h5`` HDF5 file aero: empty attribute - aero_data_dict (dict): key-value pairs of aerodynamic data + aero_dict (dict): key-value pairs of aerodynamic data """ @@ -37,7 +37,7 @@ def __init__(self): def run(self): self.data.nonlifting_body = nonliftingbodygrid.NonliftingBodyGrid() - self.data.nonlifting_body.generate(self.data_dict, + self.data.nonlifting_body.generate(self.aero_dict, self.data.structure, self.settings, self.data.ts) diff --git a/sharpy/solvers/staticcoupled.py b/sharpy/solvers/staticcoupled.py index e3fde1bae..c2cda6110 100644 --- a/sharpy/solvers/staticcoupled.py +++ b/sharpy/solvers/staticcoupled.py @@ -196,7 +196,7 @@ def run(self, **kwargs): self.data.structure.node_master_elem, self.data.structure.connectivities, self.data.structure.timestep_info[self.data.ts].cag(), - self.data.aero.data_dict) + self.data.aero.aero_dict) if self.correct_forces: struct_forces = \ @@ -216,7 +216,7 @@ def run(self, **kwargs): self.data.structure.node_master_elem, self.data.structure.connectivities, self.data.structure.timestep_info[self.data.ts].cag(), - self.data.nonlifting_body.data_dict, + self.data.nonlifting_body.aero_dict, skip_moments_generated_by_forces = True) self.data.aero.timestep_info[self.data.ts].aero_steady_forces_beam_dof = struct_forces @@ -365,7 +365,7 @@ def change_trim(self, alpha, thrust, thrust_nodes, tail_deflection, tail_cs_inde # tail deflection try: - self.data.aero.data_dict['control_surface_deflection'][tail_cs_index] = tail_deflection + self.data.aero.aero_dict['control_surface_deflection'][tail_cs_index] = tail_deflection except KeyError: raise Exception('This model has no control surfaces') except IndexError: diff --git a/sharpy/solvers/trim.py b/sharpy/solvers/trim.py index 4cf0a5a05..b8543f21e 100644 --- a/sharpy/solvers/trim.py +++ b/sharpy/solvers/trim.py @@ -291,7 +291,7 @@ def solver_wrapper(x, x_info, solver_data, i_dim=-1): tstep.quat[:] = orientation_quat # control surface deflection for i_cs in range(len(x_info['i_control_surfaces'])): - solver_data.data.aero.data_dict['control_surface_deflection'][x_info['control_surfaces_id'][i_cs]] = x[x_info['i_control_surfaces'][i_cs]] + solver_data.data.aero.aero_dict['control_surface_deflection'][x_info['control_surfaces_id'][i_cs]] = x[x_info['i_control_surfaces'][i_cs]] # thrust input tstep.steady_applied_forces[:] = 0.0 try: diff --git a/sharpy/utils/h5utils.py b/sharpy/utils/h5utils.py index 5f3549f83..927af014c 100644 --- a/sharpy/utils/h5utils.py +++ b/sharpy/utils/h5utils.py @@ -69,7 +69,7 @@ def check_fem_dict(fem_dict): print(' PASSED') -def check_data_dict(data_dict): +def check_aero_dict(aero_dict): pass diff --git a/sharpy/utils/multibody.py b/sharpy/utils/multibody.py index 6aa43f66f..b8f6f742f 100644 --- a/sharpy/utils/multibody.py +++ b/sharpy/utils/multibody.py @@ -13,7 +13,7 @@ import traceback -def split_multibody(beam, tstep, mb_data_dict, ts): +def split_multibody(beam, tstep, mb_aero_dict, ts): """ split_multibody @@ -22,7 +22,7 @@ def split_multibody(beam, tstep, mb_data_dict, ts): Args: beam (:class:`~sharpy.structure.models.beam.Beam`): structural information of the multibody system tstep (:class:`~sharpy.utils.datastructures.StructTimeStepInfo`): timestep information of the multibody system - mb_data_dict (dict): Dictionary including the multibody information + mb_aero_dict (dict): Dictionary including the multibody information ts (int): time step number Returns: @@ -47,7 +47,7 @@ def split_multibody(beam, tstep, mb_data_dict, ts): ibody_beam = beam.get_body(ibody = ibody) ibody_tstep = tstep.get_body(beam, ibody_beam.num_dof, ibody = ibody) - ibody_beam.FoR_movement = mb_data_dict['body_%02d' % ibody]['FoR_movement'] + ibody_beam.FoR_movement = mb_aero_dict['body_%02d' % ibody]['FoR_movement'] ibody_beam.ini_info.compute_psi_local_AFoR(ini_for0_pos, ini_for0_vel, ini_quat0) ibody_beam.ini_info.change_to_local_AFoR(ini_for0_pos, ini_for0_vel, ini_quat0) @@ -60,7 +60,7 @@ def split_multibody(beam, tstep, mb_data_dict, ts): return MB_beam, MB_tstep -def merge_multibody(MB_tstep, MB_beam, beam, tstep, mb_data_dict, dt): +def merge_multibody(MB_tstep, MB_beam, beam, tstep, mb_aero_dict, dt): """ merge_multibody @@ -73,7 +73,7 @@ def merge_multibody(MB_tstep, MB_beam, beam, tstep, mb_data_dict, dt): MB_tstep (list(:class:`~sharpy.utils.datastructures.StructTimeStepInfo`)): each entry represents a body beam (:class:`~sharpy.structure.models.beam.Beam`): structural information of the multibody system tstep (:class:`~sharpy.utils.datastructures.StructTimeStepInfo`): timestep information of the multibody system - mb_data_dict (dict): Dictionary including the multibody information + mb_aero_dict (dict): Dictionary including the multibody information dt(int): time step Returns: diff --git a/tests/linear/statespace/test_variable_tracker.py b/tests/linear/statespace/test_variable_tracker.py index 35d66cfe2..cbbc165aa 100644 --- a/tests/linear/statespace/test_variable_tracker.py +++ b/tests/linear/statespace/test_variable_tracker.py @@ -152,9 +152,9 @@ def test_save_to_h5(self): input_variables.add_to_h5_file(fid) with h5py.File(h5_file_name, 'r') as fid: - data_dict = h5utils.load_h5_in_dict(fid) + aero_dict = h5utils.load_h5_in_dict(fid) - loaded_variable = LinearVector.load_from_h5_file('InputVariable', data_dict['InputVariable']) + loaded_variable = LinearVector.load_from_h5_file('InputVariable', aero_dict['InputVariable']) LinearVector.check_same_vectors(input_variables, loaded_variable) From 7b1886b9d0f5e015b868d2a7951e4b985c56f3e4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stefanie=20D=C3=BCssler?= Date: Tue, 15 Aug 2023 06:13:07 -0400 Subject: [PATCH 167/232] fix remove accidentally comitted file --- run_static_trim.py | 19 ------------------- 1 file changed, 19 deletions(-) delete mode 100644 run_static_trim.py diff --git a/run_static_trim.py b/run_static_trim.py deleted file mode 100644 index 7db8e805f..000000000 --- a/run_static_trim.py +++ /dev/null @@ -1,19 +0,0 @@ -# -*- coding: utf-8 -*- -""" -Created on Mon Oct 12 06:30:29 2020 - -@author: sdues -""" - -import numpy as np -import sharpy -import sharpy.sharpy_main as sharpy_main - - -import sys -# insert at 1, 0 is the script path (or '' in REPL) -sys.path.insert(1, '../01_case_files/') -import generate_flex_op - -route_to_case = '../01_case_files/' -case_data = sharpy_main.main(['', route_to_case + 'flex_op_static_trim.sharpy']) From 368bf93377c088cd6535fdb3df7cca2ca3ea6a8a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stefanie=20D=C3=BCssler?= Date: Tue, 15 Aug 2023 05:10:48 -0400 Subject: [PATCH 168/232] Revert "rename data_dict to aero_dict" This reverts commit 4e4fcd03ae14240f3065dd4d35a5c561524948a9. --- .../example_notebooks/wind_turbine.ipynb | 2 +- sharpy/aero/models/aerogrid.py | 84 +++++++++---------- sharpy/aero/models/grid.py | 22 ++--- sharpy/aero/models/nonliftingbodygrid.py | 14 ++-- sharpy/aero/utils/mapping.py | 4 +- sharpy/generators/polaraeroforces.py | 20 ++--- .../assembler/lincontrolsurfacedeflector.py | 12 +-- sharpy/linear/src/libss.py | 42 +++++----- sharpy/postproc/liftdistribution.py | 4 +- sharpy/postproc/stallcheck.py | 2 +- sharpy/solvers/aerogridloader.py | 4 +- sharpy/solvers/beamloader.py | 30 +++---- sharpy/solvers/dynamiccoupled.py | 6 +- sharpy/solvers/gridloader.py | 6 +- sharpy/solvers/lindynamicsim.py | 8 +- sharpy/solvers/nonliftingbodygridloader.py | 4 +- sharpy/solvers/staticcoupled.py | 6 +- sharpy/solvers/trim.py | 2 +- sharpy/utils/h5utils.py | 2 +- sharpy/utils/multibody.py | 10 +-- .../statespace/test_variable_tracker.py | 4 +- 21 files changed, 144 insertions(+), 144 deletions(-) diff --git a/docs/source/content/example_notebooks/wind_turbine.ipynb b/docs/source/content/example_notebooks/wind_turbine.ipynb index be366343a..3d1722c5a 100644 --- a/docs/source/content/example_notebooks/wind_turbine.ipynb +++ b/docs/source/content/example_notebooks/wind_turbine.ipynb @@ -1010,7 +1010,7 @@ " inode_in_elem = sharpy_output.structure.node_master_elem[node_global_index, 1]\n", " CAB = algebra.crv2rotation(tstep.psi[ielem, inode_in_elem, :])\n", "\n", - " c[iblade][inode] = sharpy_output.aero.aero_dict['chord'][ielem,inode_in_elem]\n", + " c[iblade][inode] = sharpy_output.aero.data_dict['chord'][ielem,inode_in_elem]\n", "\n", " forces_AFoR = np.dot(CAB, forces[iblade][inode, 0:3])\n", "\n", diff --git a/sharpy/aero/models/aerogrid.py b/sharpy/aero/models/aerogrid.py index 5f263e7ec..8cd84e574 100644 --- a/sharpy/aero/models/aerogrid.py +++ b/sharpy/aero/models/aerogrid.py @@ -34,8 +34,8 @@ def __init__(self): self.cs_generators = [] - def generate(self, aero_dict, beam, settings, ts): - super().generate(aero_dict, beam, settings, ts) + def generate(self, data_dict, beam, settings, ts): + super().generate(data_dict, beam, settings, ts) # write grid info to screen self.output_info() @@ -49,10 +49,10 @@ def generate(self, aero_dict, beam, settings, ts): for i_elem in range(self.n_elem): for i_local_node in range(self.beam.num_node_elem): try: - self.airfoil_db[self.aero_dict['airfoil_distribution'][i_elem, i_local_node]] + self.airfoil_db[self.data_dict['airfoil_distribution'][i_elem, i_local_node]] except KeyError: - airfoil_coords = self.aero_dict['airfoils'][str(self.aero_dict['airfoil_distribution'][i_elem, i_local_node])] - self.airfoil_db[self.aero_dict['airfoil_distribution'][i_elem, i_local_node]] = ( + airfoil_coords = self.data_dict['airfoils'][str(self.data_dict['airfoil_distribution'][i_elem, i_local_node])] + self.airfoil_db[self.data_dict['airfoil_distribution'][i_elem, i_local_node]] = ( scipy.interpolate.interp1d(airfoil_coords[:, 0], airfoil_coords[:, 1], kind='quadratic', @@ -60,7 +60,7 @@ def generate(self, aero_dict, beam, settings, ts): fill_value='extrapolate', assume_sorted=True)) try: - self.n_control_surfaces = np.sum(np.unique(self.aero_dict['control_surface']) >= 0) + self.n_control_surfaces = np.sum(np.unique(self.data_dict['control_surface']) >= 0) except KeyError: pass @@ -85,7 +85,7 @@ def generate(self, aero_dict, beam, settings, ts): else: cout.cout_wrap('Initialising Control Surface {:g} generator'.format(i_cs), 1) # check that the control surface is not static - if self.aero_dict['control_surface_type'][i_cs] == 0: + if self.data_dict['control_surface_type'][i_cs] == 0: raise TypeError('Control surface {:g} is defined as static but there is a control surface generator' 'associated with it'.format(i_cs)) generator_type = gen_interface.generator_from_string( @@ -107,13 +107,13 @@ def generate(self, aero_dict, beam, settings, ts): self.generate_mapping() self.generate_zeta(self.beam, self.aero_settings, ts) - if 'polars' in self.aero_dict: + if 'polars' in self.data_dict: import sharpy.aero.utils.airfoilpolars as ap self.polars = [] - nairfoils = np.amax(self.aero_dict['airfoil_distribution']) + 1 + nairfoils = np.amax(self.data_dict['airfoil_distribution']) + 1 for iairfoil in range(nairfoils): new_polar = ap.Polar() - new_polar.initialise(aero_dict['polars'][str(iairfoil)]) + new_polar.initialise(data_dict['polars'][str(iairfoil)]) self.polars.append(new_polar) def output_info(self): @@ -149,24 +149,24 @@ def generate_zeta_timestep_info(self, structure_tstep, aero_tstep, beam, setting # check that we have control surface information try: - self.aero_dict['control_surface'] + self.data_dict['control_surface'] with_control_surfaces = True except KeyError: with_control_surfaces = False # check that we have sweep information try: - self.aero_dict['sweep'] + self.data_dict['sweep'] except KeyError: - self.aero_dict['sweep'] = np.zeros_like(self.aero_dict['twist']) + self.data_dict['sweep'] = np.zeros_like(self.data_dict['twist']) # Define first_twist for backwards compatibility - if 'first_twist' not in self.aero_dict: - self.aero_dict['first_twist'] = [True]*self.aero_dict['surface_m'].shape[0] + if 'first_twist' not in self.data_dict: + self.data_dict['first_twist'] = [True]*self.data_dict['surface_m'].shape[0] # one surface per element for i_elem in range(self.n_elem): - i_surf = self.aero_dict['surface_distribution'][i_elem] + i_surf = self.data_dict['surface_distribution'][i_elem] # check if we have to generate a surface here if i_surf == -1: continue @@ -175,7 +175,7 @@ def generate_zeta_timestep_info(self, structure_tstep, aero_tstep, beam, setting i_global_node = self.beam.elements[i_elem].global_connectivities[i_local_node] # i_global_node = self.beam.elements[i_elem].global_connectivities[ # self.beam.elements[i_elem].ordering[i_local_node]] - if not self.aero_dict['aero_node'][i_global_node]: + if not self.data_dict['aero_node'][i_global_node]: continue if i_global_node in global_node_in_surface[i_surf]: continue @@ -203,23 +203,23 @@ def generate_zeta_timestep_info(self, structure_tstep, aero_tstep, beam, setting control_surface_info = None if with_control_surfaces: # 1) check that this node and elem have a control surface - if self.aero_dict['control_surface'][i_elem, i_local_node] >= 0: - i_control_surface = self.aero_dict['control_surface'][i_elem, i_local_node] + if self.data_dict['control_surface'][i_elem, i_local_node] >= 0: + i_control_surface = self.data_dict['control_surface'][i_elem, i_local_node] # 2) type of control surface + write info control_surface_info = dict() - if self.aero_dict['control_surface_type'][i_control_surface] == 0: + if self.data_dict['control_surface_type'][i_control_surface] == 0: control_surface_info['type'] = 'static' - control_surface_info['deflection'] = self.aero_dict['control_surface_deflection'][i_control_surface] - control_surface_info['chord'] = self.aero_dict['control_surface_chord'][i_control_surface] + control_surface_info['deflection'] = self.data_dict['control_surface_deflection'][i_control_surface] + control_surface_info['chord'] = self.data_dict['control_surface_chord'][i_control_surface] try: - control_surface_info['hinge_coords'] = self.aero_dict['control_surface_hinge_coords'][i_control_surface] + control_surface_info['hinge_coords'] = self.data_dict['control_surface_hinge_coords'][i_control_surface] except KeyError: control_surface_info['hinge_coords'] = None - elif self.aero_dict['control_surface_type'][i_control_surface] == 1: + elif self.data_dict['control_surface_type'][i_control_surface] == 1: control_surface_info['type'] = 'dynamic' - control_surface_info['chord'] = self.aero_dict['control_surface_chord'][i_control_surface] + control_surface_info['chord'] = self.data_dict['control_surface_chord'][i_control_surface] try: - control_surface_info['hinge_coords'] = self.aero_dict['control_surface_hinge_coords'][i_control_surface] + control_surface_info['hinge_coords'] = self.data_dict['control_surface_hinge_coords'][i_control_surface] except KeyError: control_surface_info['hinge_coords'] = None @@ -227,7 +227,7 @@ def generate_zeta_timestep_info(self, structure_tstep, aero_tstep, beam, setting control_surface_info['deflection'], control_surface_info['deflection_dot'] = \ self.cs_generators[i_control_surface](params) - elif self.aero_dict['control_surface_type'][i_control_surface] == 2: + elif self.data_dict['control_surface_type'][i_control_surface] == 2: control_surface_info['type'] = 'controlled' try: @@ -236,12 +236,12 @@ def generate_zeta_timestep_info(self, structure_tstep, aero_tstep, beam, setting try: old_deflection = aero_tstep.control_surface_deflection[i_control_surface] except IndexError: - old_deflection = self.aero_dict['control_surface_deflection'][i_control_surface] + old_deflection = self.data_dict['control_surface_deflection'][i_control_surface] try: control_surface_info['deflection'] = aero_tstep.control_surface_deflection[i_control_surface] except IndexError: - control_surface_info['deflection'] = self.aero_dict['control_surface_deflection'][i_control_surface] + control_surface_info['deflection'] = self.data_dict['control_surface_deflection'][i_control_surface] if dt is not None: control_surface_info['deflection_dot'] = ( @@ -249,14 +249,14 @@ def generate_zeta_timestep_info(self, structure_tstep, aero_tstep, beam, setting else: control_surface_info['deflection_dot'] = 0.0 - control_surface_info['chord'] = self.aero_dict['control_surface_chord'][i_control_surface] + control_surface_info['chord'] = self.data_dict['control_surface_chord'][i_control_surface] try: - control_surface_info['hinge_coords'] = self.aero_dict['control_surface_hinge_coords'][i_control_surface] + control_surface_info['hinge_coords'] = self.data_dict['control_surface_hinge_coords'][i_control_surface] except KeyError: control_surface_info['hinge_coords'] = None else: - raise NotImplementedError(str(self.aero_dict['control_surface_type'][i_control_surface]) + + raise NotImplementedError(str(self.data_dict['control_surface_type'][i_control_surface]) + ' control surfaces are not yet implemented') @@ -264,13 +264,13 @@ def generate_zeta_timestep_info(self, structure_tstep, aero_tstep, beam, setting node_info = dict() node_info['i_node'] = i_global_node node_info['i_local_node'] = i_local_node - node_info['chord'] = self.aero_dict['chord'][i_elem, i_local_node] - node_info['eaxis'] = self.aero_dict['elastic_axis'][i_elem, i_local_node] - node_info['twist'] = self.aero_dict['twist'][i_elem, i_local_node] - node_info['sweep'] = self.aero_dict['sweep'][i_elem, i_local_node] + node_info['chord'] = self.data_dict['chord'][i_elem, i_local_node] + node_info['eaxis'] = self.data_dict['elastic_axis'][i_elem, i_local_node] + node_info['twist'] = self.data_dict['twist'][i_elem, i_local_node] + node_info['sweep'] = self.data_dict['sweep'][i_elem, i_local_node] node_info['M'] = self.dimensions[i_surf, 0] - node_info['M_distribution'] = self.aero_dict['m_distribution'].decode('ascii') - node_info['airfoil'] = self.aero_dict['airfoil_distribution'][i_elem, i_local_node] + node_info['M_distribution'] = self.data_dict['m_distribution'].decode('ascii') + node_info['airfoil'] = self.data_dict['airfoil_distribution'][i_elem, i_local_node] node_info['control_surface'] = control_surface_info node_info['beam_coord'] = structure_tstep.pos[i_global_node, :] node_info['pos_dot'] = structure_tstep.pos_dot[i_global_node, :] @@ -282,7 +282,7 @@ def generate_zeta_timestep_info(self, structure_tstep, aero_tstep, beam, setting node_info['cga'] = structure_tstep.cga() if node_info['M_distribution'].lower() == 'user_defined': ielem_in_surf = i_elem - np.sum(self.surface_distribution < i_surf) - node_info['user_defined_m_distribution'] = self.aero_dict['user_defined_m_distribution'][str(i_surf)][:, ielem_in_surf, i_local_node] + node_info['user_defined_m_distribution'] = self.data_dict['user_defined_m_distribution'][str(i_surf)][:, ielem_in_surf, i_local_node] (aero_tstep.zeta[i_surf][:, :, i_n], aero_tstep.zeta_dot[i_surf][:, :, i_n]) = ( generate_strip(node_info, @@ -291,14 +291,14 @@ def generate_zeta_timestep_info(self, structure_tstep, aero_tstep, beam, setting orientation_in=self.aero_settings['freestream_dir'], calculate_zeta_dot=True)) # set junction boundary conditions for later phantom cell creation in UVLM - if "junction_boundary_condition" in self.aero_dict: - if np.any(self.aero_dict["junction_boundary_condition"] >= 0): + if "junction_boundary_condition" in self.data_dict: + if np.any(self.data_dict["junction_boundary_condition"] >= 0): self.generate_phantom_panels_at_junction(aero_tstep) def generate_phantom_panels_at_junction(self, aero_tstep): for i_surf in range(self.n_surf): - aero_tstep.flag_zeta_phantom[0, i_surf] = self.aero_dict["junction_boundary_condition"][0,i_surf] + aero_tstep.flag_zeta_phantom[0, i_surf] = self.data_dict["junction_boundary_condition"][0,i_surf] diff --git a/sharpy/aero/models/grid.py b/sharpy/aero/models/grid.py index d50af7748..176a7a5e6 100644 --- a/sharpy/aero/models/grid.py +++ b/sharpy/aero/models/grid.py @@ -20,7 +20,7 @@ class Grid(object): """ def __init__(self): - self.aero_dict = None + self.data_dict = None self.beam = None self.aero_settings = None self.timestep_info = [] @@ -41,27 +41,27 @@ def __init__(self): self.aero2struct_mapping = [] - def generate(self, aero_dict, beam, aero_settings, ts): + def generate(self, data_dict, beam, aero_settings, ts): - self.aero_dict = aero_dict + self.data_dict = data_dict self.beam = beam self.aero_settings = aero_settings # key words = safe in aero_settings? --> grid_type # number of total nodes (structural + aero&struc) - self.n_node = len(aero_dict[self.grid_type + '_node']) # gridtype + '_node' + self.n_node = len(data_dict[self.grid_type + '_node']) # gridtype + '_node' # number of elements - self.n_elem = len(aero_dict['surface_distribution']) + self.n_elem = len(data_dict['surface_distribution']) # surface distribution - self.surface_distribution = aero_dict['surface_distribution'] + self.surface_distribution = data_dict['surface_distribution'] # number of surfaces - temp = set(aero_dict['surface_distribution']) + temp = set(data_dict['surface_distribution']) #TO-DO: improve: avoid for loops self.n_surf = sum(1 for i in temp if i >= 0) # number of chordwise panels - self.surface_m = aero_dict['surface_m'] + self.surface_m = data_dict['surface_m'] # number of aero nodes - self.n_aero_node = sum(aero_dict[self.grid_type + '_node']) + self.n_aero_node = sum(data_dict[self.grid_type + '_node']) # get N per surface @@ -96,7 +96,7 @@ def calculate_dimensions(self): continue else: nodes_in_surface[i_surf].append(i_global_node) - if self.aero_dict[self.grid_type + '_node'][i_global_node]: + if self.data_dict[self.grid_type + '_node'][i_global_node]: self.dimensions[i_surf, 1] += 1 # accounting for N+1 nodes -> N panels @@ -131,7 +131,7 @@ def generate_mapping(self): if i_surf == -1: continue for i_global_node in self.beam.elements[i_elem].reordered_global_connectivities: - if not self.aero_dict[self.grid_type + '_node'][i_global_node]: + if not self.data_dict[self.grid_type + '_node'][i_global_node]: continue if i_global_node in nodes_in_surface[i_surf]: diff --git a/sharpy/aero/models/nonliftingbodygrid.py b/sharpy/aero/models/nonliftingbodygrid.py index d21a211cb..465b87d68 100644 --- a/sharpy/aero/models/nonliftingbodygrid.py +++ b/sharpy/aero/models/nonliftingbodygrid.py @@ -22,8 +22,8 @@ def __init__(self): self.grid_type = 'nonlifting_body' - def generate(self, aero_dict, beam, nonlifting_body_settings, ts): ##input? - super().generate(aero_dict, beam, nonlifting_body_settings, ts) + def generate(self, data_dict, beam, nonlifting_body_settings, ts): ##input? + super().generate(data_dict, beam, nonlifting_body_settings, ts) # allocating initial grid storage self.ini_info = NonliftingBodyTimeStepInfo(self.dimensions) @@ -53,10 +53,10 @@ def get_zeta_and_zeta_dot(self,i_surf, structure_tstep): for node_counter, i_global_node in enumerate(self.aero2struct_mapping[i_surf]): # TODO: Adjust for ellipse input - if self.aero_dict["shape"] == 'specific': - a_ellipse = self.aero_dict["a_ellipse"][i_global_node] - b_ellipse = self.aero_dict["b_ellipse"][i_global_node] - z_0 =self.aero_dict["z_0_ellipse"][i_global_node] + if self.data_dict["shape"] == 'specific': + a_ellipse = self.data_dict["a_ellipse"][i_global_node] + b_ellipse = self.data_dict["b_ellipse"][i_global_node] + z_0 =self.data_dict["z_0_ellipse"][i_global_node] if a_ellipse == 0. or b_ellipse == 0.: radius = 0 else: @@ -64,7 +64,7 @@ def get_zeta_and_zeta_dot(self,i_surf, structure_tstep): (b_ellipse*array_cos_phi)**2 +(a_ellipse*array_sin_phi)**2) else: - radius = self.aero_dict["radius"][i_global_node] + radius = self.data_dict["radius"][i_global_node] z_0 = 0 diff --git a/sharpy/aero/utils/mapping.py b/sharpy/aero/utils/mapping.py index 736d6abb6..c7792ef3d 100644 --- a/sharpy/aero/utils/mapping.py +++ b/sharpy/aero/utils/mapping.py @@ -11,7 +11,7 @@ def aero2struct_force_mapping(aero_forces, master, conn, cag=np.eye(3), - aero_dict=None, + data_dict=None, skip_moments_generated_by_forces = False): r""" Maps the aerodynamic forces at the lattice to the structural nodes @@ -40,7 +40,7 @@ def aero2struct_force_mapping(aero_forces, master: Unused conn (np.ndarray): Connectivities matrix cag (np.ndarray): Transformation matrix between inertial and body-attached reference ``A`` - aero_dict (dict): Dictionary containing the grid's information. + data_dict (dict): Dictionary containing the grid's information. Returns: np.ndarray: structural forces in an ``n_node x 6`` vector diff --git a/sharpy/generators/polaraeroforces.py b/sharpy/generators/polaraeroforces.py index c3e471cf7..b94c368d7 100644 --- a/sharpy/generators/polaraeroforces.py +++ b/sharpy/generators/polaraeroforces.py @@ -155,7 +155,7 @@ def generate(self, **params): list_aoa_induced = [] - aero_dict = aerogrid.aero_dict + data_dict = aerogrid.data_dict if aerogrid.polars is None: return struct_forces new_struct_forces = np.zeros_like(struct_forces) @@ -168,9 +168,9 @@ def generate(self, **params): for inode in range(nnode): new_struct_forces[inode, :] = struct_forces[inode, :].copy() - if aero_dict['aero_node'][inode]: + if data_dict['aero_node'][inode]: ielem, inode_in_elem = structure.node_master_elem[inode] - iairfoil = aero_dict['airfoil_distribution'][ielem, inode_in_elem] + iairfoil = data_dict['airfoil_distribution'][ielem, inode_in_elem] isurf = aerogrid.struct2aero_mapping[inode][0]['i_surf'] if isurf not in self.settings['skip_surfaces']: i_n = aerogrid.struct2aero_mapping[inode][0]['i_n'] @@ -179,7 +179,7 @@ def generate(self, **params): cgb = np.dot(cga, cab) if not self.cd_from_cl: - airfoil = str(aero_dict['airfoil_distribution'][ielem, inode_in_elem]) + airfoil = str(data_dict['airfoil_distribution'][ielem, inode_in_elem]) aoa_0cl = self.list_aoa_cl0[int(airfoil)] # computing surface area of panels contributing to force @@ -317,7 +317,7 @@ def check_for_special_cases(self, aerogrid): # check if outboard node of aerosurface self.flag_shared_node_by_surfaces = np.zeros((self.n_node,1)) for inode in range(self.n_node): - if aerogrid.aero_dict['aero_node'][inode]: + if aerogrid.data_dict['aero_node'][inode]: i_n = aerogrid.struct2aero_mapping[inode][0]['i_n'] isurf = aerogrid.struct2aero_mapping[inode][0]['i_surf'] N = aerogrid.dimensions[isurf, 1] @@ -350,9 +350,9 @@ def compute_aoa_cl0_from_airfoil_data(self, aerogrid): """ Computes the angle of attack for which zero lift is achieved for every airfoil """ - self.list_aoa_cl0 = np.zeros((len(aerogrid.aero_dict['airfoils']),1)) - for i, airfoil in enumerate(aerogrid.aero_dict['airfoils']): - airfoil_coords = aerogrid.aero_dict['airfoils'][airfoil] + self.list_aoa_cl0 = np.zeros((len(aerogrid.data_dict['airfoils']),1)) + for i, airfoil in enumerate(aerogrid.data_dict['airfoils']): + airfoil_coords = aerogrid.data_dict['airfoils'][airfoil] self.list_aoa_cl0[i] = get_aoacl0_from_camber(airfoil_coords[:, 0], airfoil_coords[:, 1]) @generator_interface.generator @@ -401,11 +401,11 @@ def generate(self, **params): n_node = self.structure.num_node n_elem = self.structure.num_elem - aero_dict = self.aero.aero_dict + data_dict = self.aero.data_dict new_struct_forces = np.zeros_like(struct_forces) # load airfoil efficiency (if it exists); else set to one (to avoid multiple ifs in the loops) - airfoil_efficiency = aero_dict['airfoil_efficiency'] + airfoil_efficiency = data_dict['airfoil_efficiency'] # force efficiency dimensions [n_elem, n_node_elem, 2, [fx, fy, fz]] - all defined in B frame force_efficiency = np.zeros((n_elem, 3, 2, 3)) force_efficiency[:, :, 0, :] = 1. diff --git a/sharpy/linear/assembler/lincontrolsurfacedeflector.py b/sharpy/linear/assembler/lincontrolsurfacedeflector.py index e1c2007dc..2f99fda4b 100644 --- a/sharpy/linear/assembler/lincontrolsurfacedeflector.py +++ b/sharpy/linear/assembler/lincontrolsurfacedeflector.py @@ -82,7 +82,7 @@ def generate(self): tsstruct0 = self.tsstruct0 # Find the vertices corresponding to a control surface from beam coordinates to aerogrid - aero_dict = aero.aero_dict + data_dict = aero.data_dict n_surf = tsaero0.n_surf n_control_surfaces = self.n_control_surfaces @@ -120,7 +120,7 @@ def generate(self): # Although a node may be part of 2 aerodynamic surfaces, we need to ensure that the current # element for the given node is indeed part of that surface. - elems_in_surf = np.where(aero_dict['surface_distribution'] == i_surf)[0] + elems_in_surf = np.where(data_dict['surface_distribution'] == i_surf)[0] if i_elem not in elems_in_surf: continue @@ -131,18 +131,18 @@ def generate(self): K_zeta_start = 3 * sum(linuvlm.MS.KKzeta[:i_surf]) shape_zeta = (3, M + 1, N + 1) - i_control_surface = aero_dict['control_surface'][i_elem, i_local_node] + i_control_surface = data_dict['control_surface'][i_elem, i_local_node] if i_control_surface >= 0: if not with_control_surface: i_start_of_cs = i_node_span.copy() with_control_surface = True - control_surface_chord = aero_dict['control_surface_chord'][i_control_surface] + control_surface_chord = data_dict['control_surface_chord'][i_control_surface] try: control_surface_hinge_coord = \ - aero_dict['control_surface_hinge_coord'][i_control_surface] * \ - aero_dict['chord'][i_elem, i_local_node] + data_dict['control_surface_hinge_coord'][i_control_surface] * \ + data_dict['chord'][i_elem, i_local_node] except KeyError: control_surface_hinge_coord = None diff --git a/sharpy/linear/src/libss.py b/sharpy/linear/src/libss.py index c2f4ad082..7d5736a59 100644 --- a/sharpy/linear/src/libss.py +++ b/sharpy/linear/src/libss.py @@ -428,22 +428,22 @@ def load_from_h5(cls, h5_file_name): """ with h5py.File(h5_file_name, 'r') as f: - aero_dict = h5utils.load_h5_in_dict(f) + data_dict = h5utils.load_h5_in_dict(f) - new_ss = cls(aero_dict['a'], - aero_dict['b'], - aero_dict['c'], - aero_dict['d'], - dt=aero_dict.get('dt')) + new_ss = cls(data_dict['a'], + data_dict['b'], + data_dict['c'], + data_dict['d'], + dt=data_dict.get('dt')) - input_variables = aero_dict.get('InputVariable') + input_variables = data_dict.get('InputVariable') if input_variables is not None: new_ss.input_variables = LinearVector.load_from_h5_file('InputVariable', - aero_dict['InputVariable']) + data_dict['InputVariable']) new_ss.output_variables = LinearVector.load_from_h5_file('OutputVariable', - aero_dict['OutputVariable']) + data_dict['OutputVariable']) new_ss.state_variables = LinearVector.load_from_h5_file('StateVariable', - aero_dict['StateVariable']) + data_dict['StateVariable']) return new_ss else: @@ -692,35 +692,35 @@ def load_from_h5(cls, h5_file_name): Gain: instance of a Gain """ with h5py.File(h5_file_name, 'r') as f: - aero_dict = h5utils.load_h5_in_dict(f) + data_dict = h5utils.load_h5_in_dict(f) - return cls.load_from_dict(aero_dict) + return cls.load_from_dict(data_dict) @classmethod - def load_from_dict(cls, aero_dict): + def load_from_dict(cls, data_dict): """ Returns a Gain from a dictionary of data, useful for loading from a group of gains in a single .h5 file Args: - aero_dict (dict): Dictionary with keys: ``gain`` and (if available) ``InputVariable`` + data_dict (dict): Dictionary with keys: ``gain`` and (if available) ``InputVariable`` and ``OutputVariable``. Returns: Gain: instance of Gain """ - input_variables = aero_dict.get('InputVariable') + input_variables = data_dict.get('InputVariable') if input_variables is not None: input_variables = LinearVector.load_from_h5_file('InputVariable', - aero_dict['InputVariable']) + data_dict['InputVariable']) output_variables = LinearVector.load_from_h5_file('OutputVariable', - aero_dict['OutputVariable']) + data_dict['OutputVariable']) - return cls(aero_dict['gain'], input_vars=input_variables, + return cls(data_dict['gain'], input_vars=input_variables, output_vars=output_variables) else: - return cls(aero_dict['gain']) + return cls(data_dict['gain']) @classmethod def save_multiple_gains(cls, h5_file_name, *gains_names_tuple): @@ -749,10 +749,10 @@ def load_multiple_gains(cls, h5_file_name): dict: Dictionary of loaded gains in a gain_name: Gain dictionary """ with h5py.File(h5_file_name, 'r') as f: - aero_dict = h5utils.load_h5_in_dict(f) + data_dict = h5utils.load_h5_in_dict(f) out_gains = {} - for gain_name, gain_data in aero_dict.items(): + for gain_name, gain_data in data_dict.items(): out_gains[gain_name] = cls.load_from_dict(gain_data) return out_gains diff --git a/sharpy/postproc/liftdistribution.py b/sharpy/postproc/liftdistribution.py index f660a5af6..dadda480c 100644 --- a/sharpy/postproc/liftdistribution.py +++ b/sharpy/postproc/liftdistribution.py @@ -69,7 +69,7 @@ def lift_distribution(self, struct_tstep, aero_tstep): self.data.structure.node_master_elem, self.data.structure.connectivities, struct_tstep.cag(), - self.data.aero.aero_dict) + self.data.aero.data_dict) # Prepare output matrix and file N_nodes = self.data.structure.num_node numb_col = 6 @@ -84,7 +84,7 @@ def lift_distribution(self, struct_tstep, aero_tstep): lift_distribution = np.zeros((N_nodes, numb_col)) for inode in range(N_nodes): - if self.data.aero.aero_dict['aero_node'][inode]: + if self.data.aero.data_dict['aero_node'][inode]: local_node = self.data.aero.struct2aero_mapping[inode][0]["i_n"] ielem, inode_in_elem = self.data.structure.node_master_elem[inode] i_surf = int(self.data.aero.surface_distribution[ielem]) diff --git a/sharpy/postproc/stallcheck.py b/sharpy/postproc/stallcheck.py index e75fbff29..690cfe43e 100644 --- a/sharpy/postproc/stallcheck.py +++ b/sharpy/postproc/stallcheck.py @@ -103,7 +103,7 @@ def check_stall(self): for i_elem in range(self.data.structure.num_elem): for i_local_node in range(self.data.structure.num_node_elem): - airfoil_id = self.data.aero.aero_dict['airfoil_distribution'][i_elem, i_local_node] + airfoil_id = self.data.aero.data_dict['airfoil_distribution'][i_elem, i_local_node] if self.settings['airfoil_stall_angles']: i_global_node = self.data.structure.connectivities[i_elem, i_local_node] for i_dict in self.data.aero.struct2aero_mapping[i_global_node]: diff --git a/sharpy/solvers/aerogridloader.py b/sharpy/solvers/aerogridloader.py index 801994eef..84110f475 100644 --- a/sharpy/solvers/aerogridloader.py +++ b/sharpy/solvers/aerogridloader.py @@ -39,7 +39,7 @@ class AerogridLoader(GridLoader): data (ProblemData): class structure file_name (str): name of the ``.aero.h5`` HDF5 file aero: empty attribute - aero_dict (dict): key-value pairs of aerodynamic data + data_dict (dict): key-value pairs of aerodynamic data wake_shape_generator (class): Wake shape generator """ @@ -107,7 +107,7 @@ def initialise(self, data, restart=False): def run(self): self.data.aero = aerogrid.Aerogrid() - self.data.aero.generate(self.aero_dict, + self.data.aero.generate(self.data_dict, self.data.structure, self.settings, self.data.ts) diff --git a/sharpy/solvers/beamloader.py b/sharpy/solvers/beamloader.py index 97b1e8dcd..a30ee6086 100644 --- a/sharpy/solvers/beamloader.py +++ b/sharpy/solvers/beamloader.py @@ -25,8 +25,8 @@ class BeamLoader(BaseSolver): data (ProblemData): class containing the data for the problem fem_file_name (str): name of the ``.fem.h5`` HDF5 file dyn_file_name (str): name of the ``.dyn.h5`` HDF5 file - fem_aero_dict (dict): key-value pairs of FEM data - dyn_aero_dict (dict): key-value pairs of data for dynamic problems + fem_data_dict (dict): key-value pairs of FEM data + dyn_data_dict (dict): key-value pairs of data for dynamic problems structure (None): Empty attribute Notes: @@ -68,9 +68,9 @@ def __init__(self): self.fem_file_name = '' self.dyn_file_name = '' # storage of file contents - self.fem_aero_dict = dict() - self.dyn_aero_dict = dict() - self.mb_aero_dict = dict() + self.fem_data_dict = dict() + self.dyn_data_dict = dict() + self.mb_data_dict = dict() # structure storage self.structure = None @@ -101,13 +101,13 @@ def read_files(self): # read and store the hdf5 files with h5.File(self.fem_file_name, 'r') as fem_file_handle: # store files in dictionary - self.fem_aero_dict = h5utils.load_h5_in_dict(fem_file_handle) + self.fem_data_dict = h5utils.load_h5_in_dict(fem_file_handle) # TODO implement fem file validation # self.validate_fem_file() if self.settings['unsteady']: with h5.File(self.dyn_file_name, 'r') as dyn_file_handle: # store files in dictionary - self.dyn_aero_dict = h5utils.load_h5_in_dict(dyn_file_handle) + self.dyn_data_dict = h5utils.load_h5_in_dict(dyn_file_handle) # TODO implement dyn file validation # self.validate_dyn_file() @@ -116,13 +116,13 @@ def read_files(self): if os.path.isfile(self.mb_file_name): # h5utils.check_file_exists(self.mb_file_name) with h5.File(self.mb_file_name, 'r') as mb_file_handle: - self.mb_aero_dict = h5utils.load_h5_in_dict(mb_file_handle) + self.mb_data_dict = h5utils.load_h5_in_dict(mb_file_handle) # Need to redefine strings to remove the "b" at the beginning - for iconstraint in range(self.mb_aero_dict['num_constraints']): - self.mb_aero_dict["constraint_%02d" % iconstraint]['behaviour'] = self.mb_aero_dict["constraint_%02d" % iconstraint]['behaviour'].decode() - for ibody in range(self.mb_aero_dict['num_bodies']): - self.mb_aero_dict["body_%02d" % ibody]['FoR_movement'] = self.mb_aero_dict["body_%02d" % ibody]['FoR_movement'].decode() + for iconstraint in range(self.mb_data_dict['num_constraints']): + self.mb_data_dict["constraint_%02d" % iconstraint]['behaviour'] = self.mb_data_dict["constraint_%02d" % iconstraint]['behaviour'].decode() + for ibody in range(self.mb_data_dict['num_bodies']): + self.mb_data_dict["body_%02d" % ibody]['FoR_movement'] = self.mb_data_dict["body_%02d" % ibody]['FoR_movement'].decode() def validate_fem_file(self): raise NotImplementedError('validation of the fem file in beamloader is not yet implemented!') @@ -132,9 +132,9 @@ def validate_dyn_file(self): def run(self, **kwargs): self.data.structure = beam.Beam() - self.data.structure.ini_mb_dict = self.mb_aero_dict - self.data.structure.generate(self.fem_aero_dict, self.settings) - self.data.structure.dyn_dict = self.dyn_aero_dict + self.data.structure.ini_mb_dict = self.mb_data_dict + self.data.structure.generate(self.fem_data_dict, self.settings) + self.data.structure.dyn_dict = self.dyn_data_dict # Change the beam description to the local FoR for multibody # if (self.data.structure.num_bodies > 1): diff --git a/sharpy/solvers/dynamiccoupled.py b/sharpy/solvers/dynamiccoupled.py index 9fdb61324..719a58380 100644 --- a/sharpy/solvers/dynamiccoupled.py +++ b/sharpy/solvers/dynamiccoupled.py @@ -826,7 +826,7 @@ def map_forces(self, aero_kstep, structural_kstep, nl_body_kstep = None, unstead self.data.structure.node_master_elem, self.data.structure.connectivities, structural_kstep.cag(), - self.data.aero.aero_dict) + self.data.aero.data_dict) dynamic_struct_forces = unsteady_forces_coeff*mapping.aero2struct_force_mapping( aero_kstep.dynamic_forces, self.data.aero.struct2aero_mapping, @@ -836,7 +836,7 @@ def map_forces(self, aero_kstep, structural_kstep, nl_body_kstep = None, unstead self.data.structure.node_master_elem, self.data.structure.connectivities, structural_kstep.cag(), - self.data.aero.aero_dict) + self.data.aero.data_dict) if self.correct_forces: struct_forces = \ @@ -859,7 +859,7 @@ def map_forces(self, aero_kstep, structural_kstep, nl_body_kstep = None, unstead # self.data.structure.node_master_elem, # self.data.structure.connectivities, # structural_kstep.cag(), - # self.data.nonlifting_body.aero_dict) + # self.data.nonlifting_body.data_dict) # prescribed forces + aero forces # prescribed forces + aero forces + runtime generated structural_kstep.steady_applied_forces += struct_forces diff --git a/sharpy/solvers/gridloader.py b/sharpy/solvers/gridloader.py index 610b16d50..ee5803faa 100644 --- a/sharpy/solvers/gridloader.py +++ b/sharpy/solvers/gridloader.py @@ -24,7 +24,7 @@ class GridLoader(BaseSolver): data (ProblemData): class structure afile_name (str): name of the HDF5 file, e.g. ``.aero.h5`` aero: empty attribute - aero_dict (dict): key-value pairs of aerodynamic data + data_dict (dict): key-value pairs of aerodynamic data """ solver_id = 'GridLoader' @@ -39,7 +39,7 @@ def __init__(self): self.data = None self.settings = None self.file_name = '' - self.aero_dict = dict() + self.data_dict = dict() def initialise(self, data): self.data = data @@ -61,4 +61,4 @@ def read_input_files(self): # read and store the hdf5 file in dictionary with h5.File(self.file_name, 'r') as file_handle: - self.aero_dict = h5utils.load_h5_in_dict(file_handle) \ No newline at end of file + self.data_dict = h5utils.load_h5_in_dict(file_handle) \ No newline at end of file diff --git a/sharpy/solvers/lindynamicsim.py b/sharpy/solvers/lindynamicsim.py index e4a52375a..8538e0bab 100644 --- a/sharpy/solvers/lindynamicsim.py +++ b/sharpy/solvers/lindynamicsim.py @@ -83,7 +83,7 @@ def __init__(self): self.postprocessors = dict() self.with_postprocessors = False - self.input_aero_dict = dict() + self.input_data_dict = dict() self.input_file_name = "" self.folder = None @@ -154,11 +154,11 @@ def run(self, **kwargs): ss = self.data.linear.ss n_steps = self.settings['n_tsteps'] - x0 = self.input_aero_dict.get('x0', np.zeros(ss.states)) + x0 = self.input_data_dict.get('x0', np.zeros(ss.states)) if len(self.settings['input_generators']) != 0: u = self.input_vector(ss) else: - u = self.input_aero_dict['u'] + u = self.input_data_dict['u'] if len(x0) != ss.states: warnings.warn('Number of states in the initial state vector not equal to the number of states') @@ -254,7 +254,7 @@ def read_files(self): h5utils.check_file_exists(self.input_file_name) # Read and store with h5.File(self.input_file_name, 'r') as input_file_handle: - self.input_aero_dict = h5utils.load_h5_in_dict(input_file_handle) + self.input_data_dict = h5utils.load_h5_in_dict(input_file_handle) except FileNotFoundError: pass diff --git a/sharpy/solvers/nonliftingbodygridloader.py b/sharpy/solvers/nonliftingbodygridloader.py index 0b6ce89dc..75a73d74d 100644 --- a/sharpy/solvers/nonliftingbodygridloader.py +++ b/sharpy/solvers/nonliftingbodygridloader.py @@ -21,7 +21,7 @@ class NonliftingbodygridLoader(GridLoader): data (ProblemData): class structure file_name (str): name of the ``.nonlifting_body.h5`` HDF5 file aero: empty attribute - aero_dict (dict): key-value pairs of aerodynamic data + aero_data_dict (dict): key-value pairs of aerodynamic data """ @@ -37,7 +37,7 @@ def __init__(self): def run(self): self.data.nonlifting_body = nonliftingbodygrid.NonliftingBodyGrid() - self.data.nonlifting_body.generate(self.aero_dict, + self.data.nonlifting_body.generate(self.data_dict, self.data.structure, self.settings, self.data.ts) diff --git a/sharpy/solvers/staticcoupled.py b/sharpy/solvers/staticcoupled.py index c2cda6110..e3fde1bae 100644 --- a/sharpy/solvers/staticcoupled.py +++ b/sharpy/solvers/staticcoupled.py @@ -196,7 +196,7 @@ def run(self, **kwargs): self.data.structure.node_master_elem, self.data.structure.connectivities, self.data.structure.timestep_info[self.data.ts].cag(), - self.data.aero.aero_dict) + self.data.aero.data_dict) if self.correct_forces: struct_forces = \ @@ -216,7 +216,7 @@ def run(self, **kwargs): self.data.structure.node_master_elem, self.data.structure.connectivities, self.data.structure.timestep_info[self.data.ts].cag(), - self.data.nonlifting_body.aero_dict, + self.data.nonlifting_body.data_dict, skip_moments_generated_by_forces = True) self.data.aero.timestep_info[self.data.ts].aero_steady_forces_beam_dof = struct_forces @@ -365,7 +365,7 @@ def change_trim(self, alpha, thrust, thrust_nodes, tail_deflection, tail_cs_inde # tail deflection try: - self.data.aero.aero_dict['control_surface_deflection'][tail_cs_index] = tail_deflection + self.data.aero.data_dict['control_surface_deflection'][tail_cs_index] = tail_deflection except KeyError: raise Exception('This model has no control surfaces') except IndexError: diff --git a/sharpy/solvers/trim.py b/sharpy/solvers/trim.py index b8543f21e..4cf0a5a05 100644 --- a/sharpy/solvers/trim.py +++ b/sharpy/solvers/trim.py @@ -291,7 +291,7 @@ def solver_wrapper(x, x_info, solver_data, i_dim=-1): tstep.quat[:] = orientation_quat # control surface deflection for i_cs in range(len(x_info['i_control_surfaces'])): - solver_data.data.aero.aero_dict['control_surface_deflection'][x_info['control_surfaces_id'][i_cs]] = x[x_info['i_control_surfaces'][i_cs]] + solver_data.data.aero.data_dict['control_surface_deflection'][x_info['control_surfaces_id'][i_cs]] = x[x_info['i_control_surfaces'][i_cs]] # thrust input tstep.steady_applied_forces[:] = 0.0 try: diff --git a/sharpy/utils/h5utils.py b/sharpy/utils/h5utils.py index 927af014c..5f3549f83 100644 --- a/sharpy/utils/h5utils.py +++ b/sharpy/utils/h5utils.py @@ -69,7 +69,7 @@ def check_fem_dict(fem_dict): print(' PASSED') -def check_aero_dict(aero_dict): +def check_data_dict(data_dict): pass diff --git a/sharpy/utils/multibody.py b/sharpy/utils/multibody.py index b8f6f742f..6aa43f66f 100644 --- a/sharpy/utils/multibody.py +++ b/sharpy/utils/multibody.py @@ -13,7 +13,7 @@ import traceback -def split_multibody(beam, tstep, mb_aero_dict, ts): +def split_multibody(beam, tstep, mb_data_dict, ts): """ split_multibody @@ -22,7 +22,7 @@ def split_multibody(beam, tstep, mb_aero_dict, ts): Args: beam (:class:`~sharpy.structure.models.beam.Beam`): structural information of the multibody system tstep (:class:`~sharpy.utils.datastructures.StructTimeStepInfo`): timestep information of the multibody system - mb_aero_dict (dict): Dictionary including the multibody information + mb_data_dict (dict): Dictionary including the multibody information ts (int): time step number Returns: @@ -47,7 +47,7 @@ def split_multibody(beam, tstep, mb_aero_dict, ts): ibody_beam = beam.get_body(ibody = ibody) ibody_tstep = tstep.get_body(beam, ibody_beam.num_dof, ibody = ibody) - ibody_beam.FoR_movement = mb_aero_dict['body_%02d' % ibody]['FoR_movement'] + ibody_beam.FoR_movement = mb_data_dict['body_%02d' % ibody]['FoR_movement'] ibody_beam.ini_info.compute_psi_local_AFoR(ini_for0_pos, ini_for0_vel, ini_quat0) ibody_beam.ini_info.change_to_local_AFoR(ini_for0_pos, ini_for0_vel, ini_quat0) @@ -60,7 +60,7 @@ def split_multibody(beam, tstep, mb_aero_dict, ts): return MB_beam, MB_tstep -def merge_multibody(MB_tstep, MB_beam, beam, tstep, mb_aero_dict, dt): +def merge_multibody(MB_tstep, MB_beam, beam, tstep, mb_data_dict, dt): """ merge_multibody @@ -73,7 +73,7 @@ def merge_multibody(MB_tstep, MB_beam, beam, tstep, mb_aero_dict, dt): MB_tstep (list(:class:`~sharpy.utils.datastructures.StructTimeStepInfo`)): each entry represents a body beam (:class:`~sharpy.structure.models.beam.Beam`): structural information of the multibody system tstep (:class:`~sharpy.utils.datastructures.StructTimeStepInfo`): timestep information of the multibody system - mb_aero_dict (dict): Dictionary including the multibody information + mb_data_dict (dict): Dictionary including the multibody information dt(int): time step Returns: diff --git a/tests/linear/statespace/test_variable_tracker.py b/tests/linear/statespace/test_variable_tracker.py index cbbc165aa..35d66cfe2 100644 --- a/tests/linear/statespace/test_variable_tracker.py +++ b/tests/linear/statespace/test_variable_tracker.py @@ -152,9 +152,9 @@ def test_save_to_h5(self): input_variables.add_to_h5_file(fid) with h5py.File(h5_file_name, 'r') as fid: - aero_dict = h5utils.load_h5_in_dict(fid) + data_dict = h5utils.load_h5_in_dict(fid) - loaded_variable = LinearVector.load_from_h5_file('InputVariable', aero_dict['InputVariable']) + loaded_variable = LinearVector.load_from_h5_file('InputVariable', data_dict['InputVariable']) LinearVector.check_same_vectors(input_variables, loaded_variable) From f8c30f62af0a6d90191fcb2f25a4dabfe2eab482 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stefanie=20D=C3=BCssler?= Date: Tue, 15 Aug 2023 11:30:27 -0400 Subject: [PATCH 169/232] update submodule UVLM --- lib/UVLM | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/UVLM b/lib/UVLM index d5b2adb0c..0331ecc65 160000 --- a/lib/UVLM +++ b/lib/UVLM @@ -1 +1 @@ -Subproject commit d5b2adb0c9205444d159fda2f2a232603315dc7b +Subproject commit 0331ecc657bd488689fc264a5a0108b8415cc7c3 From 321db75b4101dbdfcd48e0084b00500617434b54 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stefanie=20D=C3=BCssler?= Date: Tue, 15 Aug 2023 17:01:14 -0400 Subject: [PATCH 170/232] fix [nonlifting] decode fuselage shape --- sharpy/aero/models/nonliftingbodygrid.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/sharpy/aero/models/nonliftingbodygrid.py b/sharpy/aero/models/nonliftingbodygrid.py index 465b87d68..3a621d5a0 100644 --- a/sharpy/aero/models/nonliftingbodygrid.py +++ b/sharpy/aero/models/nonliftingbodygrid.py @@ -52,8 +52,7 @@ def get_zeta_and_zeta_dot(self,i_surf, structure_tstep): cga_rotation_matrix = structure_tstep.cga() for node_counter, i_global_node in enumerate(self.aero2struct_mapping[i_surf]): - # TODO: Adjust for ellipse input - if self.data_dict["shape"] == 'specific': + if self.data_dict["shape"].decode() == 'specific': a_ellipse = self.data_dict["a_ellipse"][i_global_node] b_ellipse = self.data_dict["b_ellipse"][i_global_node] z_0 =self.data_dict["z_0_ellipse"][i_global_node] From 4c8991b243a370c70b9f90b18e04d81db261c88d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stefanie=20D=C3=BCssler?= Date: Wed, 16 Aug 2023 06:45:58 -0400 Subject: [PATCH 171/232] update [readme] SHARPy's new fuselage capabilities --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 5f7ab4a19..ccab553e1 100644 --- a/README.md +++ b/README.md @@ -33,6 +33,8 @@ vortex ring lattice with the boundary conditions enforced at the collocation poi The Kutta condition is also enforced at the trailing edge. The wake can be simulated by either additional vortex rings or by infinitely long horseshoe vortices, which are ideally suited for steady simulations only. +The aerodynamic model has recently been extended by a linear source panel method (SPM) to model nonlifting bodies for example fuselages. The SPM and UVLM can be coupled to model fuselage-wing configuration and a junction handling approach, based on phantom panels and circulation interpolation, has been added. + The input problems can be structural, aerodynamic or coupled, yielding an aeroelastic system. ## [Capabilities](http://ic-sharpy.readthedocs.io/en/latest/content/capabilities.html) From 4411dfaa4892e478ff0266d7749c2b28736b1a83 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stefanie=20D=C3=BCssler?= Date: Wed, 16 Aug 2023 08:29:07 -0400 Subject: [PATCH 172/232] fix [unittest] get results from speicfic timestep in phantom panel test --- tests/sourcepanelmethod/test_vlm_coupled_spm.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/tests/sourcepanelmethod/test_vlm_coupled_spm.py b/tests/sourcepanelmethod/test_vlm_coupled_spm.py index a802de10a..f8a3fffe9 100644 --- a/tests/sourcepanelmethod/test_vlm_coupled_spm.py +++ b/tests/sourcepanelmethod/test_vlm_coupled_spm.py @@ -138,7 +138,6 @@ def test_fuselage_wing_configuration(self): flow_case.remove('StaticUvlm') else: flow_case.remove('StaticCoupled') - print(flow_case) self.generate_simulation_settings(flow_case, wing_fuselage_model, alpha_deg, @@ -150,7 +149,7 @@ def test_fuselage_wing_configuration(self): wing_fuselage_model.run() # get results lift_distribution = self.load_lift_distribution( - self.output_route + '/' + case_name, + self.output_route + case_name, wing_fuselage_model.structure.n_node_right_wing ) @@ -229,11 +228,11 @@ def define_folder(self): if not os.path.exists(self.case_route): os.makedirs(self.case_route) - def load_lift_distribution(self, output_folder, n_node_wing): + def load_lift_distribution(self, output_folder, n_node_wing, n_tsteps=0): """ Loads the resulting pressure coefficients saved in txt-files. """ - lift_distribution = np.loadtxt(output_folder + '/lift_distribution.txt', delimiter=',') + lift_distribution = np.loadtxt(output_folder + '/liftdistribution/liftdistribution_ts{}.txt'.format(str(n_tsteps)), delimiter=',') return lift_distribution[:n_node_wing,[1,-1]] def tearDown(self): From a001b51c0909110187c9753e1a235ee9ea8462e0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stefanie=20D=C3=BCssler?= Date: Wed, 16 Aug 2023 08:30:52 -0400 Subject: [PATCH 173/232] fix [phantom test] finalize phantom test for steady and unsteady problems --- .../sourcepanelmethod/test_vlm_coupled_spm.py | 122 +++++++++++------- 1 file changed, 78 insertions(+), 44 deletions(-) diff --git a/tests/sourcepanelmethod/test_vlm_coupled_spm.py b/tests/sourcepanelmethod/test_vlm_coupled_spm.py index f8a3fffe9..0befb9a68 100644 --- a/tests/sourcepanelmethod/test_vlm_coupled_spm.py +++ b/tests/sourcepanelmethod/test_vlm_coupled_spm.py @@ -40,59 +40,77 @@ def test_phantom_panels(self): # Simulation settings horseshoe = True list_phantom_test = [False, True] + list_dynamic_test = [False, True] + dynamic_structural_solver = 'NonLinearDynamicPrescribedStep' # Simlation Solver Flow flow = ['BeamLoader', 'AerogridLoader', 'NonliftingbodygridLoader', 'StaticUvlm', + 'StaticCoupled', 'BeamLoads', 'LiftDistribution', - 'AerogridPlot', + 'DynamicCoupled', + 'LiftDistribution', ] - list_results_lift_distribution = [] # define model variables - for icase in range(len(list_phantom_test)): - phantom_test = list_phantom_test[icase] - lifting_only = not phantom_test - case_name = model + '_coupled_{}'.format(int(phantom_test)) - - # generate ellipsoid model - phantom_wing = self.generate_model(case_name, - dict_geometry_parameters, - dict_discretization, - lifting_only) - # Adjust flow for case - flow_case = flow.copy() - - if lifting_only: - flow_case.remove('NonliftingbodygridLoader') + for dynamic in list_dynamic_test: + list_results_lift_distribution = [] + for phantom_test in list_phantom_test: + horseshoe = not dynamic + lifting_only = not phantom_test + case_name = model + '_phantom{}_dynamic{}'.format(int(phantom_test), + int(dynamic)) - self.generate_simulation_settings(flow_case, - phantom_wing, - alpha_deg, - u_inf, - lifting_only, - horseshoe=horseshoe, - phantom_test=phantom_test) - # run simulation - phantom_wing.run() + # generate ellipsoid model + phantom_wing = self.generate_model(case_name, + dict_geometry_parameters, + dict_discretization, + lifting_only) + # Adjust flow for case + flow_case = flow.copy() - # get results - list_results_lift_distribution.append(self.load_lift_distribution( - self.output_route + '/' + case_name, - phantom_wing.structure.n_node_right_wing - )) + if lifting_only: + n_tsteps = 10 + flow_case.remove('NonliftingbodygridLoader') + if not dynamic: + n_tsteps = 0 + flow_case.remove('StaticCoupled') + flow_case.remove('DynamicCoupled') + self.generate_simulation_settings(flow_case, + phantom_wing, + alpha_deg, + u_inf, + lifting_only, + n_tsteps = n_tsteps, + horseshoe=horseshoe, + phantom_test=phantom_test, + dynamic_structural_solver=dynamic_structural_solver) + # # run simulation + phantom_wing.run() - # check results - with self.subTest('lift distribution'): - np.testing.assert_array_almost_equal(list_results_lift_distribution[0][3:, 1], list_results_lift_distribution[1][3:, 1], decimal=3) + # get results + list_results_lift_distribution.append(self.load_lift_distribution( + self.output_route + '/' + case_name, + phantom_wing.structure.n_node_right_wing, + n_tsteps + )) + + # check results + with self.subTest(self.get_test_name('phantom', dynamic)): + np.testing.assert_array_almost_equal(list_results_lift_distribution[0][3:, 1], + list_results_lift_distribution[1][3:, 1], + decimal=2) #3 - int(dynamic)) + def test_fuselage_wing_configuration(self): """ - Lift distribution on a low wing configuration is computed. The final - results are compared to a previous solution (backward compatibility) - that matches the experimental lift distribution for this case. + Test spanwise lift distribution on a fuselage-wing configuration. + + The lift distribution on low wing configuration is computed considering + fuselage effects. The final results are compared to a previous solution + (backward compatibility) that matches the experimental lift distribution for this case. """ self.define_folder() model = 'low_wing' @@ -124,7 +142,6 @@ def test_fuselage_wing_configuration(self): 'StaticCoupled', 'BeamLoads', 'LiftDistribution', - 'AerogridPlot', ] for static_coupled_solver in [False, True]: case_name = '{}_coupled_{}'.format(model, int(static_coupled_solver)) @@ -152,13 +169,21 @@ def test_fuselage_wing_configuration(self): self.output_route + case_name, wing_fuselage_model.structure.n_node_right_wing ) - # check results lift_distribution_test = np.loadtxt(self.route_test_dir + "/test_data/results_{}.csv".format(case_name)) - with self.subTest('lift distribution and spanwise wing deformation'): + with self.subTest(self.get_test_name('lift distribution and spanwise wing deformation', + static_coupled=static_coupled_solver)): np.testing.assert_array_almost_equal(lift_distribution_test, lift_distribution, decimal=3) - + def get_test_name(self, base_name, dynamic=False, static_coupled=False): + if static_coupled: + base_name += ' coupled' + else: + if dynamic: + base_name += ' uvlm' + else: + base_name += ' vlm' + return base_name def get_geometry_parameters(self, model_name,fuselage_length=10): """ Geometry parameters are loaded from json init file for the specified model. @@ -201,9 +226,12 @@ def generate_simulation_settings(self, alpha_deg, u_inf, lifting_only, + n_tsteps=0, horseshoe=True, nonlifting_only=False, - phantom_test=False): + phantom_test=False, + dynamic_structural_solver='NonLinearDynamicPrescribedStep' + ): """ Simulation settings are defined and written to the ".sharpy" input file. """ @@ -211,13 +239,19 @@ def generate_simulation_settings(self, aircraft_model, alpha_deg, u_inf, + dt=self.get_timestep(aircraft_model, u_inf), + n_tsteps=n_tsteps, lifting_only=lifting_only, phantom_test=phantom_test, nonlifting_only=nonlifting_only, - horseshoe=horseshoe) + horseshoe=horseshoe, + dynamic_structural_solver=dynamic_structural_solver) aircraft_model.create_settings(settings) - def define_folder(self): + def get_timestep(self, model, u_inf): + return model.aero.chord_wing / model.aero.num_chordwise_panels / u_inf + + def define_folder(self): """ Initializes all folder path needed and creates case folder. """ From 9f48816b32590e1efdb3c3aeea12622b3e51e77e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stefanie=20D=C3=BCssler?= Date: Wed, 16 Aug 2023 08:46:47 -0400 Subject: [PATCH 174/232] add [model] input to control write variables time output --- .../fuselage_wing_configuration/fwc_aero.py | 2 +- .../fwc_get_settings.py | 16 +++++++++------- .../fuselage_wing_configuration/fwc_structure.py | 1 + .../test_source_panel_method.py | 9 ++++++++- 4 files changed, 19 insertions(+), 9 deletions(-) diff --git a/sharpy/cases/templates/fuselage_wing_configuration/fwc_aero.py b/sharpy/cases/templates/fuselage_wing_configuration/fwc_aero.py index 24660932e..03bc8c6c4 100644 --- a/sharpy/cases/templates/fuselage_wing_configuration/fwc_aero.py +++ b/sharpy/cases/templates/fuselage_wing_configuration/fwc_aero.py @@ -71,7 +71,7 @@ def set_wing_properties(self): self.aero_node[:self.structure.n_node_wing_total] = True self.chord[:2*self.structure.n_elem_per_wing, :] = self.chord_wing self.elastic_axis[:2*self.structure.n_elem_per_wing, :] = self.ea_wing - self.twist [:2*self.structure.n_elem_per_wing, :] = -np.deg2rad(self.alpha_zero_deg) + self.twist[:2*self.structure.n_elem_per_wing, :] = -np.deg2rad(self.alpha_zero_deg) # surf distribution 0 for right and 1 for left wing self.surface_distribution[self.structure.n_elem_per_wing:2*self.structure.n_elem_per_wing] = 1 diff --git a/sharpy/cases/templates/fuselage_wing_configuration/fwc_get_settings.py b/sharpy/cases/templates/fuselage_wing_configuration/fwc_get_settings.py index 0ee7557bc..42d4b87fd 100644 --- a/sharpy/cases/templates/fuselage_wing_configuration/fwc_get_settings.py +++ b/sharpy/cases/templates/fuselage_wing_configuration/fwc_get_settings.py @@ -87,13 +87,15 @@ def define_simulation_settings(flow, model, alpha_deg, u_inf, }, } if 'WriteVariablesTime' in flow: - settings['WriteVariablesTime'] = { - 'cleanup_old_solution': True, - 'nonlifting_nodes_variables': ['pressure_coefficients'], - 'nonlifting_nodes_isurf': np.zeros((model.structure.n_node_fuselage,)), - 'nonlifting_nodes_im': np.zeros((model.structure.n_node_fuselage)), - 'nonlifting_nodes_in': list(range(model.structure.n_node_fuselage)), - } + settings['WriteVariablesTime'] = {'cleanup_old_solution': True} + if kwargs.get('writeCpVariables', False): + settings['WriteVariablesTime']['nonlifting_nodes_variables'] = ['pressure_coefficients'] + settings['WriteVariablesTime']['nonlifting_nodes_isurf'] = np.zeros((model.structure.n_node_fuselage,)) + settings['WriteVariablesTime']['nonlifting_nodes_im'] = np.zeros((model.structure.n_node_fuselage)) + settings['WriteVariablesTime']['nonlifting_nodes_in'] = list(range(model.structure.n_node_fuselage)) + if kwargs.get('writeWingPosVariables', False): + settings['WriteVariablesTime']['structure_variables'] = ['pos'] + settings['WriteVariablesTime']['structure_nodes'] = list(range(model.structure.n_node-2)) settings['NonliftingbodygridLoader'] = {} diff --git a/sharpy/cases/templates/fuselage_wing_configuration/fwc_structure.py b/sharpy/cases/templates/fuselage_wing_configuration/fwc_structure.py index c58a31117..de7f4865f 100644 --- a/sharpy/cases/templates/fuselage_wing_configuration/fwc_structure.py +++ b/sharpy/cases/templates/fuselage_wing_configuration/fwc_structure.py @@ -70,6 +70,7 @@ def set_element_and_nodes(self): self.n_node = self.n_node_fuselage + self.n_node_wing_total self.n_elem =self.n_elem_fuselage + 2 * self.n_elem_per_wing + # vertical wing offset requires a connection element between wing and fuselage beam if not self.vertical_wing_position == 0: self.n_elem += 1 self.n_node += 1 diff --git a/tests/sourcepanelmethod/test_source_panel_method.py b/tests/sourcepanelmethod/test_source_panel_method.py index cc37a486a..7056e6475 100644 --- a/tests/sourcepanelmethod/test_source_panel_method.py +++ b/tests/sourcepanelmethod/test_source_panel_method.py @@ -53,7 +53,14 @@ def test_ellipsoid(self): 'StaticUvlm', 'WriteVariablesTime' ] - settings = define_simulation_settings(flow, ellipsoidal_body, alpha_deg, u_inf, lifting_only=False, nonlifting_only=True, horseshoe=True) + settings = define_simulation_settings(flow, + ellipsoidal_body, + alpha_deg, + u_inf, + lifting_only=False, + nonlifting_only=True, + horseshoe=True, + writeCpVariables=True) ellipsoidal_body.create_settings(settings) # run simulation From e5076c83a6516571ec3860ef7d7e1d18e1e8fd09 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stefanie=20D=C3=BCssler?= Date: Wed, 16 Aug 2023 08:56:55 -0400 Subject: [PATCH 175/232] remove [model] left-over print command --- sharpy/cases/templates/fuselage_wing_configuration/fwc_aero.py | 1 - 1 file changed, 1 deletion(-) diff --git a/sharpy/cases/templates/fuselage_wing_configuration/fwc_aero.py b/sharpy/cases/templates/fuselage_wing_configuration/fwc_aero.py index 03bc8c6c4..685f34b71 100644 --- a/sharpy/cases/templates/fuselage_wing_configuration/fwc_aero.py +++ b/sharpy/cases/templates/fuselage_wing_configuration/fwc_aero.py @@ -64,7 +64,6 @@ def set_wing_properties(self): """ if not self.lifting_only: - print(self.structure.y[:self.structure.n_node_right_wing]) self.aero_node[:self.structure.n_node_right_wing] = abs(self.structure.y[:self.structure.n_node_right_wing]) > self.get_y_junction() - 0.05 self.aero_node[self.structure.n_node_right_wing:self.structure.n_node_wing_total] = self.aero_node[1:self.structure.n_node_right_wing] else: From 059017a68f70faec64ee93da6299ff4ecf9fd20c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stefanie=20D=C3=BCssler?= Date: Wed, 16 Aug 2023 09:28:34 -0400 Subject: [PATCH 176/232] fix [unittest] update backward-compatibility data - after fixing bugs, old results have changed and needed to be updated --- .../test_data/results_low_wing_coupled_1.csv | 34 +++++++++---------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/tests/sourcepanelmethod/test_data/results_low_wing_coupled_1.csv b/tests/sourcepanelmethod/test_data/results_low_wing_coupled_1.csv index ee100945e..f558b3649 100644 --- a/tests/sourcepanelmethod/test_data/results_low_wing_coupled_1.csv +++ b/tests/sourcepanelmethod/test_data/results_low_wing_coupled_1.csv @@ -2,20 +2,20 @@ 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 -5.630627999999999744e-01 2.663324000000000247e-01 -7.038284000000000207e-01 2.638707000000000136e-01 -8.445939000000000085e-01 2.612416000000000182e-01 -9.853593999999999964e-01 2.575641000000000180e-01 -1.126125000000000043e+00 2.530164999999999775e-01 -1.266890000000000072e+00 2.476839999999999875e-01 -1.407656000000000018e+00 2.415724999999999956e-01 -1.548421000000000047e+00 2.346193000000000028e-01 -1.689186000000000076e+00 2.266981999999999886e-01 -1.829952000000000023e+00 2.176136999999999933e-01 -1.970717000000000052e+00 2.070840999999999932e-01 -2.111482000000000081e+00 1.947038000000000102e-01 -2.252247999999999806e+00 1.798706999999999945e-01 -2.393012999999999835e+00 1.616222999999999965e-01 -2.533777999999999864e+00 1.382161000000000084e-01 -2.674544000000000032e+00 1.055984999999999979e-01 -2.815309000000000061e+00 8.849437000000000275e-02 +5.630631000000000386e-01 2.636864999999999903e-01 +7.038288000000000322e-01 2.592814999999999981e-01 +8.445945999999999732e-01 2.565006000000000230e-01 +9.853604000000000251e-01 2.526663000000000103e-01 +1.126125999999999960e+00 2.479610999999999899e-01 +1.266891999999999907e+00 2.424882999999999900e-01 +1.407658000000000076e+00 2.362653000000000114e-01 +1.548423000000000105e+00 2.292386999999999897e-01 +1.689189000000000052e+00 2.212899000000000116e-01 +1.829954999999999998e+00 2.122312000000000087e-01 +1.970720999999999945e+00 2.017887999999999904e-01 +2.111486000000000196e+00 1.895677000000000056e-01 +2.252251999999999921e+00 1.749785000000000090e-01 +2.393018000000000089e+00 1.570786999999999878e-01 +2.533783999999999814e+00 1.341562000000000032e-01 +2.674549999999999983e+00 1.022175000000000028e-01 +2.815315000000000012e+00 8.376727999999999952e-02 From 85b4eb5c5133b7656a491348e74e5a3f2e289293 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stefanie=20D=C3=BCssler?= Date: Mon, 24 Jul 2023 20:04:18 +0800 Subject: [PATCH 177/232] fix [savedata] also remove old linear data output file if it exists - Before errors were raised if the linear data file already exists, as it cannot be overwritten. As for the nonlinear data file, the old linear data file is removed before. - I stumbled across it when creating the udp tutorial as I had to erase the data file in an extra cell before generating the linear system. I could delete this part in the tutorial now, too. From efc06262ce4b67a843cb8618f230e622ebeb1253 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stefanie=20D=C3=BCssler?= Date: Mon, 21 Aug 2023 11:29:49 -0400 Subject: [PATCH 178/232] fix [savedata] wrong intend after merge - causes error when used as postprocessing in dynamiccoupled solver --- sharpy/postproc/savedata.py | 94 ++++++++++++++++++------------------- 1 file changed, 47 insertions(+), 47 deletions(-) diff --git a/sharpy/postproc/savedata.py b/sharpy/postproc/savedata.py index 79fad608c..72b018ae3 100644 --- a/sharpy/postproc/savedata.py +++ b/sharpy/postproc/savedata.py @@ -229,53 +229,53 @@ def run(self, **kwargs): ClassesToSave=self.ClassesToSave, SkipAttr=skip_attr_init, compress_float=self.settings['compress_float']) - if self.settings['save_struct']: - h5utils.add_as_grp(list(), - hdfile['data']['structure'], - grpname='timestep_info') - if self.settings['save_aero']: - h5utils.add_as_grp(list(), - hdfile['data']['aero'], - grpname='timestep_info') - if self.settings['save_nonlifting']: - h5utils.add_as_grp(list(), - hdfile['data']['nonlifting_body'], - grpname='timestep_info') - - for it in range(len(self.data.structure.timestep_info)): - tstep_p = self.data.structure.timestep_info[it] - if tstep_p is not None: - self.save_timestep(self.data, self.settings, it, hdfile) - - hdfile.close() - - if self.settings['save_linear_uvlm']: - linhdffile = h5py.File(self.filename.replace('.data.h5', '.uvlmss.h5'), 'a') - h5utils.add_as_grp(self.data.linear.linear_system.uvlm.ss, linhdffile, grpname='ss', - ClassesToSave=self.ClassesToSave, SkipAttr=self.settings['skip_attr'], - compress_float=self.settings['compress_float']) - h5utils.add_as_grp(self.data.linear.linear_system.linearisation_vectors, linhdffile, - grpname='linearisation_vectors', - ClassesToSave=self.ClassesToSave, SkipAttr=self.settings['skip_attr'], - compress_float=self.settings['compress_float']) - linhdffile.close() - - if self.settings['save_linear']: - with h5py.File(self.filename_linear, 'a') as linfile: - h5utils.add_as_grp(self.data.linear.linear_system.linearisation_vectors, linfile, - grpname='linearisation_vectors', - ClassesToSave=self.ClassesToSave, SkipAttr=self.settings['skip_attr'], - compress_float=self.settings['compress_float']) - h5utils.add_as_grp(self.data.linear.ss, linfile, grpname='ss', - ClassesToSave=self.ClassesToSave, SkipAttr=self.settings['skip_attr'], - compress_float=self.settings['compress_float']) - - if self.settings['save_rom']: - try: - for k, rom in self.data.linear.linear_system.uvlm.rom.items(): - rom.save(self.filename.replace('.data.h5', '_{:s}.rom.h5'.format(k.lower()))) - except AttributeError: - cout.cout_wrap('Could not locate a reduced order model to save') + if self.settings['save_struct']: + h5utils.add_as_grp(list(), + hdfile['data']['structure'], + grpname='timestep_info') + if self.settings['save_aero']: + h5utils.add_as_grp(list(), + hdfile['data']['aero'], + grpname='timestep_info') + if self.settings['save_nonlifting']: + h5utils.add_as_grp(list(), + hdfile['data']['nonlifting_body'], + grpname='timestep_info') + + for it in range(len(self.data.structure.timestep_info)): + tstep_p = self.data.structure.timestep_info[it] + if tstep_p is not None: + self.save_timestep(self.data, self.settings, it, hdfile) + + hdfile.close() + + if self.settings['save_linear_uvlm']: + linhdffile = h5py.File(self.filename.replace('.data.h5', '.uvlmss.h5'), 'a') + h5utils.add_as_grp(self.data.linear.linear_system.uvlm.ss, linhdffile, grpname='ss', + ClassesToSave=self.ClassesToSave, SkipAttr=self.settings['skip_attr'], + compress_float=self.settings['compress_float']) + h5utils.add_as_grp(self.data.linear.linear_system.linearisation_vectors, linhdffile, + grpname='linearisation_vectors', + ClassesToSave=self.ClassesToSave, SkipAttr=self.settings['skip_attr'], + compress_float=self.settings['compress_float']) + linhdffile.close() + + if self.settings['save_linear']: + with h5py.File(self.filename_linear, 'a') as linfile: + h5utils.add_as_grp(self.data.linear.linear_system.linearisation_vectors, linfile, + grpname='linearisation_vectors', + ClassesToSave=self.ClassesToSave, SkipAttr=self.settings['skip_attr'], + compress_float=self.settings['compress_float']) + h5utils.add_as_grp(self.data.linear.ss, linfile, grpname='ss', + ClassesToSave=self.ClassesToSave, SkipAttr=self.settings['skip_attr'], + compress_float=self.settings['compress_float']) + + if self.settings['save_rom']: + try: + for k, rom in self.data.linear.linear_system.uvlm.rom.items(): + rom.save(self.filename.replace('.data.h5', '_{:s}.rom.h5'.format(k.lower()))) + except AttributeError: + cout.cout_wrap('Could not locate a reduced order model to save') elif self.settings['format'] == 'mat': from scipy.io import savemat From c22b0d6bc0960bd3cef97fbd220b437462ab8be8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stefanie=20D=C3=BCssler?= Date: Mon, 21 Aug 2023 18:27:08 -0400 Subject: [PATCH 179/232] add [nonlifting] induced velocities by source for lifting forces --- lib/UVLM | 2 +- sharpy/aero/utils/uvlmlib.py | 11 ++++++++--- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/lib/UVLM b/lib/UVLM index 0331ecc65..1cb9420cd 160000 --- a/lib/UVLM +++ b/lib/UVLM @@ -1 +1 @@ -Subproject commit 0331ecc657bd488689fc264a5a0108b8415cc7c3 +Subproject commit 1cb9420cd870411d53f63989209a362ad1056bf2 diff --git a/sharpy/aero/utils/uvlmlib.py b/sharpy/aero/utils/uvlmlib.py index 9b2ca9dac..36dab97b1 100644 --- a/sharpy/aero/utils/uvlmlib.py +++ b/sharpy/aero/utils/uvlmlib.py @@ -35,7 +35,8 @@ class VMopts(ct.Structure): unsigned int NumSurfaces; unsigned int NumSurfacesNonlifting; double vortex_radius; - double vortex_radius_wake_ind; + double vortex_radius_wake_ind; + bool u_ind_by_sources_for_lifting_forces; }; """ _fields_ = [("ImageMethod", ct.c_bool), @@ -59,7 +60,8 @@ class VMopts(ct.Structure): ("iterative_tol", ct.c_double), ("iterative_precond", ct.c_bool), ("vortex_radius", ct.c_double), - ("vortex_radius_wake_ind", ct.c_double)] + ("vortex_radius_wake_ind", ct.c_double), + ("consider_u_ind_by_sources_for_lifting_forces", ct.c_bool)] @@ -87,6 +89,7 @@ def __init__(self): self.vortex_radius = ct.c_double(vortex_radius_def) self.vortex_radius_wake_ind = ct.c_double(vortex_radius_def) self.phantom_wing_test = ct.c_bool(False) + self.consider_u_ind_by_sources_for_lifting_forces = ct.c_bool(False) def set_options(self, options, n_surfaces = 0, n_surfaces_nonlifting = 0): @@ -134,7 +137,8 @@ class UVMopts(ct.Structure): ("interp_method", ct.c_uint), ("yaw_slerp", ct.c_double), ("quasi_steady", ct.c_bool), - ("num_spanwise_panels_wo_induced_velocity", ct.c_uint)] + ("num_spanwise_panels_wo_induced_velocity", ct.c_uint), + ("consider_u_ind_by_sources_for_lifting_forces", ct.c_bool)] def __init__(self): ct.Structure.__init__(self) @@ -155,6 +159,7 @@ def __init__(self): self.quasi_steady = ct.c_bool(False) self.num_spanwise_panels_wo_induced_velocity = ct.c_uint(0) self.phantom_wing_test = ct.c_bool(False) + self.consider_u_ind_by_sources_for_lifting_forces = ct.c_bool(False) def set_options(self, options, From 49709cac7f9ea49fd3211b42075634e27d03955f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stefanie=20D=C3=BCssler?= Date: Tue, 22 Aug 2023 15:06:29 -0400 Subject: [PATCH 180/232] update submodule UVLM - includes latest fixes --- lib/UVLM | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/UVLM b/lib/UVLM index 1cb9420cd..d7368d567 160000 --- a/lib/UVLM +++ b/lib/UVLM @@ -1 +1 @@ -Subproject commit 1cb9420cd870411d53f63989209a362ad1056bf2 +Subproject commit d7368d567d58039ad8a9df993edb01a26f84d320 From 01a9e401a4946080faa9c4eb9fbeedc24c8d0b5f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stefanie=20D=C3=BCssler?= Date: Tue, 22 Aug 2023 15:12:42 -0400 Subject: [PATCH 181/232] fix [tests] deactivate screen log --- .../templates/fuselage_wing_configuration/fwc_get_settings.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sharpy/cases/templates/fuselage_wing_configuration/fwc_get_settings.py b/sharpy/cases/templates/fuselage_wing_configuration/fwc_get_settings.py index 42d4b87fd..a7dc803a9 100644 --- a/sharpy/cases/templates/fuselage_wing_configuration/fwc_get_settings.py +++ b/sharpy/cases/templates/fuselage_wing_configuration/fwc_get_settings.py @@ -31,7 +31,7 @@ def define_simulation_settings(flow, model, alpha_deg, u_inf, settings['SHARPy'] = {'case': model.case_name, 'route': model.case_route, 'flow': flow, - 'write_screen': 'on', + 'write_screen': 'off', 'write_log': 'on', 'log_folder': model.output_route, 'log_file': model.case_name + '.log'} From 86ff2d789981efe46cb963d2e7ddd75f6eaa86b8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stefanie=20D=C3=BCssler?= Date: Tue, 22 Aug 2023 16:02:23 -0400 Subject: [PATCH 182/232] remove left-over print for debugging --- sharpy/postproc/aerogridplot.py | 1 - 1 file changed, 1 deletion(-) diff --git a/sharpy/postproc/aerogridplot.py b/sharpy/postproc/aerogridplot.py index 969289d54..2b21ac612 100644 --- a/sharpy/postproc/aerogridplot.py +++ b/sharpy/postproc/aerogridplot.py @@ -149,7 +149,6 @@ def run(self, **kwargs): self.plot_body() self.plot_wake() if self.settings['plot_nonlifting_surfaces']: - print("Plot Nonlifting Surface") self.plot_nonlifting_surfaces() return self.data From 6d4e06c630cacf697104b9fa86db05d2c6492484 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stefanie=20D=C3=BCssler?= Date: Wed, 30 Aug 2023 22:10:06 -0400 Subject: [PATCH 183/232] fix [model] passing of parameter --- .../fuselage_wing_configuration.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sharpy/cases/templates/fuselage_wing_configuration/fuselage_wing_configuration.py b/sharpy/cases/templates/fuselage_wing_configuration/fuselage_wing_configuration.py index 9f0db4440..ca7471642 100644 --- a/sharpy/cases/templates/fuselage_wing_configuration/fuselage_wing_configuration.py +++ b/sharpy/cases/templates/fuselage_wing_configuration/fuselage_wing_configuration.py @@ -25,11 +25,11 @@ def __init__(self, case_name, case_route, output_route): self.settings = None - def init_aeroelastic(self, **kwargs): + def init_aeroelastic(self, lifting_only=True, **kwargs): self.clean() self.init_structure(**kwargs) self.init_aero(**kwargs) - if not kwargs.get('lifting_only', True): + if not lifting_only: self.init_fuselage(**kwargs) def init_structure(self, **kwargs): From bd0bd9e47159bda43e8e45bcdc26f57058d5b0a8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stefanie=20D=C3=BCssler?= Date: Thu, 31 Aug 2023 11:03:52 -0400 Subject: [PATCH 184/232] update UVLM submodule --- lib/UVLM | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/UVLM b/lib/UVLM index d7368d567..eac92ac6e 160000 --- a/lib/UVLM +++ b/lib/UVLM @@ -1 +1 @@ -Subproject commit d7368d567d58039ad8a9df993edb01a26f84d320 +Subproject commit eac92ac6e300659069cf636ef0d34d61059f2801 From 6a91434d7757409fad40316d4936c5c100a579de Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stefanie=20D=C3=BCssler?= Date: Fri, 1 Sep 2023 12:48:52 -0400 Subject: [PATCH 185/232] fix [aerogridplot] file extension for nonlifting bodies --- sharpy/postproc/aerogridplot.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/sharpy/postproc/aerogridplot.py b/sharpy/postproc/aerogridplot.py index 2b21ac612..f860459f3 100644 --- a/sharpy/postproc/aerogridplot.py +++ b/sharpy/postproc/aerogridplot.py @@ -357,7 +357,8 @@ def plot_nonlifting_surfaces(self): filename = (self.nonlifting_filename + '_' + '%02u_' % i_surf + - '%06u' % self.ts) + '%06u' % self.ts+ + '.vtu') dims = nonlifting_tstep.dimensions[i_surf, :] point_data_dim = (dims[0]+1)*(dims[1]+1) # + (dims_star[0]+1)*(dims_star[1]+1) From 7ca4e443833888634e1411f5d7efaba5fdfa2a59 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stefanie=20D=C3=BCssler?= Date: Wed, 30 Aug 2023 22:10:06 -0400 Subject: [PATCH 186/232] Revert "fix [model] passing of parameter" This reverts commit 6d4e06c630cacf697104b9fa86db05d2c6492484. --- .../fuselage_wing_configuration.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sharpy/cases/templates/fuselage_wing_configuration/fuselage_wing_configuration.py b/sharpy/cases/templates/fuselage_wing_configuration/fuselage_wing_configuration.py index ca7471642..9f0db4440 100644 --- a/sharpy/cases/templates/fuselage_wing_configuration/fuselage_wing_configuration.py +++ b/sharpy/cases/templates/fuselage_wing_configuration/fuselage_wing_configuration.py @@ -25,11 +25,11 @@ def __init__(self, case_name, case_route, output_route): self.settings = None - def init_aeroelastic(self, lifting_only=True, **kwargs): + def init_aeroelastic(self, **kwargs): self.clean() self.init_structure(**kwargs) self.init_aero(**kwargs) - if not lifting_only: + if not kwargs.get('lifting_only', True): self.init_fuselage(**kwargs) def init_structure(self, **kwargs): From 586af61b754c3fe148b2fba319cbf6bc383f949c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stefanie=20D=C3=BCssler?= Date: Mon, 11 Sep 2023 00:44:03 -0400 Subject: [PATCH 187/232] add [forces] ignore first x spanwise nodes for force calculation --- lib/UVLM | 2 +- sharpy/aero/utils/uvlmlib.py | 12 +++++++++--- .../fuselage_wing_configuration/fwc_get_settings.py | 7 +++++-- sharpy/solvers/staticuvlm.py | 4 ++++ sharpy/solvers/stepuvlm.py | 4 ++++ 5 files changed, 23 insertions(+), 6 deletions(-) diff --git a/lib/UVLM b/lib/UVLM index eac92ac6e..c6df90830 160000 --- a/lib/UVLM +++ b/lib/UVLM @@ -1 +1 @@ -Subproject commit eac92ac6e300659069cf636ef0d34d61059f2801 +Subproject commit c6df908303a3791af649726c7c1c07363ac5bf85 diff --git a/sharpy/aero/utils/uvlmlib.py b/sharpy/aero/utils/uvlmlib.py index 36dab97b1..2734119a1 100644 --- a/sharpy/aero/utils/uvlmlib.py +++ b/sharpy/aero/utils/uvlmlib.py @@ -37,6 +37,7 @@ class VMopts(ct.Structure): double vortex_radius; double vortex_radius_wake_ind; bool u_ind_by_sources_for_lifting_forces; + uint ignore_first_x_nodes_in_force_calculation; }; """ _fields_ = [("ImageMethod", ct.c_bool), @@ -61,7 +62,8 @@ class VMopts(ct.Structure): ("iterative_precond", ct.c_bool), ("vortex_radius", ct.c_double), ("vortex_radius_wake_ind", ct.c_double), - ("consider_u_ind_by_sources_for_lifting_forces", ct.c_bool)] + ("consider_u_ind_by_sources_for_lifting_forces", ct.c_bool), + ("ignore_first_x_nodes_in_force_calculation", ct.c_uint)] @@ -90,6 +92,7 @@ def __init__(self): self.vortex_radius_wake_ind = ct.c_double(vortex_radius_def) self.phantom_wing_test = ct.c_bool(False) self.consider_u_ind_by_sources_for_lifting_forces = ct.c_bool(False) + self.ignore_first_x_nodes_in_force_calculation = ct.c_uint(0) def set_options(self, options, n_surfaces = 0, n_surfaces_nonlifting = 0): @@ -111,6 +114,7 @@ def set_options(self, options, n_surfaces = 0, n_surfaces_nonlifting = 0): self.only_nonlifting = ct.c_bool(options["only_nonlifting"]) self.only_lifting = ct.c_bool(not options["nonlifting_body_interactions"]) self.phantom_wing_test = ct.c_bool(options["phantom_wing_test"]) + self.ignore_first_x_nodes_in_force_calculation = ct.c_uint(options["ignore_first_x_nodes_in_force_calculation"]) class UVMopts(ct.Structure): @@ -138,7 +142,8 @@ class UVMopts(ct.Structure): ("yaw_slerp", ct.c_double), ("quasi_steady", ct.c_bool), ("num_spanwise_panels_wo_induced_velocity", ct.c_uint), - ("consider_u_ind_by_sources_for_lifting_forces", ct.c_bool)] + ("consider_u_ind_by_sources_for_lifting_forces", ct.c_bool), + ("ignore_first_x_nodes_in_force_calculation", ct.c_uint)] def __init__(self): ct.Structure.__init__(self) @@ -160,6 +165,7 @@ def __init__(self): self.num_spanwise_panels_wo_induced_velocity = ct.c_uint(0) self.phantom_wing_test = ct.c_bool(False) self.consider_u_ind_by_sources_for_lifting_forces = ct.c_bool(False) + self.ignore_first_x_nodes_in_force_calculation = ct.c_uint(0) def set_options(self, options, @@ -193,6 +199,7 @@ def set_options(self, self.only_nonlifting = ct.c_bool(options["only_nonlifting"]) self.only_lifting = ct.c_bool(not options["nonlifting_body_interactions"]) self.phantom_wing_test = ct.c_bool(options["phantom_wing_test"]) + self.ignore_first_x_nodes_in_force_calculation = ct.c_uint(options["ignore_first_x_nodes_in_force_calculation"]) self.num_spanwise_panels_wo_induced_velocity = n_span_panels_wo_u_ind @@ -222,7 +229,6 @@ def vlm_solver(ts_info, options): p_rbm_vel_g = options['rbm_vel_g'].ctypes.data_as(ct.POINTER(ct.c_double)) p_centre_rot_g = options['centre_rot_g'].ctypes.data_as(ct.POINTER(ct.c_double)) - ts_info.generate_ctypes_pointers() run_VLM(ct.byref(vmopts), ct.byref(flightconditions), diff --git a/sharpy/cases/templates/fuselage_wing_configuration/fwc_get_settings.py b/sharpy/cases/templates/fuselage_wing_configuration/fwc_get_settings.py index a7dc803a9..e17769291 100644 --- a/sharpy/cases/templates/fuselage_wing_configuration/fwc_get_settings.py +++ b/sharpy/cases/templates/fuselage_wing_configuration/fwc_get_settings.py @@ -62,7 +62,8 @@ def define_simulation_settings(flow, model, alpha_deg, u_inf, 'rho': rho, 'nonlifting_body_interactions': nonlifting_body_interactions, 'only_nonlifting': nonlifting_only, - 'phantom_wing_test': phantom_test + 'phantom_wing_test': phantom_test, + 'ignore_first_x_nodes_in_force_calculation': kwargs.get('ignore_first_x_nodes_in_force_calculation', 0), } settings['StaticCoupled'] = {'print_info': 'off', @@ -124,7 +125,9 @@ def define_simulation_settings(flow, model, alpha_deg, u_inf, 'dt': dt, 'phantom_wing_test': phantom_test, 'nonlifting_body_interactions': not lifting_only, - 'gamma_dot_filtering': 3} + 'gamma_dot_filtering': 3, + 'ignore_first_x_nodes_in_force_calculation': kwargs.get('ignore_first_x_nodes_in_force_calculation', 0),} + dynamic_structural_solver = kwargs.get('structural_solver','NonLinearDynamicPrescribedStep') settings['DynamicCoupled'] = {'structural_solver': dynamic_structural_solver, 'structural_solver_settings': settings[dynamic_structural_solver], diff --git a/sharpy/solvers/staticuvlm.py b/sharpy/solvers/staticuvlm.py index b76085add..e9fa9b859 100644 --- a/sharpy/solvers/staticuvlm.py +++ b/sharpy/solvers/staticuvlm.py @@ -123,6 +123,10 @@ class StaticUvlm(BaseSolver): settings_default['map_forces_on_struct'] = False settings_description['map_forces_on_struct'] = 'Maps the forces on the structure at the end of the timestep. Only usefull if the solver is used outside StaticCoupled' + settings_types['ignore_first_x_nodes_in_force_calculation'] = 'int' + settings_default['ignore_first_x_nodes_in_force_calculation'] = 0 + settings_description['ignore_first_x_nodes_in_force_calculation'] = 'Ignores the forces on the first user-specified number of nodes of all surfaces.' + settings_table = settings_utils.SettingsTable() __doc__ += settings_table.generate(settings_types, settings_default, settings_description) diff --git a/sharpy/solvers/stepuvlm.py b/sharpy/solvers/stepuvlm.py index 9cea8ab1e..ba4b2a686 100644 --- a/sharpy/solvers/stepuvlm.py +++ b/sharpy/solvers/stepuvlm.py @@ -147,6 +147,10 @@ class StepUvlm(BaseSolver): settings_default['centre_rot_g'] = [0., 0., 0.] settings_description['centre_rot_g'] = 'Centre of rotation in G FoR around which ``rbm_vel_g`` is applied' + settings_types['ignore_first_x_nodes_in_force_calculation'] = 'int' + settings_default['ignore_first_x_nodes_in_force_calculation'] = 0 + settings_description['ignore_first_x_nodes_in_force_calculation'] = 'Ignores the forces on the first user-specified number of nodes of all surfaces.' + settings_table = settings_utils.SettingsTable() __doc__ += settings_table.generate(settings_types, settings_default, settings_description, settings_options) From ce1f32f081ab0c34145649e74a2389e74d289a37 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stefanie=20D=C3=BCssler?= Date: Mon, 11 Sep 2023 00:45:17 -0400 Subject: [PATCH 188/232] fix [savedata] intend in postprocessor - does not save the timestep infos - also removed unused attribute ts_max --- sharpy/postproc/savedata.py | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/sharpy/postproc/savedata.py b/sharpy/postproc/savedata.py index 72b018ae3..87ccd2df5 100644 --- a/sharpy/postproc/savedata.py +++ b/sharpy/postproc/savedata.py @@ -110,7 +110,6 @@ def __init__(self): self.folder = '' self.filename = '' self.filename_linear = '' - self.ts_max = 0 self.caller = None ### specify which classes are saved as hdf5 group @@ -147,7 +146,6 @@ def initialise(self, data, custom_settings=None, caller=None, restart=False): 'ct_zeta_star_list', 'dynamic_input']) - self.ts_max = self.data.ts + 1 # create folder for containing files if necessary self.folder = data.output_folder + '/savedata/' @@ -242,10 +240,10 @@ def run(self, **kwargs): hdfile['data']['nonlifting_body'], grpname='timestep_info') - for it in range(len(self.data.structure.timestep_info)): - tstep_p = self.data.structure.timestep_info[it] - if tstep_p is not None: - self.save_timestep(self.data, self.settings, it, hdfile) + for it in range(len(self.data.structure.timestep_info)): + tstep_p = self.data.structure.timestep_info[it] + if tstep_p is not None: + self.save_timestep(self.data, self.settings, it, hdfile) hdfile.close() From 5543fb360a5b343db5f25a8c744b1ed22cffe41c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stefanie=20D=C3=BCssler?= Date: Mon, 11 Sep 2023 11:33:05 -0400 Subject: [PATCH 189/232] update submodule UVLM - fetch error in git pull request build test - I am trying to update it top the latest version in the next commit, but for now I need to revert to an older one! --- lib/UVLM | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/UVLM b/lib/UVLM index c6df90830..eac92ac6e 160000 --- a/lib/UVLM +++ b/lib/UVLM @@ -1 +1 @@ -Subproject commit c6df908303a3791af649726c7c1c07363ac5bf85 +Subproject commit eac92ac6e300659069cf636ef0d34d61059f2801 From 7605772ed757cb81ea6d2f832f6ab5b843443b2d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stefanie=20D=C3=BCssler?= Date: Mon, 11 Sep 2023 11:33:41 -0400 Subject: [PATCH 190/232] update submoduleUVLM - back to the latest version, I hope it works. --- lib/UVLM | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/UVLM b/lib/UVLM index eac92ac6e..c6df90830 160000 --- a/lib/UVLM +++ b/lib/UVLM @@ -1 +1 @@ -Subproject commit eac92ac6e300659069cf636ef0d34d61059f2801 +Subproject commit c6df908303a3791af649726c7c1c07363ac5bf85 From 3dc8d95e00453f7510f131187722bdf60f4290ab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stefanie=20D=C3=BCssler?= Date: Wed, 13 Sep 2023 13:14:08 -0400 Subject: [PATCH 191/232] reverse [generator] change for handling of temporary files in interface --- sharpy/utils/generator_interface.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sharpy/utils/generator_interface.py b/sharpy/utils/generator_interface.py index 717dae296..70fd3a28d 100644 --- a/sharpy/utils/generator_interface.py +++ b/sharpy/utils/generator_interface.py @@ -38,7 +38,7 @@ def generator_list_from_path(cwd): onlyfiles = [f for f in os.listdir(cwd) if os.path.isfile(os.path.join(cwd, f))] for i_file in range(len(onlyfiles)): - if ".py" in onlyfiles[i_file]: + if onlyfiles[i_file].split('.')[-1] == 'py': # support autosaved files in the folder if onlyfiles[i_file] == "__init__.py": onlyfiles[i_file] = "" continue From d0f09505f5b2c684e9856672fb1b33c350d40810 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stefanie=20D=C3=BCssler?= Date: Wed, 13 Sep 2023 15:51:15 -0400 Subject: [PATCH 192/232] revert sharpy_main to latest develop version - inputs needed to be added in the listed solvers to avoid bugs during unittesting - these inputs have been adjusted to the other solvers --- sharpy/postproc/liftdistribution.py | 4 +-- sharpy/sharpy_main.py | 33 +++++++++++++++++++--- sharpy/solvers/aerogridloader.py | 2 +- sharpy/solvers/gridloader.py | 2 +- sharpy/solvers/nonliftingbodygridloader.py | 2 +- 5 files changed, 34 insertions(+), 9 deletions(-) diff --git a/sharpy/postproc/liftdistribution.py b/sharpy/postproc/liftdistribution.py index dadda480c..d0cf7e79d 100644 --- a/sharpy/postproc/liftdistribution.py +++ b/sharpy/postproc/liftdistribution.py @@ -44,7 +44,7 @@ def __init__(self): self.folder = None self.caller = None - def initialise(self, data, caller=None): + def initialise(self, data, custom_settings=None, restart=False, caller=None): self.data = data self.settings = data.settings[self.solver_id] settings_utils.to_custom_types(self.settings, self.settings_types, self.settings_default) @@ -53,7 +53,7 @@ def initialise(self, data, caller=None): if not os.path.exists(self.folder): os.makedirs(self.folder) - def run(self, online=False): + def run(self, **kwargs): self.lift_distribution(self.data.structure.timestep_info[self.data.ts], self.data.aero.timestep_info[self.data.ts]) return self.data diff --git a/sharpy/sharpy_main.py b/sharpy/sharpy_main.py index dd9c3d20f..6bb4b4f5d 100644 --- a/sharpy/sharpy_main.py +++ b/sharpy/sharpy_main.py @@ -87,18 +87,32 @@ def main(args=None, sharpy_input_dict=None): if args.input_filename == '': parser.error('input_filename is a required argument of SHARPy.') settings = input_arg.read_settings(args) + missing_solvers = False if args.restart is None: # run preSHARPy data = PreSharpy(settings) + solvers = dict() + restart = False else: try: with open(args.restart, 'rb') as restart_file: data = pickle.load(restart_file) + try: + solvers = pickle.load(restart_file) + except EOFError: + # For backwards compatibility + missing_solvers = True + solvers = dict() + cout.cout_wrap('Solvers not found in Pickle file. Using the settings in *.sharpy file.') + if "UpdatePickle" in solvers.keys(): + # For backwards compatibility + missing_solvers = True + solvers = dict() except FileNotFoundError: raise FileNotFoundError('The file specified for the snapshot \ restart (-r) does not exist. Please check.') - + restart = True # update the settings data.update_settings(settings) @@ -111,11 +125,22 @@ def main(args=None, sharpy_input_dict=None): # for it in range(self.num_steps): # data.structure.dynamic_input.append(dict()) + # Restart the solvers + old_solvers_list = list(solvers.keys()) + for old_solver_name in old_solvers_list: + if old_solver_name not in settings['SHARPy']['flow']: + del solvers[old_solver_name] + # Loop for the solvers specified in *.sharpy['SHARPy']['flow'] for solver_name in settings['SHARPy']['flow']: - solver = solver_interface.initialise_solver(solver_name) - solver.initialise(data) - data = solver.run() + if (args.restart is None) or (solver_name not in solvers.keys()) or (missing_solvers): + solvers[solver_name] = solver_interface.initialise_solver(solver_name) + if missing_solvers: + solvers[solver_name].initialise(data, restart=False) + else: + solvers[solver_name].initialise(data, restart=restart) + data = solvers[solver_name].run(solvers=solvers) + solvers[solver_name].teardown() cpu_time = time.process_time() - t wall_time = time.perf_counter() - t0_wall diff --git a/sharpy/solvers/aerogridloader.py b/sharpy/solvers/aerogridloader.py index 84110f475..af8098676 100644 --- a/sharpy/solvers/aerogridloader.py +++ b/sharpy/solvers/aerogridloader.py @@ -105,7 +105,7 @@ def initialise(self, data, restart=False): self.settings['wake_shape_generator_input'], restart=restart) - def run(self): + def run(self, **kwargs): self.data.aero = aerogrid.Aerogrid() self.data.aero.generate(self.data_dict, self.data.structure, diff --git a/sharpy/solvers/gridloader.py b/sharpy/solvers/gridloader.py index ee5803faa..0caad5dc4 100644 --- a/sharpy/solvers/gridloader.py +++ b/sharpy/solvers/gridloader.py @@ -41,7 +41,7 @@ def __init__(self): self.file_name = '' self.data_dict = dict() - def initialise(self, data): + def initialise(self, data, restart=False): self.data = data self.read_input_files() diff --git a/sharpy/solvers/nonliftingbodygridloader.py b/sharpy/solvers/nonliftingbodygridloader.py index 75a73d74d..85f80bac2 100644 --- a/sharpy/solvers/nonliftingbodygridloader.py +++ b/sharpy/solvers/nonliftingbodygridloader.py @@ -35,7 +35,7 @@ def __init__(self): # nonlifting_body storage self.nonlifting_body = None - def run(self): + def run(self, **kwargs): self.data.nonlifting_body = nonliftingbodygrid.NonliftingBodyGrid() self.data.nonlifting_body.generate(self.data_dict, self.data.structure, From 3591000ae395f6568d1c2975b37ad03580962666 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stefanie=20D=C3=BCssler?= Date: Wed, 13 Sep 2023 15:51:45 -0400 Subject: [PATCH 193/232] remove [nonlifting] unused import in nonliftingbodygrid --- sharpy/aero/models/nonliftingbodygrid.py | 1 - 1 file changed, 1 deletion(-) diff --git a/sharpy/aero/models/nonliftingbodygrid.py b/sharpy/aero/models/nonliftingbodygrid.py index 3a621d5a0..f2ed885b0 100644 --- a/sharpy/aero/models/nonliftingbodygrid.py +++ b/sharpy/aero/models/nonliftingbodygrid.py @@ -6,7 +6,6 @@ from sharpy.aero.models.grid import Grid from sharpy.utils.datastructures import NonliftingBodyTimeStepInfo import numpy as np -from scipy.optimize import fsolve import sharpy.utils.algebra as algebra From 9d7376c18958c510202cc9ae76e4e3182999b2ab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stefanie=20D=C3=BCssler?= Date: Thu, 14 Sep 2023 00:34:38 -0400 Subject: [PATCH 194/232] documentation [mapping] add comments to function --- sharpy/aero/utils/mapping.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/sharpy/aero/utils/mapping.py b/sharpy/aero/utils/mapping.py index c7792ef3d..098f088c7 100644 --- a/sharpy/aero/utils/mapping.py +++ b/sharpy/aero/utils/mapping.py @@ -41,6 +41,7 @@ def aero2struct_force_mapping(aero_forces, conn (np.ndarray): Connectivities matrix cag (np.ndarray): Transformation matrix between inertial and body-attached reference ``A`` data_dict (dict): Dictionary containing the grid's information. + skip_moments_generated_by_forces (bool): Flag to skip local moment calculation. Returns: np.ndarray: structural forces in an ``n_node x 6`` vector @@ -72,6 +73,13 @@ def aero2struct_force_mapping(aero_forces, for i_m in range(n_m): struct_forces[i_global_node, 0:3] += np.dot(cbg, aero_forces[i_surf][0:3, i_m, i_n]) struct_forces[i_global_node, 3:6] += np.dot(cbg, aero_forces[i_surf][3:6, i_m, i_n]) + """ + The calculation of the local moment is skipped for fuselage bodies, since this + leads to noticeably asymmetric aeroforces. This transitional solution makes + sense since we have only considered the stiff fuselage so far and the pitching + moment coming from the fuselage is mainly caused by the longitudinal force distribution. + TODO: Correct the calculation of the local moment for the fuselage model. + """ if not skip_moments_generated_by_forces: chi_g = zeta[i_surf][:, i_m, i_n] - np.dot(cag.T, pos_def[i_global_node, :]) struct_forces[i_global_node, 3:6] += np.dot(cbg, algebra.cross3(chi_g, aero_forces[i_surf][0:3, i_m, i_n])) From 5d211cc1ff2be692b3285fdd458a4d4217459d23 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stefanie=20D=C3=BCssler?= Date: Mon, 18 Sep 2023 14:46:39 +0200 Subject: [PATCH 195/232] refactor [nonlifting] grid generation comments - adjusted to equations in a document --- sharpy/aero/models/nonliftingbodygrid.py | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/sharpy/aero/models/nonliftingbodygrid.py b/sharpy/aero/models/nonliftingbodygrid.py index f2ed885b0..7661fdf59 100644 --- a/sharpy/aero/models/nonliftingbodygrid.py +++ b/sharpy/aero/models/nonliftingbodygrid.py @@ -51,6 +51,8 @@ def get_zeta_and_zeta_dot(self,i_surf, structure_tstep): cga_rotation_matrix = structure_tstep.cga() for node_counter, i_global_node in enumerate(self.aero2struct_mapping[i_surf]): + # 1) Set B-Frame position + # 1a) get cross-sectional fuselage geometry at node if self.data_dict["shape"].decode() == 'specific': a_ellipse = self.data_dict["a_ellipse"][i_global_node] b_ellipse = self.data_dict["b_ellipse"][i_global_node] @@ -65,31 +67,35 @@ def get_zeta_and_zeta_dot(self,i_surf, structure_tstep): radius = self.data_dict["radius"][i_global_node] z_0 = 0 - - # get nodes position in B frame + # 1b) Get nodes position in B frame matrix_nodes[1, :, node_counter] = radius*array_cos_phi matrix_nodes[2, :, node_counter] = radius*array_sin_phi + z_0 - # convert position from B to A frame + # 2) A frame + # 2a) Convert structural position from B to A frame i_elem, i_local_node = self.get_elment_and_local_node_id(i_surf, i_global_node) psi_node = structure_tstep.psi[i_elem, i_local_node,:] - psi_dot_node = structure_tstep.psi_dot[i_elem, i_local_node,:] - omega_a = algebra.crv_dot2omega(psi_node, psi_dot_node) if not (psi_node == [0, 0, 0]).all(): # just perform roation from B to A if psi not 0 Cab = algebra.crv2rotation(psi_node) for idx in range(numb_radial_nodes): matrix_nodes[:, idx, node_counter] = np.dot(Cab, matrix_nodes[:, idx, node_counter]) + # 2b) Add beam displacements (expressed in A-frame) for dim in range(3): matrix_nodes[dim, :, node_counter] += structure_tstep.pos[i_global_node,dim] - # get zeta dot in A frame (velocity due to pos_dot) + # 2c) Add structural beam velocities (expressed in A-frame) + for dim in range(3): + # velocity due to pos_dot matrix_nodes_dot[dim, :, node_counter] += structure_tstep.pos_dot[i_global_node, dim] - + # 2d) Add effect of structural beam rotations an node velocity (expressed in A-frame) + psi_dot_node = structure_tstep.psi_dot[i_elem, i_local_node,:] + omega_a = algebra.crv_dot2omega(psi_node, psi_dot_node) for idx in range(numb_radial_nodes): - # get zeta dot in A frame (velocity due to psi_dot)) matrix_nodes_dot[:, idx, node_counter] += (np.dot(algebra.skew(omega_a), matrix_nodes[:, idx, node_counter])) - # convert position from A to G frame + + # 3) Convert position and velocities from A to G frame + for idx in range(numb_radial_nodes): matrix_nodes[:, idx, node_counter] = np.dot(cga_rotation_matrix, matrix_nodes[:, idx, node_counter]) matrix_nodes_dot[:, idx, node_counter] = np.dot(cga_rotation_matrix, From 02acde7c01417a837e8dab92d829b64091c67515 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stefanie=20D=C3=BCssler?= Date: Mon, 18 Sep 2023 14:47:31 +0200 Subject: [PATCH 196/232] fix [aerogrid] add user-specified chordwise panel distribution - must have been removed during a merge process as it is included in the current SHARPy version --- sharpy/aero/models/aerogrid.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/sharpy/aero/models/aerogrid.py b/sharpy/aero/models/aerogrid.py index 8cd84e574..4ba0d4f74 100644 --- a/sharpy/aero/models/aerogrid.py +++ b/sharpy/aero/models/aerogrid.py @@ -390,6 +390,9 @@ def generate_strip(node_info, airfoil_db, aligned_grid, strip_coordinates_b_frame[1, :] = np.linspace(0.0, 1.0, node_info['M'] + 1) elif node_info['M_distribution'] == '1-cos': domain = np.linspace(0, 1.0, node_info['M'] + 1) + strip_coordinates_b_frame[1, :] = 0.5*(1.0 - np.cos(domain*np.pi)) + elif node_info['M_distribution'].lower() == 'user_defined': + strip_coordinates_b_frame[1,:] = node_info['user_defined_m_distribution'] else: raise NotImplemented('M_distribution is ' + node_info['M_distribution'] + ' and it is not yet supported') From d8d77807b836adc4f0fc3ea12be25b9e15073841 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stefanie=20D=C3=BCssler?= Date: Wed, 20 Sep 2023 00:08:34 +0200 Subject: [PATCH 197/232] update UVLM documentation - also includes adjustments made in uvlmlib after renaming some interface functions --- lib/UVLM | 2 +- sharpy/aero/utils/uvlmlib.py | 12 ++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/lib/UVLM b/lib/UVLM index c6df90830..70b33d4fa 160000 --- a/lib/UVLM +++ b/lib/UVLM @@ -1 +1 @@ -Subproject commit c6df908303a3791af649726c7c1c07363ac5bf85 +Subproject commit 70b33d4fa9b41c29503dfd920b9ddac7119e29b1 diff --git a/sharpy/aero/utils/uvlmlib.py b/sharpy/aero/utils/uvlmlib.py index 2734119a1..9f065c0ae 100644 --- a/sharpy/aero/utils/uvlmlib.py +++ b/sharpy/aero/utils/uvlmlib.py @@ -246,7 +246,7 @@ def vlm_solver(ts_info, options): ts_info.remove_ctypes_pointers() def vlm_solver_nonlifting_body(ts_info, options): - run_VLM_nonlifting = UvlmLib.run_VLM_nonlifting_body + run_linear_source_panel_method = UvlmLib.run_linear_source_panel_method_body vmopts = VMopts() vmopts.set_options(options, n_surfaces_nonlifting = ts_info.n_surf) @@ -257,7 +257,7 @@ def vlm_solver_nonlifting_body(ts_info, options): flightconditions.uinf_direction = np.ctypeslib.as_ctypes(ts_info.u_ext[0][:, 0, 0]/flightconditions.uinf) ts_info.generate_ctypes_pointers() - run_VLM_nonlifting(ct.byref(vmopts), + run_linear_source_panel_method(ct.byref(vmopts), ct.byref(flightconditions), ts_info.ct_p_dimensions, ts_info.ct_p_zeta, @@ -268,8 +268,8 @@ def vlm_solver_nonlifting_body(ts_info, options): ts_info.remove_ctypes_pointers() def vlm_solver_lifting_and_nonlifting_bodies(ts_info_lifting, ts_info_nonlifting, options): - run_VLM_lifting_and_nonlifting = UvlmLib.run_VLM_lifting_and_nonlifting_bodies - run_VLM_lifting_and_nonlifting.restype = None + run_VLM_coupled_with_LSPM = UvlmLib.run_VLM_coupled_with_LSPM_bodies + run_VLM_coupled_with_LSPM.restype = None vmopts = VMopts() vmopts.set_options(options, n_surfaces = ts_info_lifting.n_surf, n_surfaces_nonlifting = ts_info_nonlifting.n_surf) @@ -283,7 +283,7 @@ def vlm_solver_lifting_and_nonlifting_bodies(ts_info_lifting, ts_info_nonlifting p_centre_rot = options['centre_rot_g'].ctypes.data_as(ct.POINTER(ct.c_double)) ts_info_lifting.generate_ctypes_pointers() ts_info_nonlifting.generate_ctypes_pointers() - run_VLM_lifting_and_nonlifting(ct.byref(vmopts), + run_VLM_coupled_with_LSPM(ct.byref(vmopts), ct.byref(flightconditions), ts_info_lifting.ct_p_dimensions, ts_info_lifting.ct_p_dimensions_star, @@ -374,7 +374,7 @@ def uvlm_solver_lifting_and_nonlifting(i_iter, ts_info, ts_info_nonlifting, stru convect_wake = convect_wake, n_span_panels_wo_u_ind=4) uvmopts.only_lifting = ct.c_bool(False) - run_UVLM = UvlmLib.run_UVLM_lifting_and_nonlifting + run_UVLM = UvlmLib.run_UVLM_coupled_with_LSPM run_UVLM.restype = None flightconditions = FlightConditions() From a18a5e721c46d66994bc1205d5b11aec7bd3c55c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stefanie=20D=C3=BCssler?= Date: Wed, 20 Sep 2023 00:32:33 +0200 Subject: [PATCH 198/232] refactor uvlmlib - improve flight condition definitions - remove unncessary comments --- sharpy/aero/utils/uvlmlib.py | 64 +++++++++--------------------------- 1 file changed, 16 insertions(+), 48 deletions(-) diff --git a/sharpy/aero/utils/uvlmlib.py b/sharpy/aero/utils/uvlmlib.py index 2734119a1..32c329a58 100644 --- a/sharpy/aero/utils/uvlmlib.py +++ b/sharpy/aero/utils/uvlmlib.py @@ -4,19 +4,14 @@ import ctypes as ct from ctypes import * import numpy as np -import platform -import os -from sharpy.utils.constants import NDIM, vortex_radius_def +from sharpy.utils.constants import vortex_radius_def try: UvlmLib = ct_utils.import_ctypes_lib(SharpyDir + '/UVLM', 'libuvlm') except OSError: UvlmLib = ct_utils.import_ctypes_lib(SharpyDir + '/lib/UVLM/lib', 'libuvlm') - - # TODO: Combine VMOpts and UVMOpts (Class + inheritance)? -# TODO: Combine solver functions (e.g. vlm solver is able to start nonlifting only, lifitng only, nonlifting and lifting coupled) class VMopts(ct.Structure): """ctypes definition for VMopts class @@ -118,8 +113,6 @@ def set_options(self, options, n_surfaces = 0, n_surfaces_nonlifting = 0): class UVMopts(ct.Structure): - # TODO: add set_options function - # TODO: possible to combine with VMopts? _fields_ = [("dt", ct.c_double), ("NumCores", ct.c_uint), ("NumSurfaces", ct.c_uint), @@ -209,23 +202,26 @@ class FlightConditions(ct.Structure): ("rho", ct.c_double), ("c_ref", ct.c_double)] - def __init__(self): + def __init__(self, rho, vec_u_inf): ct.Structure.__init__(self) + self.set_flight_conditions(rho, vec_u_inf) + + def set_flight_conditions(self, rho, vec_u_inf): + self.rho = rho + self.uinf = np.ctypeslib.as_ctypes(np.linalg.norm(vec_u_inf)) + self.uinf_direction = np.ctypeslib.as_ctypes(vec_u_inf/self.uinf) + # type for 2d integer matrix t_2int = ct.POINTER(ct.c_int)*2 def vlm_solver(ts_info, options): run_VLM = UvlmLib.run_VLM - run_VLM.restype = None vmopts = VMopts() vmopts.set_options(options, n_surfaces = ts_info.n_surf) - flightconditions = FlightConditions() - flightconditions.rho = options['rho'] - flightconditions.uinf = np.ctypeslib.as_ctypes(np.linalg.norm(ts_info.u_ext[0][:, 0, 0])) - flightconditions.uinf_direction = np.ctypeslib.as_ctypes(ts_info.u_ext[0][:, 0, 0]/flightconditions.uinf) + flightconditions = FlightConditions(options['rho'], ts_info.u_ext[0][:, 0, 0]) p_rbm_vel_g = options['rbm_vel_g'].ctypes.data_as(ct.POINTER(ct.c_double)) p_centre_rot_g = options['centre_rot_g'].ctypes.data_as(ct.POINTER(ct.c_double)) @@ -251,10 +247,7 @@ def vlm_solver_nonlifting_body(ts_info, options): vmopts = VMopts() vmopts.set_options(options, n_surfaces_nonlifting = ts_info.n_surf) - flightconditions = FlightConditions() - flightconditions.rho = options['rho'] - flightconditions.uinf = np.ctypeslib.as_ctypes(np.linalg.norm(ts_info.u_ext[0][:, 0, 0])) - flightconditions.uinf_direction = np.ctypeslib.as_ctypes(ts_info.u_ext[0][:, 0, 0]/flightconditions.uinf) + flightconditions = FlightConditions(options['rho'], ts_info.u_ext[0][:, 0, 0]) ts_info.generate_ctypes_pointers() run_VLM_nonlifting(ct.byref(vmopts), @@ -269,15 +262,11 @@ def vlm_solver_nonlifting_body(ts_info, options): def vlm_solver_lifting_and_nonlifting_bodies(ts_info_lifting, ts_info_nonlifting, options): run_VLM_lifting_and_nonlifting = UvlmLib.run_VLM_lifting_and_nonlifting_bodies - run_VLM_lifting_and_nonlifting.restype = None vmopts = VMopts() vmopts.set_options(options, n_surfaces = ts_info_lifting.n_surf, n_surfaces_nonlifting = ts_info_nonlifting.n_surf) - flightconditions = FlightConditions() - flightconditions.rho = options['rho'] - flightconditions.uinf = np.ctypeslib.as_ctypes(np.linalg.norm(ts_info_lifting.u_ext[0][:, 0, 0])) - flightconditions.uinf_direction = np.ctypeslib.as_ctypes(ts_info_lifting.u_ext[0][:, 0, 0]/flightconditions.uinf) + flightconditions = FlightConditions(options['rho'], ts_info_lifting.u_ext[0][:, 0, 0]) p_rbm_vel_g = options['rbm_vel_g'].ctypes.data_as(ct.POINTER(ct.c_double)) p_centre_rot = options['centre_rot_g'].ctypes.data_as(ct.POINTER(ct.c_double)) @@ -317,7 +306,6 @@ def uvlm_solver(i_iter, ts_info, struct_ts_info, options, convect_wake=True, dt= run_UVLM = UvlmLib.run_UVLM - run_UVLM.restype = None uvmopts = UVMopts() uvmopts.set_options(options, @@ -327,18 +315,10 @@ def uvlm_solver(i_iter, ts_info, struct_ts_info, options, convect_wake=True, dt= convect_wake = convect_wake, n_span_panels_wo_u_ind=0) + flightconditions = FlightConditions(options['rho'], ts_info.u_ext[0][:, 0, 0]) - flightconditions = FlightConditions() - flightconditions.rho = options['rho'] - flightconditions.uinf = np.ctypeslib.as_ctypes(np.linalg.norm(ts_info.u_ext[0][:, 0, 0])) - # direction = np.array([1.0, 0, 0]) - flightconditions.uinf_direction = np.ctypeslib.as_ctypes(ts_info.u_ext[0][:, 0, 0]/flightconditions.uinf) - # flightconditions.uinf_direction = np.ctypeslib.as_ctypes(direction) - - i = ct.c_uint(i_iter) ts_info.generate_ctypes_pointers() - # previous_ts_info.generate_ctypes_pointers() run_UVLM(ct.byref(uvmopts), ct.byref(flightconditions), ts_info.ct_p_dimensions, @@ -375,14 +355,8 @@ def uvlm_solver_lifting_and_nonlifting(i_iter, ts_info, ts_info_nonlifting, stru n_span_panels_wo_u_ind=4) uvmopts.only_lifting = ct.c_bool(False) run_UVLM = UvlmLib.run_UVLM_lifting_and_nonlifting - run_UVLM.restype = None - flightconditions = FlightConditions() - flightconditions.rho = options['rho'] - flightconditions.uinf = np.ctypeslib.as_ctypes(np.linalg.norm(ts_info.u_ext[0][:, 0, 0])) - # direction = np.array([1.0, 0, 0]) - flightconditions.uinf_direction = np.ctypeslib.as_ctypes(ts_info.u_ext[0][:, 0, 0]/flightconditions.uinf) - # flightconditions.uinf_direction = np.ctypeslib.as_ctypes(direction) + flightconditions = FlightConditions(options['rho'], ts_info.u_ext[0][:, 0, 0]) rbm_vel = struct_ts_info.for_vel.copy() rbm_vel[0:3] = np.dot(struct_ts_info.cga(), rbm_vel[0:3]) @@ -429,7 +403,6 @@ def uvlm_calculate_unsteady_forces(ts_info, convect_wake=True, dt=None): calculate_unsteady_forces = UvlmLib.calculate_unsteady_forces - calculate_unsteady_forces.restype = None uvmopts = UVMopts() if dt is None: @@ -446,10 +419,8 @@ def uvlm_calculate_unsteady_forces(ts_info, uvmopts.convect_wake = ct.c_bool(convect_wake) uvmopts.vortex_radius = ct.c_double(options['vortex_radius']) - flightconditions = FlightConditions() - flightconditions.rho = options['rho'] - flightconditions.uinf = np.ctypeslib.as_ctypes(np.linalg.norm(ts_info.u_ext[0][:, 0, 0])) - flightconditions.uinf_direction = np.ctypeslib.as_ctypes(ts_info.u_ext[0][:, 0, 0]/flightconditions.uinf) + + flightconditions = FlightConditions(options['rho'], ts_info.u_ext[0][:, 0, 0]) rbm_vel = struct_ts_info.for_vel.copy() rbm_vel[0:3] = np.dot(struct_ts_info.cga(), rbm_vel[0:3]) @@ -478,7 +449,6 @@ def uvlm_calculate_unsteady_forces(ts_info, def uvlm_calculate_incidence_angle(ts_info, struct_ts_info): calculate_incidence_angle = UvlmLib.UVLM_check_incidence_angle - calculate_incidence_angle.restype = None rbm_vel = struct_ts_info.for_vel.copy() rbm_vel[0:3] = np.dot(struct_ts_info.cga(), rbm_vel[0:3]) @@ -521,7 +491,6 @@ def uvlm_calculate_total_induced_velocity_at_points(ts_info, """ calculate_uind_at_points = UvlmLib.total_induced_velocity_at_points - calculate_uind_at_points.restype = None uvmopts = UVMopts() uvmopts.NumSurfaces = ct.c_uint(ts_info.n_surf) @@ -670,7 +639,6 @@ def get_induced_velocity_cpp(maps, zeta, gamma, zeta_target, """ call_ind_vel = UvlmLib.call_ind_vel - call_ind_vel.restype = None assert zeta_target.flags['C_CONTIGUOUS'], "Input not C contiguous" From 6f91fa1e2caae3abaf00a824e22391d83b69b09f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stefanie=20D=C3=BCssler?= Date: Wed, 20 Sep 2023 08:38:34 +0200 Subject: [PATCH 199/232] update SHARPy docs and readme with fuselage module --- README.md | 2 +- docs/source/content/casefiles.rst | 49 +++++++++++++++++++++++++++++++ docs/source/index.rst | 2 +- 3 files changed, 51 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index ccab553e1..75c434a80 100644 --- a/README.md +++ b/README.md @@ -44,7 +44,7 @@ wings and wind turbines. In addition, it supports linearisation of these nonline arbitrary conditions and includes various tools such as: model reduction or frequency analysis. In short, SHARPy offers (amongst others) the following solutions to the user: -* Static aerodynamic, structural and aeroelastic solutions +* Static aerodynamic, structural and aeroelastic solutions including fuselage effects * Finding trim conditions for aeroelastic configurations * Nonlinear, dynamic time domain simulations under a large number of conditions such as: + Prescribed trajectories. diff --git a/docs/source/content/casefiles.rst b/docs/source/content/casefiles.rst index db537ac60..da570fc42 100644 --- a/docs/source/content/casefiles.rst +++ b/docs/source/content/casefiles.rst @@ -321,6 +321,55 @@ Item by item: should be included for each airfoil defined. Each entry consists of a 4-column table. The first column corresponds to the angle of attack (in radians) and then the ``C_L``, ``C_D`` and ``C_M``. +Nonlifting Body file +----------------- + +All the nonlifting body data is contained in ``case.nonlifting_body.h5``. + +The idea behind the structure of the model definition of nonlifting bodies in SHARPy is similiar to the aerodynamic +one for lifting surfaces. Again for each node or element we define several parameters. + +Item by item: + +* ``shape``: Type of geometrical form of 3D nonlifting body. + + In the ``nonlifting_body.h5`` file, there is a Group called ``shape``. The shape indicates the geometrical form of the + nonlifting body. Common options for this parameter are ``'cylindrical'`` and ``'specific'``. For the former, SHARPy + expects rotational symmetric cross-section for which only a radius is required for each node. For the ``'specific'`` + option, SHARPy can create a more unique nonlifting body geometry by creating an ellipse at each fuselage defined by + :math:`\frac{y^2}{a^2}+\frac{z^2}{b^2}=1` with the given ellipse axis lengths :math:`a` and :math:`b`. Further, SHARPy + lets define the user to create a vertical offset from the node with :math:`z_0`. + +* ``radius [num_node]``: Cross-sectional radius. + + Is an array with the radius of specified for each fuselage node. + +* ``a_ellipse [num_node]``: Elliptical axis lengths along the local y-axis. + + Is an array with the length of the elliptical axis along the y-axis. + +* ``b_ellipse [num_node]``: Elliptical axis lengths along the local z-axis. + + Is an array with the length of the elliptical axis along the z-axis. + +* ``z_0_ellipse [num_node]``: Vertical offset of the ellipse center from the beam node. + + Is an array with the vertical offset of the center of the elliptical cross-sectoin from the fuselage node. + +* ``surface_m [num_surfaces]``: Radial panelling. + + Is an integer array with the number of radial panels for every surface. + +* ``nonlifting_body_node [num_node]``: Nonlifting body node definition. + + Is a boolean (``True`` or ``False``) array that indicates if that node has a nonlifting body + attached to it. + +* ``surface_distribution [num_elem]``: Nonlifting Surface integer array. + + It contains the index of the surface the element belongs to. Surfaces need to be continuous, so please note + that if your beam numbering is not continuous, you need to make a surface per continuous section. + Time-varying force input file (``.dyn.h5``) ------------------------------------------- diff --git a/docs/source/index.rst b/docs/source/index.rst index 6ae696e07..adbc7cf5d 100644 --- a/docs/source/index.rst +++ b/docs/source/index.rst @@ -29,7 +29,7 @@ SHARPy is an aeroelastic analysis package currently under development at the Dep Imperial College London. It can be used for the structural, aerodynamic, aeroelastic and flight dynamics analysis of flexible aircraft, flying wings and wind turbines. Amongst other capabilities_, it offers the following solutions to the user: -* Static aerodynamic, structural and aeroelastic solutions +* Static aerodynamic, structural and aeroelastic solutions including fuselage effects * Finding trim conditions for aeroelastic configurations * Nonlinear, dynamic time domain simulations under a large number of conditions such as: From ad9ab63f4353bbb8da9e0e5f6e70a4aef0d329fd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stefanie=20D=C3=BCssler?= Date: Wed, 20 Sep 2023 08:51:30 +0200 Subject: [PATCH 200/232] remoce [nonlifting] outdated comments in nonlifting related codes --- lib/UVLM | 2 +- sharpy/aero/models/nonliftingbodygrid.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/UVLM b/lib/UVLM index 70b33d4fa..91ec36ae5 160000 --- a/lib/UVLM +++ b/lib/UVLM @@ -1 +1 @@ -Subproject commit 70b33d4fa9b41c29503dfd920b9ddac7119e29b1 +Subproject commit 91ec36ae5401caa79733f97545b14ca1f4ef4515 diff --git a/sharpy/aero/models/nonliftingbodygrid.py b/sharpy/aero/models/nonliftingbodygrid.py index 7661fdf59..a4c4d9f61 100644 --- a/sharpy/aero/models/nonliftingbodygrid.py +++ b/sharpy/aero/models/nonliftingbodygrid.py @@ -21,7 +21,7 @@ def __init__(self): self.grid_type = 'nonlifting_body' - def generate(self, data_dict, beam, nonlifting_body_settings, ts): ##input? + def generate(self, data_dict, beam, nonlifting_body_settings, ts): super().generate(data_dict, beam, nonlifting_body_settings, ts) # allocating initial grid storage From 4085e943ae61677b0f0deaa7997a428d25b200c2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stefanie=20D=C3=BCssler?= Date: Wed, 20 Sep 2023 09:27:59 +0200 Subject: [PATCH 201/232] fix [uvlmlib] name of interface functions - copy and paste went horribly wrong before - unittests are running for these cases again --- sharpy/aero/utils/uvlmlib.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sharpy/aero/utils/uvlmlib.py b/sharpy/aero/utils/uvlmlib.py index 212a21c81..7e25b48a6 100644 --- a/sharpy/aero/utils/uvlmlib.py +++ b/sharpy/aero/utils/uvlmlib.py @@ -242,7 +242,7 @@ def vlm_solver(ts_info, options): ts_info.remove_ctypes_pointers() def vlm_solver_nonlifting_body(ts_info, options): - run_linear_source_panel_method = UvlmLib.run_linear_source_panel_method_body + run_linear_source_panel_method = UvlmLib.run_linear_source_panel_method vmopts = VMopts() vmopts.set_options(options, n_surfaces_nonlifting = ts_info.n_surf) @@ -261,7 +261,7 @@ def vlm_solver_nonlifting_body(ts_info, options): ts_info.remove_ctypes_pointers() def vlm_solver_lifting_and_nonlifting_bodies(ts_info_lifting, ts_info_nonlifting, options): - run_VLM_coupled_with_LSPM = UvlmLib.run_VLM_coupled_with_LSPM_bodies + run_VLM_coupled_with_LSPM = UvlmLib.run_VLM_coupled_with_LSPM vmopts = VMopts() vmopts.set_options(options, n_surfaces = ts_info_lifting.n_surf, n_surfaces_nonlifting = ts_info_nonlifting.n_surf) From 24e3dc9e548b595d27d642a76ebe9dcc90e318f8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stefanie=20D=C3=BCssler?= Date: Wed, 20 Sep 2023 09:30:09 +0200 Subject: [PATCH 202/232] refactor [uvlmlib] add function for rigid body velocity ctype pointer --- sharpy/aero/utils/uvlmlib.py | 35 ++++++++++++----------------------- 1 file changed, 12 insertions(+), 23 deletions(-) diff --git a/sharpy/aero/utils/uvlmlib.py b/sharpy/aero/utils/uvlmlib.py index 7e25b48a6..d9f8e54fc 100644 --- a/sharpy/aero/utils/uvlmlib.py +++ b/sharpy/aero/utils/uvlmlib.py @@ -298,13 +298,10 @@ def vlm_solver_lifting_and_nonlifting_bodies(ts_info_lifting, ts_info_nonlifting def uvlm_solver(i_iter, ts_info, struct_ts_info, options, convect_wake=True, dt=None): - rbm_vel = struct_ts_info.for_vel.copy() - rbm_vel[0:3] = np.dot(struct_ts_info.cga(), rbm_vel[0:3]) - rbm_vel[3:6] = np.dot(struct_ts_info.cga(), rbm_vel[3:6]) - p_rbm_vel = rbm_vel.ctypes.data_as(ct.POINTER(ct.c_double)) + + p_rbm_vel = get_ctype_pointer_of_rbm_vel_in_G_frame(struct_ts_info.for_vel.copy(), struct_ts_info.cga()) p_centre_rot = options['centre_rot'].ctypes.data_as(ct.POINTER(ct.c_double)) - run_UVLM = UvlmLib.run_UVLM uvmopts = UVMopts() @@ -340,10 +337,8 @@ def uvlm_solver(i_iter, ts_info, struct_ts_info, options, convect_wake=True, dt= ts_info.remove_ctypes_pointers() def uvlm_solver_lifting_and_nonlifting(i_iter, ts_info, ts_info_nonlifting, struct_ts_info, options, convect_wake=True, dt=None): - rbm_vel = struct_ts_info.for_vel.copy() - rbm_vel[0:3] = np.dot(struct_ts_info.cga(), rbm_vel[0:3]) - rbm_vel[3:6] = np.dot(struct_ts_info.cga(), rbm_vel[3:6]) - p_rbm_vel = rbm_vel.ctypes.data_as(ct.POINTER(ct.c_double)) + + p_rbm_vel = get_ctype_pointer_of_rbm_vel_in_G_frame(struct_ts_info.for_vel.copy(), struct_ts_info.cga()) p_centre_rot = options['centre_rot'].ctypes.data_as(ct.POINTER(ct.c_double)) uvmopts = UVMopts() @@ -358,12 +353,6 @@ def uvlm_solver_lifting_and_nonlifting(i_iter, ts_info, ts_info_nonlifting, stru flightconditions = FlightConditions(options['rho'], ts_info.u_ext[0][:, 0, 0]) - rbm_vel = struct_ts_info.for_vel.copy() - rbm_vel[0:3] = np.dot(struct_ts_info.cga(), rbm_vel[0:3]) - rbm_vel[3:6] = np.dot(struct_ts_info.cga(), rbm_vel[3:6]) - p_rbm_vel = rbm_vel.ctypes.data_as(ct.POINTER(ct.c_double)) - p_centre_rot = options['centre_rot'].ctypes.data_as(ct.POINTER(ct.c_double)) - i = ct.c_uint(i_iter) ts_info.generate_ctypes_pointers() ts_info_nonlifting.generate_ctypes_pointers() @@ -422,10 +411,7 @@ def uvlm_calculate_unsteady_forces(ts_info, flightconditions = FlightConditions(options['rho'], ts_info.u_ext[0][:, 0, 0]) - rbm_vel = struct_ts_info.for_vel.copy() - rbm_vel[0:3] = np.dot(struct_ts_info.cga(), rbm_vel[0:3]) - rbm_vel[3:6] = np.dot(struct_ts_info.cga(), rbm_vel[3:6]) - p_rbm_vel = rbm_vel.ctypes.data_as(ct.POINTER(ct.c_double)) + p_rbm_vel = get_ctype_pointer_of_rbm_vel_in_G_frame(struct_ts_info.for_vel.copy(), struct_ts_info.cga()) for i_surf in range(ts_info.n_surf): ts_info.dynamic_forces[i_surf].fill(0.0) @@ -450,10 +436,7 @@ def uvlm_calculate_incidence_angle(ts_info, struct_ts_info): calculate_incidence_angle = UvlmLib.UVLM_check_incidence_angle - rbm_vel = struct_ts_info.for_vel.copy() - rbm_vel[0:3] = np.dot(struct_ts_info.cga(), rbm_vel[0:3]) - rbm_vel[3:6] = np.dot(struct_ts_info.cga(), rbm_vel[3:6]) - p_rbm_vel = rbm_vel.ctypes.data_as(ct.POINTER(ct.c_double)) + p_rbm_vel = get_ctype_pointer_of_rbm_vel_in_G_frame(struct_ts_info.for_vel.copy(), struct_ts_info.cga()) n_surf = ct.c_uint(ts_info.n_surf) @@ -753,3 +736,9 @@ def dvinddzeta_cpp(zetac, surf_in, is_bound, ) return der_coll, der_vert + +def get_ctype_pointer_of_rbm_vel_in_G_frame(rbm_vel, cga): + rbm_vel[0:3] = np.dot(cga, rbm_vel[0:3]) + rbm_vel[3:6] = np.dot(cga, rbm_vel[3:6]) + p_rbm_vel = rbm_vel.ctypes.data_as(ct.POINTER(ct.c_double)) + return p_rbm_vel \ No newline at end of file From fbedff3d51ea52a4f8d79ef94e711a00835047e3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stefanie=20D=C3=BCssler?= Date: Wed, 20 Sep 2023 09:31:14 +0200 Subject: [PATCH 203/232] reefactor [uvlmlib] set options in the appropriate function --- sharpy/aero/utils/uvlmlib.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/sharpy/aero/utils/uvlmlib.py b/sharpy/aero/utils/uvlmlib.py index d9f8e54fc..5946e557f 100644 --- a/sharpy/aero/utils/uvlmlib.py +++ b/sharpy/aero/utils/uvlmlib.py @@ -166,7 +166,8 @@ def set_options(self, n_surfaces_nonlifting = 0, dt = None, convect_wake = False, - n_span_panels_wo_u_ind = 0): + n_span_panels_wo_u_ind = 0, + lifting_only=True): if dt is None: self.dt = ct.c_double(options["dt"]) else: @@ -190,7 +191,7 @@ def set_options(self, self.quasi_steady = ct.c_bool(options['quasi_steady']) self.only_nonlifting = ct.c_bool(options["only_nonlifting"]) - self.only_lifting = ct.c_bool(not options["nonlifting_body_interactions"]) + self.only_lifting = ct.c_bool(lifting_only) self.phantom_wing_test = ct.c_bool(options["phantom_wing_test"]) self.ignore_first_x_nodes_in_force_calculation = ct.c_uint(options["ignore_first_x_nodes_in_force_calculation"]) self.num_spanwise_panels_wo_induced_velocity = n_span_panels_wo_u_ind @@ -347,8 +348,8 @@ def uvlm_solver_lifting_and_nonlifting(i_iter, ts_info, ts_info_nonlifting, stru n_surfaces_nonlifting = ts_info_nonlifting.n_surf, dt = dt, convect_wake = convect_wake, - n_span_panels_wo_u_ind=4) - uvmopts.only_lifting = ct.c_bool(False) + n_span_panels_wo_u_ind=4, + only_lifting=False) run_UVLM = UvlmLib.run_UVLM_coupled_with_LSPM flightconditions = FlightConditions(options['rho'], ts_info.u_ext[0][:, 0, 0]) @@ -356,7 +357,6 @@ def uvlm_solver_lifting_and_nonlifting(i_iter, ts_info, ts_info_nonlifting, stru i = ct.c_uint(i_iter) ts_info.generate_ctypes_pointers() ts_info_nonlifting.generate_ctypes_pointers() - # previous_ts_info.generate_ctypes_pointers() run_UVLM(ct.byref(uvmopts), ct.byref(flightconditions), ts_info.ct_p_dimensions, From 836994470ff25d247673ced6058e4478629362fb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stefanie=20D=C3=BCssler?= Date: Wed, 20 Sep 2023 11:21:02 +0200 Subject: [PATCH 204/232] refactor [unittest] speed-up dynamic test - achieved by coarser discretisation, lower fsi tolerances and steps --- tests/coupled/dynamic/hale/generate_hale.py | 22 +++++++++------------ tests/coupled/dynamic/test_dynamic.py | 9 ++++----- 2 files changed, 13 insertions(+), 18 deletions(-) diff --git a/tests/coupled/dynamic/hale/generate_hale.py b/tests/coupled/dynamic/hale/generate_hale.py index b7399f273..2d68873bb 100644 --- a/tests/coupled/dynamic/hale/generate_hale.py +++ b/tests/coupled/dynamic/hale/generate_hale.py @@ -10,18 +10,13 @@ # EXECUTION flow = ['BeamLoader', 'AerogridLoader', - 'StaticTrim', + 'StaticCoupled', 'DynamicCoupled', 'BeamLoads' ] # if free_flight is False, the motion of the centre of the wing is prescribed. free_flight = True -if not free_flight: - case_name += '_prescribed' - amplitude = 0 * np.pi / 180 - period = 3 - case_name += '_amp_' + str(amplitude).replace('.', '') + '_period_' + str(period) # FLIGHT CONDITIONS # the simulation is set such that the aircraft flies at a u_inf velocity while @@ -44,7 +39,7 @@ # gust settings gust_intensity = 0.20 gust_length = 1 * u_inf -gust_offset = 0.2 * u_inf +gust_offset = 0.0 * u_inf # numerics n_step = 5 @@ -101,7 +96,7 @@ # chordiwse panels m = 4 # spanwise elements -n_elem_multiplier = 2 +n_elem_multiplier = 1 n_elem_main = int(4 * n_elem_multiplier) n_elem_tail = int(2 * n_elem_multiplier) n_elem_fin = int(2 * n_elem_multiplier) @@ -112,7 +107,7 @@ physical_time = 30 tstep_factor = 1. dt = 1.0 / m / u_inf * tstep_factor -n_tstep = 20 +n_tstep = 5 # END OF INPUT----------------------------------------------------------------- @@ -669,15 +664,16 @@ def generate_solver_file(): 'structural_solver_settings': settings[solver], 'aero_solver': 'StepUvlm', 'aero_solver_settings': settings['StepUvlm'], - 'fsi_substeps': 200, - 'fsi_tolerance': fsi_tolerance, - 'relaxation_factor': relaxation_factor, + 'fsi_substeps': 3, + 'fsi_tolerance': 1e-3, + 'relaxation_factor': 0, 'minimum_steps': 1, 'relaxation_steps': 150, 'final_relaxation_factor': 0.5, 'n_time_steps': n_tstep, 'dt': dt, - 'include_unsteady_force_contribution': 'on',} + 'include_unsteady_force_contribution': 'on', + } settings['BeamLoader'] = {'unsteady': 'on', 'orientation': algebra.euler2quat(np.array([roll, diff --git a/tests/coupled/dynamic/test_dynamic.py b/tests/coupled/dynamic/test_dynamic.py index d677f3740..2e8bf36e5 100644 --- a/tests/coupled/dynamic/test_dynamic.py +++ b/tests/coupled/dynamic/test_dynamic.py @@ -14,7 +14,7 @@ def test_hale_dynamic(self): """ Case and results from: tests/coupled/dynamic/hale - reference results produced with SHARPy version 1.3 + reference results produced with SHARPy version 2.0 :return: """ import sharpy.sharpy_main @@ -29,11 +29,10 @@ def test_hale_dynamic(self): output_folder = cases_folder + '/output/' sharpy.sharpy_main.main(['', cases_folder + '/hale.sharpy']) - n_tstep = 20 - + n_tstep = 5 # compare results with reference values - ref_Fz = 50.4986064826483 - ref_My = -1833.91402522644 + ref_Fz = -526.712530589119 + ref_My =-1513.64676181859 file = os.path.join(output_folder, case_name, 'beam/beam_loads_%i.csv' % (n_tstep)) beam_loads_ts = np.loadtxt(file, delimiter=',') np.testing.assert_almost_equal(float(beam_loads_ts[0, 6]), ref_Fz, From cd0a9e80575ec89b842df018ee954370e7041f67 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stefanie=20D=C3=BCssler?= Date: Wed, 20 Sep 2023 11:31:31 +0200 Subject: [PATCH 205/232] fix [uvlmlib] name of input parameter only_lifting instead of lifting_only --- sharpy/aero/utils/uvlmlib.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/sharpy/aero/utils/uvlmlib.py b/sharpy/aero/utils/uvlmlib.py index 5946e557f..10903b955 100644 --- a/sharpy/aero/utils/uvlmlib.py +++ b/sharpy/aero/utils/uvlmlib.py @@ -166,8 +166,8 @@ def set_options(self, n_surfaces_nonlifting = 0, dt = None, convect_wake = False, - n_span_panels_wo_u_ind = 0, - lifting_only=True): + n_span_panels_wo_u_ind = 0, + only_lifting=True): if dt is None: self.dt = ct.c_double(options["dt"]) else: @@ -191,7 +191,7 @@ def set_options(self, self.quasi_steady = ct.c_bool(options['quasi_steady']) self.only_nonlifting = ct.c_bool(options["only_nonlifting"]) - self.only_lifting = ct.c_bool(lifting_only) + self.only_lifting = ct.c_bool(only_lifting) self.phantom_wing_test = ct.c_bool(options["phantom_wing_test"]) self.ignore_first_x_nodes_in_force_calculation = ct.c_uint(options["ignore_first_x_nodes_in_force_calculation"]) self.num_spanwise_panels_wo_induced_velocity = n_span_panels_wo_u_ind From bb0bcc5cc5508adfd0eb835fd01a9fbaf5da8f06 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stefanie=20D=C3=BCssler?= Date: Wed, 20 Sep 2023 11:35:54 +0200 Subject: [PATCH 206/232] refactor [unittest] speed up dynamic phantom test --- .../fwc_get_settings.py | 12 +++++------ .../sourcepanelmethod/test_vlm_coupled_spm.py | 21 ++++++++++++++----- 2 files changed, 22 insertions(+), 11 deletions(-) diff --git a/sharpy/cases/templates/fuselage_wing_configuration/fwc_get_settings.py b/sharpy/cases/templates/fuselage_wing_configuration/fwc_get_settings.py index e17769291..311ff1ac6 100644 --- a/sharpy/cases/templates/fuselage_wing_configuration/fwc_get_settings.py +++ b/sharpy/cases/templates/fuselage_wing_configuration/fwc_get_settings.py @@ -112,7 +112,7 @@ def define_simulation_settings(flow, model, alpha_deg, u_inf, 'newmark_damp': 1e-4, 'gravity_on': gravity, 'gravity': 9.81, - 'num_steps': kwargs.get('n_tstep',10), + 'num_steps': kwargs.get('n_tsteps',10), 'dt': dt, } settings['StepUvlm'] = {'print_info': 'on', @@ -121,7 +121,7 @@ def define_simulation_settings(flow, model, alpha_deg, u_inf, 'velocity_field_input': {'u_inf': u_inf, 'u_inf_direction': [1., 0., 0.]}, 'rho': rho, - 'n_time_steps': kwargs.get('n_tstep',10), + 'n_time_steps': kwargs.get('n_tsteps',10), 'dt': dt, 'phantom_wing_test': phantom_test, 'nonlifting_body_interactions': not lifting_only, @@ -133,13 +133,13 @@ def define_simulation_settings(flow, model, alpha_deg, u_inf, 'structural_solver_settings': settings[dynamic_structural_solver], 'aero_solver': 'StepUvlm', 'aero_solver_settings': settings['StepUvlm'], - 'fsi_substeps': 200, + 'fsi_substeps': kwargs.get('fsi_substeps', 200), 'fsi_tolerance': fsi_tolerance, - 'relaxation_factor': 0.1, + 'relaxation_factor': kwargs.get('relaxation_factor',0.1), 'minimum_steps': 1, 'relaxation_steps': 150, - 'final_relaxation_factor': 0.05, - 'n_time_steps': kwargs.get('n_tstep',10), + 'final_relaxation_factor': kwargs.get('final_relaxation_factor', 0.05), + 'n_time_steps': kwargs.get('n_tsteps',10), 'dt': dt, 'nonlifting_body_interactions': not lifting_only, 'include_unsteady_force_contribution': kwargs.get('unsteady_force_distribution', True), diff --git a/tests/sourcepanelmethod/test_vlm_coupled_spm.py b/tests/sourcepanelmethod/test_vlm_coupled_spm.py index 0befb9a68..b5b651799 100644 --- a/tests/sourcepanelmethod/test_vlm_coupled_spm.py +++ b/tests/sourcepanelmethod/test_vlm_coupled_spm.py @@ -34,7 +34,7 @@ def test_phantom_panels(self): dict_discretization = { 'n_elem_per_wing': 20, 'n_elem_fuselage': 10, - 'num_chordwise_panels': 8 + 'num_chordwise_panels': 4 } # Simulation settings @@ -42,6 +42,11 @@ def test_phantom_panels(self): list_phantom_test = [False, True] list_dynamic_test = [False, True] dynamic_structural_solver = 'NonLinearDynamicPrescribedStep' + + # Numerical Settings for DynamicCoupled (very small for faster test runs) + fsi_substeps = 2 + fsi_tolerance = 1e-2 + # Simlation Solver Flow flow = ['BeamLoader', 'AerogridLoader', @@ -71,7 +76,7 @@ def test_phantom_panels(self): flow_case = flow.copy() if lifting_only: - n_tsteps = 10 + n_tsteps = 5 flow_case.remove('NonliftingbodygridLoader') if not dynamic: n_tsteps = 0 @@ -85,7 +90,9 @@ def test_phantom_panels(self): n_tsteps = n_tsteps, horseshoe=horseshoe, phantom_test=phantom_test, - dynamic_structural_solver=dynamic_structural_solver) + dynamic_structural_solver=dynamic_structural_solver, + fsi_substeps=fsi_substeps, + fsi_tolerance=fsi_tolerance) # # run simulation phantom_wing.run() @@ -230,7 +237,9 @@ def generate_simulation_settings(self, horseshoe=True, nonlifting_only=False, phantom_test=False, - dynamic_structural_solver='NonLinearDynamicPrescribedStep' + dynamic_structural_solver='NonLinearDynamicPrescribedStep', + fsi_substeps=200, + fsi_tolerance=1e-6 ): """ Simulation settings are defined and written to the ".sharpy" input file. @@ -245,7 +254,9 @@ def generate_simulation_settings(self, phantom_test=phantom_test, nonlifting_only=nonlifting_only, horseshoe=horseshoe, - dynamic_structural_solver=dynamic_structural_solver) + dynamic_structural_solver=dynamic_structural_solver, + fsi_substeps=fsi_substeps, + fsi_tolerance=fsi_tolerance) aircraft_model.create_settings(settings) def get_timestep(self, model, u_inf): From 8f4bb761e263cc9a6c72e50395e3c6058960a4e0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stefanie=20D=C3=BCssler?= Date: Wed, 20 Sep 2023 12:37:01 +0200 Subject: [PATCH 207/232] update UVLM after merge with master branch --- lib/UVLM | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/UVLM b/lib/UVLM index 91ec36ae5..d8af34a22 160000 --- a/lib/UVLM +++ b/lib/UVLM @@ -1 +1 @@ -Subproject commit 91ec36ae5401caa79733f97545b14ca1f4ef4515 +Subproject commit d8af34a22baf1cddd38f1e362274c407637aab1c From a0f43bfc779708e138da553820f6c282df94f3ac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stefanie=20D=C3=BCssler?= Date: Wed, 20 Sep 2023 12:38:29 +0200 Subject: [PATCH 208/232] fix [unittest] tear down function for dynamic test --- tests/coupled/dynamic/test_dynamic.py | 34 +++++++++++++-------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/tests/coupled/dynamic/test_dynamic.py b/tests/coupled/dynamic/test_dynamic.py index 2e8bf36e5..1ca8de52e 100644 --- a/tests/coupled/dynamic/test_dynamic.py +++ b/tests/coupled/dynamic/test_dynamic.py @@ -24,8 +24,8 @@ def test_hale_dynamic(self): import tests.coupled.dynamic.hale.generate_hale case_name = 'hale' - route_test_dir = os.path.abspath(os.path.dirname(os.path.realpath(__file__))) - cases_folder = os.path.join(route_test_dir, case_name) + self.route_file_dir = os.path.abspath(os.path.dirname(os.path.realpath(__file__))) + cases_folder = os.path.join(self.route_file_dir, case_name) output_folder = cases_folder + '/output/' sharpy.sharpy_main.main(['', cases_folder + '/hale.sharpy']) @@ -33,6 +33,7 @@ def test_hale_dynamic(self): # compare results with reference values ref_Fz = -526.712530589119 ref_My =-1513.64676181859 + file = os.path.join(output_folder, case_name, 'beam/beam_loads_%i.csv' % (n_tstep)) beam_loads_ts = np.loadtxt(file, delimiter=',') np.testing.assert_almost_equal(float(beam_loads_ts[0, 6]), ref_Fz, @@ -45,22 +46,21 @@ def test_hale_dynamic(self): verbose=True) @classmethod - def tearDownClass(cls): - + def tearDown(self): + """ + Removes all created files within this test. + """ import shutil - list_cases = ['hale'] - list_file_extensions = ['.fem.h5', '.aero.h5', '.sharpy'] - list_folders = ['output', '__pycache__'] - for case in list_cases: - file_path = os.path.join(os.path.abspath(os.path.dirname(os.path.realpath(__file__))), - case) - for folder in list_folders: - if os.path.isdir(folder): - shutil.rmtree(folder) - for extension in list_file_extensions: - os.remove(os.path.join(file_path, case + extension)) - pass - + folders = ['hale/output'] + for folder in folders: + shutil.rmtree(self.route_file_dir + '/' + folder) + files = ['hale/hale.aero.h5', 'hale/hale.fem.h5', 'hale.hale.sharpy'] + for file in files: + file_dir = self.route_file_dir + '/' + file + if os.path.isfile(file_dir): + os.remove(file_dir) + + if __name__ == '__main__': unittest.main() \ No newline at end of file From a1de78e8b19a7aae9cb8d9f81256f4f81aeba523 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stefanie=20D=C3=BCssler?= Date: Wed, 20 Sep 2023 12:45:49 +0200 Subject: [PATCH 209/232] fix [unittest] earlier definition of file directory for tear down function --- tests/coupled/dynamic/test_dynamic.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/coupled/dynamic/test_dynamic.py b/tests/coupled/dynamic/test_dynamic.py index 1ca8de52e..aa67f3642 100644 --- a/tests/coupled/dynamic/test_dynamic.py +++ b/tests/coupled/dynamic/test_dynamic.py @@ -10,6 +10,8 @@ class TestCoupledDynamic(unittest.TestCase): - Gust response of the hale aircraft """ + route_file_dir = os.path.abspath(os.path.dirname(os.path.realpath(__file__))) + def test_hale_dynamic(self): """ Case and results from: @@ -24,7 +26,6 @@ def test_hale_dynamic(self): import tests.coupled.dynamic.hale.generate_hale case_name = 'hale' - self.route_file_dir = os.path.abspath(os.path.dirname(os.path.realpath(__file__))) cases_folder = os.path.join(self.route_file_dir, case_name) output_folder = cases_folder + '/output/' From 1e9356a8fc6a646b00ae31a7a8874c7af6e60c5b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stefanie=20D=C3=BCssler?= Date: Wed, 20 Sep 2023 13:43:04 +0200 Subject: [PATCH 210/232] fix [unittest] tear down function specified file --- tests/coupled/dynamic/test_dynamic.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/coupled/dynamic/test_dynamic.py b/tests/coupled/dynamic/test_dynamic.py index aa67f3642..85dc61732 100644 --- a/tests/coupled/dynamic/test_dynamic.py +++ b/tests/coupled/dynamic/test_dynamic.py @@ -55,7 +55,7 @@ def tearDown(self): folders = ['hale/output'] for folder in folders: shutil.rmtree(self.route_file_dir + '/' + folder) - files = ['hale/hale.aero.h5', 'hale/hale.fem.h5', 'hale.hale.sharpy'] + files = ['hale/hale.aero.h5', 'hale/hale.fem.h5', 'hale/hale.sharpy'] for file in files: file_dir = self.route_file_dir + '/' + file if os.path.isfile(file_dir): From c3f2d9ea2854c9ae35df8716cf71ed7cce84e840 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stefanie=20D=C3=BCssler?= Date: Wed, 20 Sep 2023 13:44:31 +0200 Subject: [PATCH 211/232] fix [unittest] dynamic estt reference value --- tests/coupled/dynamic/test_dynamic.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/coupled/dynamic/test_dynamic.py b/tests/coupled/dynamic/test_dynamic.py index 85dc61732..635832c6f 100644 --- a/tests/coupled/dynamic/test_dynamic.py +++ b/tests/coupled/dynamic/test_dynamic.py @@ -32,8 +32,8 @@ def test_hale_dynamic(self): sharpy.sharpy_main.main(['', cases_folder + '/hale.sharpy']) n_tstep = 5 # compare results with reference values - ref_Fz = -526.712530589119 - ref_My =-1513.64676181859 + ref_Fz = -531.023900359779 + ref_My = -1530.0477841197576 file = os.path.join(output_folder, case_name, 'beam/beam_loads_%i.csv' % (n_tstep)) beam_loads_ts = np.loadtxt(file, delimiter=',') From a0a222dd2e222daa5a0f6d44fcc8f7f238d3c6f0 Mon Sep 17 00:00:00 2001 From: Ben Preston <144227999+ben-l-p@users.noreply.github.com> Date: Wed, 4 Oct 2023 16:06:54 +0100 Subject: [PATCH 212/232] Add files via upload --- utils/environment_macos_arm64.yml | 96 +++++++++++++++++++++++++++++++ 1 file changed, 96 insertions(+) create mode 100644 utils/environment_macos_arm64.yml diff --git a/utils/environment_macos_arm64.yml b/utils/environment_macos_arm64.yml new file mode 100644 index 000000000..ad9f12981 --- /dev/null +++ b/utils/environment_macos_arm64.yml @@ -0,0 +1,96 @@ +name: sharpy_env +channels: + - conda-forge + - defaults +dependencies: + - alabaster + - apptools + - blas + - bzip2 + - ca-certificates + - certifi + - cmake + - colorama + - configobj + - control + - coverage + - curl + - cycler + - dbus + - dill + - eigen + - envisage + - expat + - freetype + - future + - gettext + - glib + - h5py + - hdf4 + - hdf5 + - icu + - jpeg + - jsoncpp + - kiwisolver + - krb5 + - libcurl + - libcxx + - libedit + - libffi + - libgfortran + - libiconv + - libnetcdf + - libogg + - libopenblas + - libpng + - libssh2 + - libtheora + - libtiff + - libvorbis + - libxml2 + - libxslt + - lxml + - lz4-c + - matplotlib + - matplotlib-base + - mayavi + - ncurses + - numpy + - numpy-base + - openblas + - openssl + - pandas + - pcre + - pip + - pyface + - pygments + - pyparsing + - pyqt + - python + - python-dateutil + - python_abi + - pytz + - pyyaml + - qt + - readline + - rhash + - scipy + - setuptools + - sip + - six + - slycot + - sqlite + - tbb + - tk + - tornado + - traits + - traitsui + - vtk + - wheel + - xlrd + - xz + - yaml + - zlib + - zstd + - openpyxl + From a73686112b58b6351ba84abdd7584ff7ad36983f Mon Sep 17 00:00:00 2001 From: Ben Preston <144227999+ben-l-p@users.noreply.github.com> Date: Mon, 9 Oct 2023 10:08:20 +0100 Subject: [PATCH 213/232] Update environment_macos_arm64.yml Replaced environment file to use the new environment. Only change is the less strict make version requirement. --- utils/environment_macos_arm64.yml | 114 ++++++------------------------ 1 file changed, 22 insertions(+), 92 deletions(-) diff --git a/utils/environment_macos_arm64.yml b/utils/environment_macos_arm64.yml index ad9f12981..f2642e411 100644 --- a/utils/environment_macos_arm64.yml +++ b/utils/environment_macos_arm64.yml @@ -1,96 +1,26 @@ -name: sharpy_env +name: sharpy channels: - conda-forge - defaults dependencies: - - alabaster - - apptools - - blas - - bzip2 - - ca-certificates - - certifi - - cmake - - colorama - - configobj - - control - - coverage - - curl - - cycler - - dbus - - dill - - eigen - - envisage - - expat - - freetype - - future - - gettext - - glib - - h5py - - hdf4 - - hdf5 - - icu - - jpeg - - jsoncpp - - kiwisolver - - krb5 - - libcurl - - libcxx - - libedit - - libffi - - libgfortran - - libiconv - - libnetcdf - - libogg - - libopenblas - - libpng - - libssh2 - - libtheora - - libtiff - - libvorbis - - libxml2 - - libxslt - - lxml - - lz4-c - - matplotlib - - matplotlib-base - - mayavi - - ncurses - - numpy - - numpy-base - - openblas - - openssl - - pandas - - pcre - - pip - - pyface - - pygments - - pyparsing - - pyqt - - python - - python-dateutil - - python_abi - - pytz - - pyyaml - - qt - - readline - - rhash - - scipy - - setuptools - - sip - - six - - slycot - - sqlite - - tbb - - tk - - tornado - - traits - - traitsui - - vtk - - wheel - - xlrd - - xz - - yaml - - zlib - - zstd - - openpyxl - + - asn1crypto>=1.2.0 + - colorama>=0.4.1 + - control>=0.8.4 + - dill>=0.3.1.1 + - h5py>=2.9.0 + - jupyterlab>=1.2.3 + - lxml>=4.4.1 + - mayavi>=4.7.1 + - nbsphinx>=0.4.3 + - pandas>=0.25.3 + - pyOpenSSL>=19.0.0 + - PySocks>=1.7.1 + - PyYAML>=5.1.2 + - recommonmark>=0.6.0 + - slycot>=0.4.0 + - sphinx_rtd_theme>=0.4.3 + - wheel>=0.33.6 + - xlrd>=1.2.0 + - python=3.10 + - openpyxl>=3.0.10 + - cmake>=3.14.0 From aa871954b93d4dd67a056bd4f574850fc1b6986c Mon Sep 17 00:00:00 2001 From: Ben Preston <144227999+ben-l-p@users.noreply.github.com> Date: Mon, 9 Oct 2023 10:39:31 +0100 Subject: [PATCH 214/232] Update installation instructions --- docs/source/content/installation.md | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/docs/source/content/installation.md b/docs/source/content/installation.md index e9cb91a2a..dbd104c71 100644 --- a/docs/source/content/installation.md +++ b/docs/source/content/installation.md @@ -1,5 +1,5 @@ # SHARPy v2.0 Installation Guide -__Last revision 27 June 2023__ +__Last revision 9 October 2023__ The following step by step tutorial will guide you through the installation process of SHARPy. This is the updated process valid from v2.0. @@ -11,7 +11,8 @@ SHARPy is being developed and tested on the following operating systems: * CentOS 7 and CentOS 8 * Ubuntu 18.04 LTS * Debian 10 -* MacOS Mojave and Catalina +* MacOS Mojave and Catalina (Intel) +* MacOS Sonoma (Apple Silicon M2) Windows users can also run it by first installing the Windows Subsystem for Linux (https://learn.microsoft.com/en-us/windows/wsl/install) and a XServer such as GWSL, which can be installed through the Microsoft Store. SHARPy is also available to the vast majority of operating systems that are supported by Docker @@ -72,7 +73,7 @@ or running any SHARPy cases. ``` 4. Create the conda environment that SHARPy will use. Change `environment_new.yml` to read `environment_macos.yml` - if you are installing SHARPy on Mac OS X. + if you are installing SHARPy on MacOS (Intel), or `environment_macos_arm64.yml` if installing on macOS (Apple Silicon). ```bash cd sharpy/utils conda env create -f environment_new.yml @@ -156,7 +157,7 @@ to your taste. This command will check out the `develop` branch and set it to track the remote origin. It will also set the submodules (xbeam and UVLM) to the right commit. 2. Create the conda environment that SHARPy will use. Change `environment_new.yml` to read `environment_macos.yml` - if you are installing SHARPy on Mac OS X. + if you are installing SHARPy on MacOS (Intel), or `environment_macos_arm64.yml` if installing on macOS (Apple Silicon). ```bash cd sharpy/utils conda env create -f environment_new.yml From 62b78136b1be31fdd97532618fba3e8a7ee26984 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stefanie=20D=C3=BCssler?= Date: Wed, 11 Oct 2023 11:59:33 +0100 Subject: [PATCH 215/232] fix [docker] conda environment for docker --- Dockerfile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Dockerfile b/Dockerfile index 9f49d0448..d96957346 100644 --- a/Dockerfile +++ b/Dockerfile @@ -31,7 +31,7 @@ RUN conda init bash && \ conda config --set always_yes yes --set changeps1 no && \ conda update -q conda && \ conda config --set auto_activate_base false && \ - conda env create -f /sharpy_dir/utils/environment_minimal.yml && conda clean -afy && \ + conda env create -f /sharpy_dir/utils/environment_new.yml && conda clean -afy && \ find /miniconda3/ -follow -type f -name '*.a' -delete && \ find /miniconda3/ -follow -type f -name '*.pyc' -delete && \ find /miniconda3/ -follow -type f -name '*.js.map' -delete @@ -40,7 +40,7 @@ RUN conda init bash && \ RUN ln -s /sharpy_dir/utils/docker/* /root/ RUN cd sharpy_dir && \ - conda activate sharpy_minimal && \ + conda activate sharpy && \ git submodule update --init --recursive && \ mkdir build && \ cd build && \ From 1c3854f8c75716aa8f43f6d933e07b570095e181 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stefanie=20D=C3=BCssler?= Date: Wed, 11 Oct 2023 17:37:17 +0100 Subject: [PATCH 216/232] fix [docker] use correct environment name in docker bashrc --- utils/docker/bashrc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/utils/docker/bashrc b/utils/docker/bashrc index aaf5aee1c..dbe250efe 100644 --- a/utils/docker/bashrc +++ b/utils/docker/bashrc @@ -26,7 +26,7 @@ fi unset __conda_setup # <<< conda initialize <<< -conda activate sharpy_minimal +conda activate sharpy # custom prompt PS1="SHARPy> " From 1211514bd2393d3264cbd2b01c82e3cb108c74bf Mon Sep 17 00:00:00 2001 From: Ben Preston <144227999+ben-l-p@users.noreply.github.com> Date: Wed, 18 Oct 2023 09:13:10 +0100 Subject: [PATCH 217/232] Delete utils/environment_macos.yml Old MacOS environment is to be replaced with the new environment --- utils/environment_macos.yml | 103 ------------------------------------ 1 file changed, 103 deletions(-) delete mode 100644 utils/environment_macos.yml diff --git a/utils/environment_macos.yml b/utils/environment_macos.yml deleted file mode 100644 index 740bc301b..000000000 --- a/utils/environment_macos.yml +++ /dev/null @@ -1,103 +0,0 @@ -name: sharpy_env -channels: - - conda-forge - - defaults -dependencies: - - _libgcc_mutex=0.1=main - - alabaster=0.7.12=py37_0 - - apptools=4.4.0=py37_1 - - blas=1.0=mkl - - bzip2=1.0.8=h01d97ff_1 - - ca-certificates=2020.10.14=0 - - certifi=2020.6.20=py37_0 - - cmake=3.14.0=haff7e42_0 - - colorama=0.4.1=py37_0 - - configobj=5.0.6=py37_1 - - control=0.8.4=py37hf985489_0 - - coverage=4.5.4=py37h1de35cc_0 - - curl=7.67.0=ha441bb4_0 - - cycler=0.10.0=py37_0 - - dbus=1.13.12=h90a0687_0 - - dill=0.3.1.1=py37_0 - - eigen=3.3.7=h04f5b5a_1000 - - envisage=4.8.0=py_0 - - expat=2.2.6=h0a44026_0 - - freetype=2.9.1=hb4e5f40_0 - - future=0.18.2=py37_0 - - gettext=0.19.8.1=h15daf44_3 - - glib=2.63.1=hd977a24_0 - - h5py=2.9.0=py37h3134771_0 - - hdf4=4.2.13=h39711bb_2 - - hdf5=1.10.4=hfa1e0ec_0 - - icu=58.2=h4b95b61_1 - - intel-openmp=2019.4=233 - - jpeg=9b=he5867d9_2 - - jsoncpp=1.8.4=h04f5b5a_0 - - kiwisolver=1.1.0=py37h0a44026_0 - - krb5=1.16.1=hddcf347_7 - - libcurl=7.67.0=h051b688_0 - - libcxx=4.0.1=hcfea43d_1 - - libcxxabi=4.0.1=hcfea43d_1 - - libedit=3.1.20181209=hb402a30_0 - - libffi=3.2.1=h475c297_4 - - libgfortran=3.0.1=h93005f0_2 - - libiconv=1.15=hdd342a3_7 - - libnetcdf=4.6.1=hd5207e6_2 - - libogg=1.3.2=h1de35cc_0 - - libopenblas=0.3.7=hd44dcd8_1 - - libpng=1.6.37=ha441bb4_0 - - libssh2=1.8.2=hcdc9a53_2 - - libtheora=1.1.1=hb4e5f40_1 - - libtiff=4.1.0=hcb84e12_0 - - libvorbis=1.3.6=h1de35cc_0 - - libxml2=2.9.9=hf6e021a_1 - - libxslt=1.1.33=h33a18ac_0 - - lxml=4.4.2=py37hef8c89e_0 - - lz4-c=1.8.1.2=h1de35cc_0 - - matplotlib=3.1.1=py37h54f8f79_0 - - matplotlib-base=3.1.1=py37h3a684a6_1 - - mayavi=4.6.2=py37hdde6e19_4 - - mkl=2019.4=233 - - mkl-include=2019.4=233 - - mkl-service=2.3.0=py37hfbe908c_0 - - mkl_fft=1.0.15=py37h5e564d8_0 - - mkl_random=1.1.0=py37ha771720_0 - - ncurses=6.1=h0a44026_1 - - numpy=1.17.4=py37h890c691_0 - - numpy-base=1.17.4=py37h6575580_0 - - openblas=0.3.7=hd44dcd8_1 - - openssl=1.1.1h=haf1e3a3_0 - - pandas=0.25.3=py37h0a44026_0 - - pcre=8.43=h0a44026_0 - - pip=19.3.1=py37_0 - - pyface=6.1.2=py37_0 - - pygments=2.5.2=py_0 - - pyparsing=2.4.5=py_0 - - pyqt=5.9.2=py37h655552a_2 - - python=3.7.5=h359304d_0 - - python-dateutil=2.8.1=py_0 - - python_abi=3.7=1_cp37m - - pytz=2019.3=py_0 - - pyyaml=5.3.1=py37haf1e3a3_1 - - qt=5.9.7=h468cd18_1 - - readline=7.0=h1de35cc_5 - - rhash=1.3.8=ha12b0ac_0 - - scipy=1.3.1=py37h1410ff5_0 - - setuptools=42.0.2=py37_0 - - sip=4.19.8=py37h0a44026_0 - - six=1.13.0=py37_0 - - slycot=0.3.5.0=py37h82d0005_0 - - sqlite=3.30.1=ha441bb4_0 - - tbb=2019.8=h04f5b5a_0 - - tk=8.6.8=ha441bb4_0 - - tornado=6.0.3=py37h1de35cc_0 - - traits=5.2.0=py37h1de35cc_0 - - traitsui=6.1.3=py_0 - - vtk=8.2.0=py37h9bafd54_200 - - wheel=0.33.6=py37_0 - - xlrd=1.2.0=py_0 - - xz=5.2.4=h1de35cc_4 - - yaml=0.2.5=haf1e3a3_0 - - zlib=1.2.11=h1de35cc_3 - - zstd=1.3.7=h5bba6e5_0 - From 9441349626d03662fde4ee4b56ee1214ced431f4 Mon Sep 17 00:00:00 2001 From: Ben Preston <144227999+ben-l-p@users.noreply.github.com> Date: Wed, 18 Oct 2023 09:14:19 +0100 Subject: [PATCH 218/232] Changed make version from 3.14 to 3.19 (Apple Silicon minimum) --- utils/environment_new.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/utils/environment_new.yml b/utils/environment_new.yml index e16da0afd..f6c7afba7 100644 --- a/utils/environment_new.yml +++ b/utils/environment_new.yml @@ -23,4 +23,4 @@ dependencies: - xlrd>=1.2.0 - python=3.10 - openpyxl>=3.0.10 - - cmake=3.14.0 \ No newline at end of file + - cmake>=3.19.0 From 6bf9a58569c3217635f1c845622e84d9dd6c1a76 Mon Sep 17 00:00:00 2001 From: Ben Preston <144227999+ben-l-p@users.noreply.github.com> Date: Wed, 18 Oct 2023 09:19:13 +0100 Subject: [PATCH 219/232] Finalised install guide changes for new environment --- docs/source/content/installation.md | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/docs/source/content/installation.md b/docs/source/content/installation.md index dbd104c71..96caad405 100644 --- a/docs/source/content/installation.md +++ b/docs/source/content/installation.md @@ -72,8 +72,7 @@ or running any SHARPy cases. conda install python=3.10 ``` -4. Create the conda environment that SHARPy will use. Change `environment_new.yml` to read `environment_macos.yml` - if you are installing SHARPy on MacOS (Intel), or `environment_macos_arm64.yml` if installing on macOS (Apple Silicon). +4. Create the conda environment that SHARPy will use: ```bash cd sharpy/utils conda env create -f environment_new.yml @@ -156,8 +155,7 @@ to your taste. ``` This command will check out the `develop` branch and set it to track the remote origin. It will also set the submodules (xbeam and UVLM) to the right commit. -2. Create the conda environment that SHARPy will use. Change `environment_new.yml` to read `environment_macos.yml` - if you are installing SHARPy on MacOS (Intel), or `environment_macos_arm64.yml` if installing on macOS (Apple Silicon). +2. Create the conda environment that SHARPy will use: ```bash cd sharpy/utils conda env create -f environment_new.yml From f85e81bf555ffaa50bca02ed308b29cc62292a86 Mon Sep 17 00:00:00 2001 From: Ben Preston <144227999+ben-l-p@users.noreply.github.com> Date: Wed, 18 Oct 2023 12:06:28 +0100 Subject: [PATCH 220/232] Delete utils/environment_macos_arm64.yml Environment not needed - replaced with environment_new.yml --- utils/environment_macos_arm64.yml | 26 -------------------------- 1 file changed, 26 deletions(-) delete mode 100644 utils/environment_macos_arm64.yml diff --git a/utils/environment_macos_arm64.yml b/utils/environment_macos_arm64.yml deleted file mode 100644 index f2642e411..000000000 --- a/utils/environment_macos_arm64.yml +++ /dev/null @@ -1,26 +0,0 @@ -name: sharpy -channels: - - conda-forge - - defaults -dependencies: - - asn1crypto>=1.2.0 - - colorama>=0.4.1 - - control>=0.8.4 - - dill>=0.3.1.1 - - h5py>=2.9.0 - - jupyterlab>=1.2.3 - - lxml>=4.4.1 - - mayavi>=4.7.1 - - nbsphinx>=0.4.3 - - pandas>=0.25.3 - - pyOpenSSL>=19.0.0 - - PySocks>=1.7.1 - - PyYAML>=5.1.2 - - recommonmark>=0.6.0 - - slycot>=0.4.0 - - sphinx_rtd_theme>=0.4.3 - - wheel>=0.33.6 - - xlrd>=1.2.0 - - python=3.10 - - openpyxl>=3.0.10 - - cmake>=3.14.0 From bb60716fc4f12707e2f822ef2adb4b0683fef5b4 Mon Sep 17 00:00:00 2001 From: Ben Preston <144227999+ben-l-p@users.noreply.github.com> Date: Wed, 18 Oct 2023 13:52:43 +0100 Subject: [PATCH 221/232] Delete utils/environment_linux.yml --- utils/environment_linux.yml | 170 ------------------------------------ 1 file changed, 170 deletions(-) delete mode 100644 utils/environment_linux.yml diff --git a/utils/environment_linux.yml b/utils/environment_linux.yml deleted file mode 100644 index 0834869ce..000000000 --- a/utils/environment_linux.yml +++ /dev/null @@ -1,170 +0,0 @@ -name: sharpy_env -channels: - - conda-forge - - defaults -dependencies: - - _libgcc_mutex=0.1=main - - alabaster=0.7.12=py37_0 - - apptools=4.4.0=py37_1 - - asn1crypto=1.2.0=py37_0 - - attrs=19.3.0=py_0 - - babel=2.7.0=py_0 - - backcall=0.1.0=py37_0 - - bleach=3.1.0=py_0 - - bzip2=1.0.8=h7b6447c_0 - - ca-certificates=2020.12.5=ha878542_0 - - certifi=2020.12.5=py37h89c1867_1 - - cffi=1.13.1=py37h2e261b9_0 - - chardet=3.0.4=py37_1003 - - cmake=3.14.0=h52cb24c_0 - - colorama=0.4.1=py37_0 - - commonmark=0.9.0=py_0 - - configobj=5.0.6=py37_1 - - control=0.8.4=py37h89c1867_0 - - cryptography=2.8=py37h1ba5d50_0 - - curl=7.67.0=hbc83047_0 - - cycler=0.10.0=py37_0 - - dbus=1.13.12=h746ee38_0 - - decorator=4.4.1=py_0 - - defusedxml=0.6.0=py_0 - - dill=0.3.1.1=py37_0 - - docutils=0.15.2=py37_0 - - eigen=3.3.7=hfd86e86_0 - - entrypoints=0.3=py37_1000 - - envisage=4.8.0=py_0 - - expat=2.2.6=he6710b0_0 - - fontconfig=2.13.0=h9420a91_0 - - freetype=2.9.1=h8a8886c_1 - - future=0.18.2=py37_0 - - glib=2.63.1=h5a9c865_0 - - gst-plugins-base=1.14.0=hbbd80ab_1 - - gstreamer=1.14.0=hb453b48_1 - - h5py=2.9.0=nompi_py37hcafd542_1103 - - hdf4=4.2.13=h3ca952b_2 - - hdf5=1.10.4=hb1b8bf9_0 - - icu=58.2=h9c2bf20_1 - - idna=2.8=py37_0 - - imagesize=1.1.0=py37_0 - - importlib_metadata=0.23=py37_0 - - intel-openmp=2019.4=243 - - ipykernel=5.1.3=py37h39e3cac_0 - - ipython=7.10.1=py37h39e3cac_0 - - ipython_genutils=0.2.0=py_1 - - jedi=0.15.1=py37_0 - - jinja2=2.10.3=py_0 - - jpeg=9b=h024ee3a_2 - - json5=0.8.5=py_0 - - jsoncpp=1.8.4=hfd86e86_0 - - jsonschema=3.1.1=py37_0 - - jupyter_client=5.3.3=py37_1 - - jupyter_core=4.5.0=py_0 - - jupyterlab=1.2.3=py_0 - - jupyterlab_server=1.0.6=py_0 - - kiwisolver=1.1.0=py37he6710b0_0 - - krb5=1.16.1=h173b8e3_7 - - lapack=3.6.1=ha44fe06_2 - - libblas=3.8.0=14_openblas - - libcblas=3.8.0=14_openblas - - libcurl=7.67.0=h20c2e04_0 - - libedit=3.1.20181209=hc058e9b_0 - - libffi=3.2.1=hd88cf55_4 - - libgcc-ng=9.1.0=hdf63c60_0 - - libgfortran=3.0.0=1 - - libgfortran-ng=7.3.0=hdf63c60_0 - - liblapack=3.8.0=14_openblas - - libnetcdf=4.6.1=h11d0813_2 - - libogg=1.3.2=h7b6447c_0 - - libopenblas=0.3.7=h5ec1e0e_4 - - libpng=1.6.37=hbc83047_0 - - libsodium=1.0.16=h1bed415_0 - - libssh2=1.8.2=h1ba5d50_0 - - libstdcxx-ng=9.1.0=hdf63c60_0 - - libtheora=1.1.1=h5ab3b9f_1 - - libtiff=4.1.0=h2733197_0 - - libuuid=1.0.3=h1bed415_2 - - libvorbis=1.3.6=h7b6447c_0 - - libxcb=1.13=h1bed415_1 - - libxml2=2.9.9=hea5a465_1 - - libxslt=1.1.33=h7d1a2b0_0 - - lxml=4.4.1=py37hefd8a0e_0 - - lz4-c=1.8.1.2=h14c3975_0 - - markupsafe=1.1.1=py37h7b6447c_0 - - matplotlib=3.1.1=py37h5429711_0 - - matplotlib-base=3.1.3=py37hef1b27d_0 - - mayavi=4.7.1=py37h94891b3_2 - - mistune=0.8.4=py37h516909a_1000 - - more-itertools=7.2.0=py_0 - - nbconvert=5.6.1=py37_0 - - nbformat=4.4.0=py_1 - - nbsphinx=0.4.3=py_0 - - ncurses=6.1=he6710b0_1 - - notebook=6.0.1=py37_0 - - numpy=1.17.3=py37h95a1406_0 - - openssl=1.1.1i=h27cfd23_0 - - packaging=19.2=py_0 - - pandas=0.25.3=py37hb3f55d8_0 - - pandoc=2.7.3=0 - - pandocfilters=1.4.2=py_1 - - parso=0.5.1=py_0 - - pcre=8.43=he6710b0_0 - - pexpect=4.7.0=py37_0 - - pickleshare=0.7.5=py37_0 - - pip=19.3.1=py37_0 - - prometheus_client=0.7.1=py_0 - - prompt_toolkit=3.0.2=py_0 - - ptyprocess=0.6.0=py37_0 - - pycparser=2.19=py37_0 - - pyface=6.1.2=py37_0 - - pygments=2.4.2=py_0 - - pyopenssl=19.0.0=py37_0 - - pyparsing=2.4.4=py_0 - - pyqt=5.9.2=py37h05f1152_2 - - pyrsistent=0.15.5=py37h516909a_0 - - pysocks=1.7.1=py37_0 - - python=3.7.5=h0371630_0 - - python-dateutil=2.8.1=py_0 - - python_abi=3.7=1_cp37m - - pytz=2019.3=py_0 - - pyyaml=5.1.2=py37h7b6447c_0 - - pyzmq=18.1.0=py37he6710b0_0 - - qt=5.9.7=h5867ecd_1 - - readline=7.0=h7b6447c_5 - - recommonmark=0.6.0=py_0 - - requests=2.22.0=py37_0 - - rhash=1.3.8=h1ba5d50_0 - - scipy=1.3.2=py37h921218d_0 - - send2trash=1.5.0=py37_0 - - setuptools=41.6.0=py37_0 - - sip=4.19.8=py37hf484d3e_0 - - six=1.13.0=py37_0 - - snowballstemmer=2.0.0=py_0 - - sphinx=3.0.3=py_0 - - sphinx_rtd_theme=0.5.0=pyh9f0ad1d_0 - - sphinxcontrib-applehelp=1.0.1=py_0 - - sphinxcontrib-devhelp=1.0.1=py_0 - - sphinxcontrib-htmlhelp=1.0.2=py_0 - - sphinxcontrib-jsmath=1.0.1=py_0 - - sphinxcontrib-qthelp=1.0.2=py_0 - - sphinxcontrib-serializinghtml=1.1.3=py_0 - - sqlite=3.30.1=h7b6447c_0 - - tbb=2019.8=hfd86e86_0 - - terminado=0.8.3=py37_0 - - testpath=0.4.4=py_0 - - tk=8.6.8=hbc83047_0 - - tornado=6.0.3=py37h7b6447c_0 - - traitlets=4.3.3=py37_0 - - traits=5.2.0=py37h7b6447c_0 - - traitsui=6.1.3=py_0 - - urllib3=1.24.2=py37_0 - - vtk=8.2.0=py37haa4764d_200 - - wcwidth=0.1.7=py37_0 - - webencodings=0.5.1=py_1 - - wheel=0.33.6=py37_0 - - xlrd=1.2.0=py37_0 - - xz=5.2.4=h14c3975_4 - - yaml=0.1.7=had09818_2 - - zeromq=4.3.1=he6710b0_3 - - zipp=0.6.0=py_0 - - zlib=1.2.11=h7b6447c_3 - - zstd=1.3.7=h0b5b093_0 - - slycot=0.4.0.0=py37h27181d0_1 From 044064607e966c1bda17d8de883ea48373e8c662 Mon Sep 17 00:00:00 2001 From: Ben Preston <144227999+ben-l-p@users.noreply.github.com> Date: Wed, 18 Oct 2023 13:52:56 +0100 Subject: [PATCH 222/232] Delete utils/environment_minimal.yml --- utils/environment_minimal.yml | 104 ---------------------------------- 1 file changed, 104 deletions(-) delete mode 100644 utils/environment_minimal.yml diff --git a/utils/environment_minimal.yml b/utils/environment_minimal.yml deleted file mode 100644 index ea5841579..000000000 --- a/utils/environment_minimal.yml +++ /dev/null @@ -1,104 +0,0 @@ -name: sharpy_minimal -channels: - - conda-forge - - defaults -dependencies: - - _libgcc_mutex=0.1=main - - apptools=4.5.0=py_0 - - bzip2=1.0.8=h7b6447c_0 - - ca-certificates=2020.12.5=ha878542_0 - - certifi=2020.12.5=py37h89c1867_1 - - cmake=3.14.0=h52cb24c_0 - - colorama=0.4.1=py37_0 - - configobj=5.0.6=py_0 - - control=0.8.4=py37h89c1867_0 - - curl=7.67.0=hbc83047_0 - - cycler=0.10.0=py_2 - - dbus=1.13.6=he372182_0 - - dill=0.3.1.1=py37_0 - - eigen=3.3.7=hc9558a2_1001 - - envisage=4.8.0=py_0 - - expat=2.2.6=he6710b0_0 - - fontconfig=2.13.1=he4413a7_1000 - - freetype=2.10.0=he983fc9_1 - - future=0.18.2=py37_0 - - gettext=0.19.8.1=hc5be6a0_1002 - - glib=2.58.3=py37h6f030ca_1002 - - gst-plugins-base=1.14.5=h0935bb2_0 - - gstreamer=1.14.5=h36ae1b5_0 - - h5py=2.9.0=py37h7918eee_0 - - hdf4=4.2.13=h3ca952b_2 - - hdf5=1.10.4=hb1b8bf9_0 - - icu=58.2=hf484d3e_1000 - - jpeg=9c=h14c3975_1001 - - jsoncpp=1.8.4=hfd86e86_0 - - kiwisolver=1.1.0=py37hc9558a2_0 - - krb5=1.16.1=h173b8e3_7 - - lapack=3.6.1=ha44fe06_2 - - libblas=3.8.0=14_openblas - - libcblas=3.8.0=14_openblas - - libcurl=7.67.0=h20c2e04_0 - - libedit=3.1.20181209=hc058e9b_0 - - libffi=3.2.1=hd88cf55_4 - - libgcc-ng=9.1.0=hdf63c60_0 - - libgfortran=3.0.0=1 - - libgfortran-ng=7.3.0=hdf63c60_2 - - libiconv=1.15=h516909a_1005 - - liblapack=3.8.0=14_openblas - - libnetcdf=4.6.1=h11d0813_2 - - libogg=1.3.2=h7b6447c_0 - - libopenblas=0.3.7=h5ec1e0e_4 - - libpng=1.6.37=hed695b0_0 - - libssh2=1.8.2=h1ba5d50_0 - - libstdcxx-ng=9.1.0=hdf63c60_0 - - libtheora=1.1.1=h5ab3b9f_1 - - libtiff=4.1.0=h2733197_0 - - libuuid=2.32.1=h14c3975_1000 - - libvorbis=1.3.6=h7b6447c_0 - - libxcb=1.13=h14c3975_1002 - - libxml2=2.9.9=hea5a465_1 - - libxslt=1.1.33=h7d1a2b0_0 - - lxml=4.4.2=py37hefd8a0e_0 - - lz4-c=1.8.1.2=h14c3975_0 - - matplotlib=3.1.1=py37h5429711_0 - - matplotlib-base=3.1.3=py37hef1b27d_0 - - mayavi=4.7.1=py37h94891b3_2 - - ncurses=6.1=he6710b0_1 - - numpy=1.17.3=py37h95a1406_0 - - openssl=1.1.1i=h27cfd23_0 - - pandas=0.25.3=py37hb3f55d8_0 - - pcre=8.43=he1b5a44_0 - - pip=19.3.1=py37_0 - - pthread-stubs=0.4=h14c3975_1001 - - pyface=6.1.2=py37_0 - - pygments=2.5.2=py_0 - - pyparsing=2.4.5=py_0 - - pyqt=5.9.2=py37hcca6a23_4 - - python=3.7.5=h0371630_0 - - python-dateutil=2.8.1=py_0 - - python_abi=3.7=1_cp37m - - pytz=2019.3=py_0 - - pyyaml=5.1.2=py37h7b6447c_0 - - qt=5.9.7=h52cfd70_2 - - readline=7.0=h7b6447c_5 - - rhash=1.3.8=h1ba5d50_0 - - scipy=1.3.2=py37h921218d_0 - - setuptools=42.0.2=py37_0 - - sip=4.19.8=py37hf484d3e_0 - - six=1.13.0=py37_0 - - slycot=0.4.0.0=py37h27181d0_1 - - sqlite=3.30.1=h7b6447c_0 - - tbb=2019.8=hfd86e86_0 - - tk=8.6.8=hbc83047_0 - - tornado=6.0.3=py37h516909a_0 - - traits=5.2.0=py37h7b6447c_0 - - traitsui=6.1.3=py_0 - - vtk=8.2.0=py37haa4764d_200 - - wheel=0.33.6=py37_0 - - xlrd=1.2.0=py37_0 - - xorg-libxau=1.0.9=h14c3975_0 - - xorg-libxdmcp=1.1.3=h516909a_0 - - xz=5.2.4=h14c3975_4 - - yaml=0.1.7=had09818_2 - - zlib=1.2.11=h7b6447c_3 - - zstd=1.3.7=h0b5b093_0 From 0493bfd0fb3461d495f092320ea3b506192f5bfb Mon Sep 17 00:00:00 2001 From: Ben Preston <144227999+ben-l-p@users.noreply.github.com> Date: Wed, 18 Oct 2023 13:53:41 +0100 Subject: [PATCH 223/232] Rename environment_new.yml to environment.yml --- utils/{environment_new.yml => environment.yml} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename utils/{environment_new.yml => environment.yml} (100%) diff --git a/utils/environment_new.yml b/utils/environment.yml similarity index 100% rename from utils/environment_new.yml rename to utils/environment.yml From aace787862880dcf072ea3dda6d6cd7c04e3f52c Mon Sep 17 00:00:00 2001 From: Ben Preston <144227999+ben-l-p@users.noreply.github.com> Date: Wed, 18 Oct 2023 13:55:58 +0100 Subject: [PATCH 224/232] Update bashrc to not use minimal environment --- utils/docker/bashrc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/utils/docker/bashrc b/utils/docker/bashrc index aaf5aee1c..dbe250efe 100644 --- a/utils/docker/bashrc +++ b/utils/docker/bashrc @@ -26,7 +26,7 @@ fi unset __conda_setup # <<< conda initialize <<< -conda activate sharpy_minimal +conda activate sharpy # custom prompt PS1="SHARPy> " From d1760e2b8c10cb7f40f5daebe92daa5f743f59e8 Mon Sep 17 00:00:00 2001 From: Ben Preston <144227999+ben-l-p@users.noreply.github.com> Date: Wed, 18 Oct 2023 13:59:02 +0100 Subject: [PATCH 225/232] Renamed environment file in documentation --- docs/source/content/installation.md | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/docs/source/content/installation.md b/docs/source/content/installation.md index 96caad405..a3f6d6741 100644 --- a/docs/source/content/installation.md +++ b/docs/source/content/installation.md @@ -75,12 +75,10 @@ or running any SHARPy cases. 4. Create the conda environment that SHARPy will use: ```bash cd sharpy/utils - conda env create -f environment_new.yml + conda env create -f environment.yml cd ../.. ``` - This should take approximately 15 minutes to complete (Tested on Ubuntu 22.04.1). We also provide a - lightweight environment with the minimum required dependencies. If you'd like to use it, create the - conda environment using `environment_minimal.yml`. + This should take approximately 15 minutes to complete (Tested on Ubuntu 22.04.1). 5. Activate the `sharpy` conda environment: ```bash @@ -158,7 +156,7 @@ to your taste. 2. Create the conda environment that SHARPy will use: ```bash cd sharpy/utils - conda env create -f environment_new.yml + conda env create -f environment.yml cd ../.. ``` From 2995b061133f37ef243c8443dcb18a6aad39380b Mon Sep 17 00:00:00 2001 From: Ben Preston <144227999+ben-l-p@users.noreply.github.com> Date: Wed, 18 Oct 2023 14:26:25 +0100 Subject: [PATCH 226/232] Update Dockerfile to use renamed environment --- Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index 9f49d0448..c3e8e2ff6 100644 --- a/Dockerfile +++ b/Dockerfile @@ -31,7 +31,7 @@ RUN conda init bash && \ conda config --set always_yes yes --set changeps1 no && \ conda update -q conda && \ conda config --set auto_activate_base false && \ - conda env create -f /sharpy_dir/utils/environment_minimal.yml && conda clean -afy && \ + conda env create -f /sharpy_dir/utils/environment.yml && conda clean -afy && \ find /miniconda3/ -follow -type f -name '*.a' -delete && \ find /miniconda3/ -follow -type f -name '*.pyc' -delete && \ find /miniconda3/ -follow -type f -name '*.js.map' -delete From 5034646d287ce6766a2fa5d2b6290be702e20001 Mon Sep 17 00:00:00 2001 From: Ben Preston <144227999+ben-l-p@users.noreply.github.com> Date: Wed, 18 Oct 2023 14:27:45 +0100 Subject: [PATCH 227/232] Update sharpy_tests.yaml to use renamed environment --- .github/workflows/sharpy_tests.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/sharpy_tests.yaml b/.github/workflows/sharpy_tests.yaml index 6945cc94d..d5dff4462 100644 --- a/.github/workflows/sharpy_tests.yaml +++ b/.github/workflows/sharpy_tests.yaml @@ -51,7 +51,7 @@ jobs: hash -r export QT_QPA_PLATFORM='offscreen' sudo apt install libeigen3-dev - conda env create -f utils/environment_new.yml + conda env create -f utils/environment.yml conda init bash source activate sharpy git submodule init From 0b343d87bf231d636630fc7ce96f816e51d86244 Mon Sep 17 00:00:00 2001 From: Ben Preston <144227999+ben-l-p@users.noreply.github.com> Date: Wed, 18 Oct 2023 14:28:59 +0100 Subject: [PATCH 228/232] Update contributing.md to use renamed environment --- docs/source/content/contributing.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/source/content/contributing.md b/docs/source/content/contributing.md index 92caf694a..3734a3086 100644 --- a/docs/source/content/contributing.md +++ b/docs/source/content/contributing.md @@ -11,7 +11,7 @@ If you are submitting a bug report: same branch. 2. Double check that your python distribution is updated by comparing with -the `utils/environment_*.yml` file. +the `utils/environment.yml` file. 3. Try to assemble a minimal working example that can be run quickly and easily. From 9ad187558909f04d469049f75e83c5f10f4b60c3 Mon Sep 17 00:00:00 2001 From: kccwing <60852830+kccwing@users.noreply.github.com> Date: Wed, 18 Oct 2023 16:08:08 +0100 Subject: [PATCH 229/232] Update conf.py for v2.2 --- docs/source/conf.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/source/conf.py b/docs/source/conf.py index 0ae8306e8..01c0029a9 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -82,9 +82,9 @@ # built documents. # # The short X.Y version. -version = '2.1' +version = '2.2' # The full version, including alpha/beta/rc tags. -release = '2.1' +release = '2.2' # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. From 25610b3ea18ba0a8293f5c70948e46038d7cfb5b Mon Sep 17 00:00:00 2001 From: kccwing <60852830+kccwing@users.noreply.github.com> Date: Wed, 18 Oct 2023 16:08:55 +0100 Subject: [PATCH 230/232] Update .version.json for v2.2 --- .version.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.version.json b/.version.json index de22c42a8..6c490a523 100644 --- a/.version.json +++ b/.version.json @@ -1,6 +1,6 @@ { "schemaVersion": 1, "label": "release version", - "message": "2.1", + "message": "2.2", "color": "green" } From 32f9bed39bab5635bdfcb407b8f1b64de1c7e0f8 Mon Sep 17 00:00:00 2001 From: kccwing <60852830+kccwing@users.noreply.github.com> Date: Wed, 18 Oct 2023 16:09:38 +0100 Subject: [PATCH 231/232] Update version.py for v2.2 --- sharpy/version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sharpy/version.py b/sharpy/version.py index 579a34419..0cf04b30f 100644 --- a/sharpy/version.py +++ b/sharpy/version.py @@ -1,2 +1,2 @@ # version stored here to don't load dependencies by storing it in __init__.py -__version__ = '2.0' \ No newline at end of file +__version__ = '2.2' From 806fd92e0d15c2b98faeb486d704e5f012ff2e42 Mon Sep 17 00:00:00 2001 From: kccwing <60852830+kccwing@users.noreply.github.com> Date: Wed, 18 Oct 2023 16:10:36 +0100 Subject: [PATCH 232/232] Update contributing.md for v2.2 --- docs/source/content/contributing.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/source/content/contributing.md b/docs/source/content/contributing.md index 3734a3086..b26212e3b 100644 --- a/docs/source/content/contributing.md +++ b/docs/source/content/contributing.md @@ -212,7 +212,7 @@ In the release candidate branch: 2. Update `version.json` file -3. Update version in `sharpy/__init__.py` file +3. Update version in `sharpy/version.py` file 4. Commit, push and wait for tests to pass