Skip to content

Commit 2777fff

Browse files
committed
Merge remote-tracking branch 'origin/develop' into gust_vanes
2 parents 375d09b + a6dcd00 commit 2777fff

17 files changed

+3521
-61
lines changed
Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
function [success] = control_design_script(route_directory)
2+
addpath(strcat(route_directory,'/matlab_functions/'));
3+
success = false;
4+
%% Define parameters;
5+
case_name = 'pazy_ROM';
6+
output_folder = strcat(route_directory,'/output/',case_name);
7+
output_folder_linear = strcat(output_folder, '/linear_results/');
8+
complex_matrices = true;
9+
10+
%% Reade from SHARPy generated state space model
11+
[state_space_system, eta_ref] = read_SHARPy_state_space_system(...
12+
strcat(case_name, '.linss.h5'), ...
13+
strcat(output_folder, '/savedata/'), ...
14+
complex_matrices...
15+
);
16+
17+
%% Load simulation settings and store in struct
18+
load(strcat(output_folder_linear, 'simulation_parameters.mat'));
19+
n_nodes = uint8(n_nodes);
20+
rigid_body_motions = false;
21+
22+
input_settings = set_input_parameters(u_inf, ...
23+
state_space_system.Ts, ...
24+
num_modes, ...
25+
num_aero_states, ...
26+
rigid_body_motions, ...
27+
simulation_time, ...
28+
num_control_surfaces, ...
29+
n_nodes, ...
30+
control_input_start, ...
31+
gust_input_start);
32+
33+
%% Remove unused input and outputs from the generated ROM in SHARPy and link deflection and its rate
34+
state_space_system = adjust_state_space_system(state_space_system, input_settings);
35+
36+
37+
%% Get gust input
38+
39+
gust = get_1minuscosine_gust_input(...
40+
gust_length, ...
41+
gust_intensity, ...
42+
state_space_system.Ts, ...
43+
u_inf, ...
44+
simulation_time);
45+
%% Configure Simulink Inputs
46+
model_name = "PID_linear_model";
47+
simIn = Simulink.SimulationInput(model_name);
48+
simIn = setVariable(simIn,'D_gain',0);
49+
simIn = setVariable(simIn,'P_gain',0);
50+
simIn = setVariable(simIn,'I_gain',0);
51+
simIn = setVariable(simIn,'state_space_system',state_space_system);
52+
simIn = setVariable(simIn,'input_settings',input_settings);
53+
simIn = setVariable(simIn,'gust', gust);
54+
%% Run Simulink with PID Controller
55+
warning('off','all');
56+
% open loop
57+
fprintf('Simulate linear open-loop gust response.')
58+
simIn = setVariable(simIn,'controller_on',0);
59+
60+
out_open_loop = sim(simIn);
61+
% closed loop P = 5
62+
simIn = setVariable(simIn,'controller_on',1);
63+
simIn = setVariable(simIn,'P_gain',5);
64+
fprintf('Simulate linear closed-loop gust response with P=5.')
65+
out_PID_5 = sim(simIn);
66+
67+
% closed loop P = 10
68+
simIn = setVariable(simIn,'P_gain',10);
69+
fprintf('Simulate linear closed-loop gust response with P=10.')
70+
out_PID_10 = sim(simIn);
71+
72+
%% Save results
73+
deflection = [out_PID_5.delta.Data(:,1)...
74+
out_PID_10.delta.Data(:,1)...
75+
zeros(size(out_PID_5.delta.Time, 1), 1)];
76+
deflection_rate = [out_PID_5.delta_dot.Data(:,1)...
77+
out_PID_10.delta_dot.Data(:,1)...
78+
zeros(size(out_PID_5.delta_dot.Time, 1), 1)];
79+
tip_deflection = [out_PID_5.actual_output.Data(:,1)...
80+
out_PID_10.actual_output.Data(:,1)...
81+
out_open_loop.actual_output.Data(:,1)];
82+
tip_deflection= tip_deflection + eta_ref(n_nodes/2*6-3);
83+
time_array = out_open_loop.tout;
84+
85+
output_folder_linear = strcat(output_folder, '/linear_results/')
86+
writematrix(deflection, strcat(output_folder_linear, 'deflection.txt'),'Delimiter',',');
87+
writematrix(time_array, strcat(output_folder_linear, 'time_array_linear.txt'),'Delimiter',',');
88+
writematrix(deflection_rate,strcat(output_folder_linear, './deflection_rate.txt'),'Delimiter',',');
89+
writematrix(tip_deflection,strcat(output_folder_linear, './tip_deflection.txt'),'Delimiter',',');
90+
91+
success = true
92+
93+
end
Lines changed: 207 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,207 @@
1+
"""
2+
Script to set the required SHARPy settings for all cases within
3+
the UDP Control example notebook.
4+
"""
5+
def get_settings_udp(model, flow, **kwargs):
6+
gust = kwargs.get('gust', False)
7+
8+
horseshoe = kwargs.get('horseshoe', False)
9+
gravity = kwargs.get('gravity', True)
10+
wake_length_factor = kwargs.get('wake_length_factor', 10)
11+
12+
num_cores = kwargs.get('num_cores', 2)
13+
num_modes = kwargs.get('num_modes', 20)
14+
free_flight = kwargs.get('free_flight', False)
15+
tolerance = kwargs.get('tolerance', 1e-6)
16+
n_load_steps = kwargs.get('n_load_steps', 5)
17+
fsi_tolerance = kwargs.get('fsi_tolerance', 1e-6)
18+
relaxation_factor = kwargs.get('relaxation_factor', 0.1)
19+
newmark_damp = kwargs.get('newmark_damp', 0.5e-4)
20+
output_folder = kwargs.get('output_folder', './output/')
21+
model.config['SHARPy'] = {'case': model.case_name,
22+
'route': model.route,
23+
'flow': flow,
24+
'write_screen': kwargs.get('write_screen', 'on'),
25+
'write_log': 'on',
26+
'save_settings': 'on',
27+
'log_folder': output_folder + '/',
28+
'log_file': model.case_name + '.log'}
29+
30+
31+
model.config['BeamLoader'] = {'unsteady': 'off',
32+
'orientation': model.quat}
33+
34+
model.config['AerogridLoader'] = {'unsteady': 'on',
35+
'aligned_grid': 'on',
36+
'mstar': wake_length_factor*model.M, #int(20/tstep_factor),
37+
'wake_shape_generator': 'StraightWake',
38+
'wake_shape_generator_input': {
39+
'u_inf':model.u_inf,
40+
'u_inf_direction': [1., 0., 0.],
41+
'dt':model.dt,
42+
},
43+
}
44+
if horseshoe:
45+
model.config['AerogridLoader']['mstar'] = 1
46+
47+
model.config['StaticCoupled'] = {
48+
'print_info': 'on',
49+
'max_iter': 200,
50+
'n_load_steps': n_load_steps,
51+
'tolerance': 1e-5,
52+
'relaxation_factor': relaxation_factor,
53+
'aero_solver': 'StaticUvlm',
54+
'aero_solver_settings': {
55+
'rho': model.rho,
56+
'print_info': 'off',
57+
'horseshoe': 'on',
58+
'num_cores': num_cores,
59+
'n_rollup': 0,
60+
'velocity_field_generator': 'SteadyVelocityField',
61+
'velocity_field_input': {
62+
'u_inf': model.u_inf,
63+
'u_inf_direction': model.u_inf_direction},
64+
'vortex_radius': 1e-9},
65+
'structural_solver': 'NonLinearStatic',
66+
'structural_solver_settings': {'print_info': 'off',
67+
'max_iterations': 200,
68+
'num_load_steps': 5,
69+
'delta_curved': 1e-6,
70+
'min_delta': 1e-8,
71+
'gravity': gravity,
72+
'gravity': 9.81},
73+
}
74+
75+
76+
model.config['AerogridPlot'] = {'include_rbm': 'off',
77+
'include_applied_forces': 'on',
78+
'minus_m_star': 0}
79+
model.config['BeamPlot'] = {'include_rbm': 'off',
80+
'include_applied_forces': 'on'}
81+
82+
model.config['WriteVariablesTime'] = {'structure_variables': ['pos'],
83+
'structure_nodes': list(range(0, model.num_node_surf)),
84+
'cleanup_old_solution': 'on',
85+
}
86+
87+
88+
89+
model.config['DynamicCoupled'] = {'print_info': 'on',
90+
'structural_substeps': 10,
91+
'dynamic_relaxation': 'on',
92+
'cleanup_previous_solution': 'on',
93+
'structural_solver': 'NonLinearDynamicPrescribedStep',
94+
'structural_solver_settings': {'print_info': 'off',
95+
'max_iterations': 950,
96+
'delta_curved': 1e-1,
97+
'min_delta': tolerance,
98+
'newmark_damp': newmark_damp,
99+
'gravity': gravity,
100+
'gravity': 9.81,
101+
'num_steps': model.n_tstep,
102+
'dt':model.dt,
103+
},
104+
'aero_solver': 'StepUvlm',
105+
'aero_solver_settings': {'print_info': 'on',
106+
'num_cores': num_cores,
107+
'convection_scheme': 2,
108+
'velocity_field_generator': 'SteadyVelocityField',
109+
'velocity_field_input': {'u_inf': model.u_inf,
110+
'u_inf_direction': [1., 0., 0.]},
111+
'rho': model.rho,
112+
'n_time_steps': model.n_tstep,
113+
'vortex_radius': 1e-9,
114+
'dt': model.dt,
115+
'gamma_dot_filtering': 3},
116+
'fsi_substeps': 200,
117+
'fsi_tolerance': fsi_tolerance,
118+
'relaxation_factor': model.relaxation_factor,
119+
'minimum_steps': 1,
120+
'relaxation_steps': 150,
121+
'final_relaxation_factor': 0.0,
122+
'n_time_steps': model.n_tstep,
123+
'dt': model.dt,
124+
'include_unsteady_force_contribution': kwargs.get('unsteady_force_distribution', True),
125+
'postprocessors': ['WriteVariablesTime', 'BeamPlot', 'AerogridPlot'],
126+
'postprocessors_settings': {'BeamPlot': {'include_rbm': 'on',
127+
'include_applied_forces': 'on'},
128+
'StallCheck': {},
129+
'AerogridPlot': {
130+
'u_inf': model.u_inf,
131+
'include_rbm': 'on',
132+
'include_applied_forces': 'on',
133+
'minus_m_star': 0},
134+
'WriteVariablesTime': {
135+
'structure_variables': ['pos', 'psi'],
136+
'structure_nodes': [model.num_node_surf - 1,
137+
model.num_node_surf,
138+
model.num_node_surf + 1],
139+
},
140+
},
141+
'network_settings': kwargs.get('network_settings', {}),
142+
}
143+
144+
if gust:
145+
gust_settings = kwargs.get('gust_settings', {'gust_shape': '1-cos',
146+
'gust_length': 10.,
147+
'gust_intensity': 0.01,
148+
'gust_offset': 0.})
149+
model.config['DynamicCoupled']['aero_solver_settings']['velocity_field_generator'] = 'GustVelocityField'
150+
model.config['DynamicCoupled']['aero_solver_settings']['velocity_field_input'] = {'u_inf': model.u_inf,
151+
'u_inf_direction': [1., 0, 0],
152+
'relative_motion': bool(not free_flight),
153+
'offset': gust_settings['gust_offset'],
154+
'gust_shape': gust_settings['gust_shape'],
155+
'gust_parameters': {
156+
'gust_length': gust_settings['gust_length'],
157+
'gust_intensity': gust_settings['gust_intensity'] * model.u_inf,
158+
}
159+
}
160+
161+
model.config['PickleData'] = {}
162+
163+
model.config['LinearAssembler'] = {'linear_system': 'LinearAeroelastic',
164+
'inout_coordinates': 'nodes',
165+
# 'recover_accelerations': True,
166+
'linear_system_settings': {
167+
'beam_settings': {'modal_projection': True,
168+
'inout_coords': 'modes',
169+
'discrete_time': True,
170+
'newmark_damp': newmark_damp,
171+
'discr_method': 'newmark',
172+
'dt': model.dt,
173+
'proj_modes': 'undamped',
174+
'num_modes': num_modes,
175+
'print_info': 'on',
176+
'gravity': gravity,
177+
'remove_dofs': []},
178+
'aero_settings': {'dt': model.dt,
179+
'integr_order': 2,
180+
'density': model.rho,
181+
'remove_predictor': True,
182+
'use_sparse': 'off',
183+
'gust_assembler': 'LeadingEdge',
184+
'ScalingDict': kwargs.get('scaling_dict', {'length':1, 'speed': 1, 'density': 1}),
185+
},
186+
'track_body': free_flight,
187+
'use_euler': free_flight,
188+
}}
189+
model.config['Modal'] = {'print_info': True,
190+
'use_undamped_modes': True,
191+
'NumLambda': num_modes,
192+
'rigid_body_modes': free_flight,
193+
'write_modes_vtk': False,
194+
'print_matrices': False,
195+
'continuous_eigenvalues': 'off',
196+
'dt': model.dt,
197+
'plot_eigenvalues': False,
198+
}
199+
model.config['SaveData']['save_linear'] = True
200+
if kwargs.get('remove_gust_input_in_statespace', False):
201+
model.config['LinearAssembler']['linear_system_settings']['aero_settings']['remove_inputs'] = ['u_gust']
202+
203+
rom_settings = kwargs.get('rom_settings', {'use': False})
204+
if rom_settings['use']:
205+
model.config['LinearAssembler']['linear_system_settings']['aero_settings']['rom_method'] = rom_settings['rom_method'],
206+
model.config['LinearAssembler']['linear_system_settings']['aero_settings']['rom_method_settings'] = rom_settings['rom_method_settings']
207+
return model.config
Binary file not shown.
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
function state_space_converted = adjust_state_space_system(state_space_system, input_settings)
2+
%convert_state_space_for_LQR Adjust extracted state space system
3+
% The loaded state space system exported from SHARPy is adjusted here for
4+
% the closed-loop simulations. This includes:
5+
% - extracts the column from the B and D matrices that describe the
6+
% effect of the gust input to the states
7+
% - removing all unused inputs and rearranging for example the
8+
% deflection of a control surface and its rate as both depend on
9+
% each other
10+
% - removes not considered outputs
11+
% - assembles updated discrete state space system
12+
13+
14+
%% Reduce model by deleting unused outupts (only tip deflection relevant here)
15+
C_sensor = state_space_system.C(input_settings.index.tip_displacement,:);
16+
D_sensor = state_space_system.D(input_settings.index.tip_displacement,:);
17+
18+
%% Reduce model with not used inputs
19+
idx_input_end =input_settings.index.control_input_start + 2 * input_settings.num_control_surfaces - 1;
20+
21+
idx_inputs = [input_settings.index.control_input_start:idx_input_end];
22+
B_cs = state_space_system.B(:,idx_inputs);
23+
D_cs = D_sensor(:,idx_inputs);
24+
25+
26+
%% Extract delta to state
27+
28+
new_A_colum = B_cs(:,1 :input_settings.num_control_surfaces);
29+
A = [state_space_system.A new_A_colum; ...
30+
zeros(input_settings.num_control_surfaces,...
31+
(size(state_space_system.A,2)+ input_settings.num_control_surfaces))];
32+
33+
for counter = 0:1:input_settings.num_control_surfaces-1
34+
A(size(A,1)-counter,size(A,2)-counter) = 1; % Explain one
35+
end
36+
37+
%Delete delta input and add delta_dot influence on delta
38+
B_cs(:,1:input_settings.num_control_surfaces) = [];
39+
B_cs = [B_cs; eye(input_settings.num_control_surfaces) * state_space_system.Ts];
40+
41+
% Adding feed through of the delta-input on the output on C and deleting
42+
% column added in C out of D; new column
43+
C = [C_sensor D_cs(:,1:input_settings.num_control_surfaces)];
44+
D_cs(:, 1:input_settings.num_control_surfaces) = [];
45+
46+
47+
%% Get Disturbance Matrices
48+
G = [state_space_system.B(:,input_settings.index.gust_input_start); ...
49+
zeros(input_settings.num_control_surfaces, 1)];
50+
H = D_sensor(:,input_settings.index.gust_input_start);
51+
%% Assemble final state space system
52+
53+
state_space_converted = ss(A,[B_cs G],C,[D_cs H],state_space_system.Ts);
54+
55+
end

0 commit comments

Comments
 (0)