Supersonic Propulsion - Design Research Projects
Two informal papers describing coursework done for a class on compressible flow and supersonic aerodynamics / propulsion.
The two papers included in this post are from a course called “Rocket Science” which was essentially an introduction to compressible flow and supersonic propulsion.
The first paper is about optimizing a body to minimize pressure and viscous drag. It is relatively brief, but I think it is a good demonstration of formulating an approach and reaching a reasonable answer for a generic problem.
Pressure Drag Optimization
The second paper is a group project describing the design of a ramjet engine. I did a lot of the coding and CAD, and I am pretty proud of the product that we came up with given what we knew back then. Ignoring the caveat that the geometry is all two-dimensional, the inlet design almost makes sense, other than the fact that the turning angle is very high. The expanding nozzle is also reasonable given that it was designed using MOC. The cowl is way too thin to take any load. We won’t even get into the combustor since I still don’t know how that works. Enjoy JP’s pretty CFD plots!
Full Engine Design
Fuselage Oblique and Expansion Waves
Engine Design Code in MATLAB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Final Project - Ramjet Engine %
% 5/10/2024 %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
clc
clear
close all
% This script is for calculating the engine design. Evaluation of the
% design at all operating points is done in another script.
% Full description of the model, assumptions, calculation approach, and
% work done by each team member can be found detailed in our final report.
%% Flags to enable certain portions of the code:
PlotInlet_MaxM = 1; % Boolean for plotting the inlet at M1=3.25
PlotInlet_MinM = 0; % Boolean for plotting the inlet at flow properties specified below
PlotNozzle = 1; % Boolean for plotting the outlet
%% Incoming Flow Properties
M1 = 3.25;
% Assumed pressure and temperature at 55,000 ft
P1 = 9450; % Pa
T1 = 216; % K
gamma = 1.4;
R_air = 287; % J/kg*K
rho1 = P1/(R_air*T1); % kg/m^3
u1 = M1*sqrt(gamma*R_air*T1);
%% Inlet
% The inlet ramp is designed at maximum operating Mach 3.25 to avoid
% complications from the oblique shocks entering and causing distortion.
Mach_max = 3.25;
% Overall inlet geometry parameters
theta_total = 30; % Degrees, total turn angle
n_sections = 10; % Number of turn sections
theta_increment = theta_total/n_sections;
% Calculate properties along each section of inlet
M_inlet_design = zeros(n_sections+1,1); beta_design = zeros(n_sections,1); % Allocate Memory
M_inlet_design(1) = Mach_max; % Assign initial value
for i = 1:n_sections
% Call ObliqueShock function repeatedly
[M_inlet_design(i+1), ~, ~, beta_design(i)] = ObliqueShock(M_inlet_design(i), gamma, theta_increment);
end
% Calculate section lengths such that all the waves intersect at a single point.
[os_wavelength, section_lengths] = deal(zeros(n_sections,1)); % Allocate Memory
% The following parameter is used to scale the entire engine. It is the
% length of the first oblique shock wave up to the intersection point ahead
% of the cowl:
os_wavelength(1) = 1; % Meters
for i = 2:n_sections
% Calculate the lengths of each subsequent section using law of sines.
section_lengths(i-1) = os_wavelength(i-1)*sind(beta_design(i)-beta_design(i-1)+theta_increment)/sind(180-beta_design(i));
os_wavelength(i) = os_wavelength(i-1)*sind(beta_design(i-1)-theta_increment)/sind(180-beta_design(i));
end
% Calculate angles with respect to global coordinate system
theta_sum = zeros(n_sections+1,1); beta_design_global = zeros(n_sections,1); % Allocate Memory
for i = 1:n_sections
theta_sum(i+1) = theta_sum(i) + theta_increment;
beta_design_global(i) = beta_design(i) + theta_sum(i);
end
% Calculate inlet coordinates and shock intersection locations to double
% check geometry.
[x_inlet,y_inlet] = deal(zeros(n_sections+1,1)); % Allocate Memory
[x_beta_design,y_beta_design] = deal(zeros(n_sections,1));
for i = 1:n_sections
x_inlet(i+1) = x_inlet(i) + section_lengths(i)*cosd(theta_sum(i+1));
y_inlet(i+1) = y_inlet(i) - section_lengths(i)*sind(theta_sum(i+1));
x_beta_design(i) = x_inlet(i) + os_wavelength(i)*cosd(beta_design_global(i));
y_beta_design(i) = y_inlet(i) - os_wavelength(i)*sind(beta_design_global(i));
end
% We want 5% of the flow to be wasted at 3.25 mach as a safety margin.
% Calculate the position of the cowl to satisfy this constraint.
s_para = -tand(theta_total);
s_perp = -1/s_para;
x_ns_top = (y_beta_design(n_sections) - y_inlet(n_sections+1) + s_para*x_inlet(n_sections+1) - s_perp*x_beta_design(n_sections))/(s_para-s_perp);
y_ns_top = s_para*(x_ns_top-x_inlet(n_sections+1))+y_inlet(n_sections+1);
delta_x = x_beta_design(n_sections) - x_ns_top;
delta_y = y_beta_design(n_sections) - y_ns_top;
x_ns_cowl = 0.95*delta_x + x_ns_top;
y_ns_cowl = 0.95*delta_y + y_ns_top;
inlet_coordinates = [x_inlet y_inlet ; x_ns_top y_ns_top ; x_ns_cowl y_ns_cowl];
%% Plot inlet at design condition M1=3.25
if PlotInlet_MaxM == 1
fig = figure;
hold on
% Inlet top geometry
plot(x_inlet(1:n_sections),y_inlet(1:n_sections),'-xk')
plot([x_inlet(n_sections) x_ns_top],[y_inlet(n_sections) y_ns_top],'-k')
% Oblique shocks
for i=1:n_sections
plot([x_inlet(i) x_beta_design(i)],[y_inlet(i) y_beta_design(i)],Color='#F19024')
end
% Normal Shock
plot([x_ns_top x_ns_cowl],[y_ns_top y_ns_cowl],'r')
% Cowl point
plot(x_ns_cowl,y_ns_cowl,'*b')
%plot([x_ns_cowl+0.1*cosd(theta_total+15) x_ns_cowl x_ns_cowl+0.1*cosd(theta_total)],[y_ns_cowl-0.1*sind(theta_total+15) y_ns_cowl y_ns_cowl-0.1*sind(theta_total)],'-k')
xlabel("Length [meters]")
ylabel("Height [meters]")
title("Inlet Geometry")
fontsize(15,'points')
% Size margins and plot for screen
xmin = 0;
xmax = x_ns_top;
ymin = y_beta_design(n_sections);
ymax = 0;
dx = xmax-xmin;
dy = ymax-ymin;
ar = dy/dx;
cushion = 0.05;
xlim([xmin - cushion*dx xmax + cushion*dx])
ylim([ymin - cushion*dy ymax + cushion*dy])
screenx = 1920;
screeny = 1080;
fig.Position = [0 0.5*(screeny-screenx*ar) screenx screenx*ar];
end
%% Inlet at set Mach Number
% Now that the above code has been ran for 3.25 mach, the geometry can be
% locked in and we can determine the oblique shock pattern for other mach
% numbers.
% Calculate properties along each section of inlet
[M_inlet,P_inlet,T_inlet] = deal(zeros(n_sections+1,1)); beta = zeros(n_sections,1); % Allocate Memory
M_inlet(1) = M1; P_inlet(1) = P1; T_inlet(1) = T1; % Assign initial values
for i = 1:n_sections
% Call ObliqueShock function repeatedly
[M_inlet(i+1), P_ratio, T_ratio, beta(i)] = ObliqueShock(M_inlet(i), gamma, theta_increment);
P_inlet(i+1) = P_ratio*P_inlet(i); T_inlet(i+1) = T_ratio*T_inlet(i);
end
% Calculate angles with respect to global coordinate system
beta_global = zeros(n_sections,1); % Allocate Memory
for i = 1:n_sections
beta_global(i) = beta(i) + theta_sum(i);
end
% Calculate oblique shock lines up until the cowl y coordinate.
[x_beta,y_beta] = deal(zeros(n_sections,1));
for i = 1:n_sections
slope = -tand(beta_global(i));
x_beta(i) = x_inlet(i) + (y_ns_cowl-0.1-y_inlet(i))/slope;
y_beta(i) = y_ns_cowl-0.1;
end
% Calculate line from last shock intersection and make sure the flow coming
% into the throat is uniform.
s1 = -tand(beta_global(n_sections-1));
s2 = -tand(beta_global(n_sections));
x_lastshockint = (y_inlet(n_sections)-y_inlet(n_sections-1)-s2*x_inlet(n_sections)+s1*x_inlet(n_sections-1))/(s1-s2);
y_lastshockint = s2*(x_lastshockint - x_inlet(n_sections)) + y_inlet(n_sections);
x_dodgecowl = x_lastshockint + 0.05*cosd(theta_total);
y_dodgecowl = y_lastshockint - 0.05*sind(theta_total);
%% Plot inlet at Mach specified in Incoming flow properties
if PlotInlet_MinM == 1
fig = figure;
hold on
% Inlet top geometry
plot(x_inlet(1:n_sections),y_inlet(1:n_sections),'-xk')
plot([x_inlet(n_sections) x_ns_top],[y_inlet(n_sections) y_ns_top],'-k')
% Oblique shocks
for i=1:n_sections
plot([x_inlet(i) x_beta(i)],[y_inlet(i) y_beta(i)],Color='#F19024')
end
% Normal Shock
plot([x_ns_top x_ns_cowl],[y_ns_top y_ns_cowl],'r')
% Cowl point
plot(x_ns_cowl,y_ns_cowl,'*b')
% Last disturbed flow approaching inlet
plot([x_lastshockint x_dodgecowl],[y_lastshockint y_dodgecowl],'-g')
%plot([x_ns_cowl+0.1*cosd(theta_total+15) x_ns_cowl x_ns_cowl+0.1*cosd(theta_total)],[y_ns_cowl-0.1*sind(theta_total+15) y_ns_cowl y_ns_cowl-0.1*sind(theta_total)],'-k')
xlabel("Length [meters]")
ylabel("Height [meters]")
title("Inlet Geometry")
fontsize(15,'points')
% Size margins and plot for screen
xmin = 0;
xmax = x_ns_top;
ymin = y_beta(n_sections);
ymax = 0;
dx = xmax-xmin;
dy = ymax-ymin;
ar = dy/dx;
cushion = 0.05;
xlim([xmin - cushion*dx xmax + cushion*dx])
ylim([ymin - cushion*dy ymax + cushion*dy])
screenx = 1920;
screeny = 1080;
fig.Position = [0 0.5*(screeny-screenx*ar) screenx screenx*ar];
end
%% Cowl (Normal Shock)
Cowl_Area = 0.95*sqrt(delta_x^2 + delta_y^2); %m Area per unit depth at normal shock
M2 = M_inlet(n_sections+1);
P2 = P_inlet(n_sections+1);
T2 = T_inlet(n_sections+1);
m_dot_2 = P2*M2*Cowl_Area*sqrt(gamma/(R_air*T2)); %kg/s/m Mass flow per unit depth
m_dot_air = m_dot_2;
% Normal Shock Relations to determine state after normal shock
g = gamma;
M2_p = sqrt((1 + 0.5*(g-1)*M2^2)/(g*M2^2 - 0.5*(g-1)));
P2_p = P2*(1 + (2*g*(M2^2 - 1))/(g+1));
T2_p = T2*(1 + (2*g*(M2^2 - 1))/(g+1))*((2 + (g-1)*M2^2)/((g+1)*M2^2));
m_dot_2_p = P2_p*M2_p*Cowl_Area*sqrt(gamma/(R_air*T2_p));
% Stagnation state
T0_2_p = T2_p*(1 + 0.5*(g-1)*M2_p^2);
P0_2_p = P2_p*(1 + 0.5*(g-1)*M2_p^2)^(g/(g-1));
%% Quasi-1D expansion of subsonic flow
% A/A* at 2 after the normal shock:
A_star_ratio_2p = (1/M2_p)*((2+(g-1)*M2_p^2)/(g+1))^(0.5*(g+1)/(g-1));
A_star_sec1 = Cowl_Area/A_star_ratio_2p;
% We can change this variable to set the flow characteristics going into
% the combustor.
A3 = 0.4; % Meters, area per unit depth before the flameholder
A_star_ratio_3 = A3/A_star_sec1;
% Use Newton's method to find Mach number at 3
M_search = 0.5; % Start search on the correct side of the curve
M_temp = 0; % Placeholder value
delta = 1; % Placeholder value for step difference
while delta > 0.0000000001
innerterm = (2+(g-1)*M_search^2)/(g+1); % Base term that gets reused
Astarfunction = (M_search^-2) * innerterm^((g+1)/(g-1)) - A_star_ratio_3^2;
dfdM = (2/M_search) * (innerterm)^(2/(g-1)) * (1 - innerterm/(M_search^2));
M_temp = M_search - 0.01*Astarfunction/dfdM;
% Had to make the scaling small (0.01) to make sure it doesn't
% overshoot to the negative side of the function.
delta = abs(M_temp-M_search);
M_search = M_temp;
end
M3 = M_search;
% Isentropic flow means stagnation state has not changed from 2p
T3 = T0_2_p/(1 + 0.5*(g-1)*M3^2);
P3 = P0_2_p/(1 + 0.5*(g-1)*M3^2)^(g/(g-1));
u3 = M3*sqrt(gamma*R_air*T3);
rho3 = P3/(R_air*T3);
m_dot_3 = P3*M3*A3*sqrt(gamma/(R_air*T3));
%% Flameholder
% Adiabatic Pressure Drop
P3_p = P3*(1-0.81*gamma*M3^2);
T3_p = T3;
A3_p = A3;
rho3_p = P3_p/(R_air*T3_p);
% Across the pressure drop, mass must be conserved
u3_p = rho3*u3/rho3_p;
M3_p = u3_p/sqrt(gamma*R_air*T3_p);
m_dot_3_p = P3_p*M3_p*A3_p*sqrt(gamma/(R_air*T3_p));
% Confirm adiabatic
T0_3_p = T3_p*(1 + 0.5*(g-1)*M3_p^2);
%% Combustor
% Stoichiometric Ratio for H2 and Air
% https://www1.eere.energy.gov/hydrogenandfuelcells/tech_validation/pdfs/fcm03r0.pdf
fuel_air_massratio_stoic = 1/34.33;
% Equivalence ratio, manage this so that we avoid unstart from too much
% heating.
phi = 1; % For M1=2.75, phi = 1. For any M1>2.75, phi will decrease to choke at nozzle throat.
m_dot_fuel = m_dot_air * phi * fuel_air_massratio_stoic;
Lfv_H2 = 120*10^6; % J/kg, Lower heating value of H2
q_addn = Lfv_H2*m_dot_fuel/m_dot_air; % J/kg, Heat addition (per unit mass of air) due to fuel combustion
% Find state after instantaneous heat addition
[M4,P4,T4] = HeatAddition_1D(q_addn,M3_p,P3_p,T3_p,R_air,gamma);
% Find "average" velocity, pressure, and stagnation temperature during heating
u4 = M4*sqrt(g*R_air*T4);
u_avg = 0.5*(u3_p+u4);
P_avg = 0.5*(P3_p+P4);
P0_4 = P4*(1 + 0.5*(g-1)*M4^2)^(g/(g-1));
T0_3_p = T3_p*(1+0.5*(g-1)*M3^2);
T0_4 = T4*(1+0.5*(g-1)*M4^2);
T0_avg = 0.5*(T0_3_p+T0_4);
% Calculate a few different combustion times based on different points in
% the process to see how much they differ. All have units of seconds.
tau_3 = 325*(P3_p/101325)^(-1.6)*exp(-0.8*T0_3_p/1000)*10^-6;
tau_avg = 325*(P_avg/101325)^(-1.6)*exp(-0.8*T0_avg/1000)*10^-6;
tau_4 = 325*(P4/101325)^(-1.6)*exp(-0.8*T0_4/1000)*10^-6;
% Required length for full combustion
L_required = u_avg*tau_avg; % meters
A4 = A3_p;
m_dot_4 = P4*M4*A4*sqrt(gamma/(R_air*T4));
%% Quasi 1D Compression
% A/A* at 4 after combustion:
A_star_ratio_4 = (1/M4)*((2+(g-1)*M4^2)/(g+1))^(0.5*(g+1)/(g-1));
% Throat State:
A5 = A3/A_star_ratio_4;
M5 = 1;
T5 = T0_4/(1 + 0.5*(g-1)*M5^2);
P5 = P0_4/(1 + 0.5*(g-1)*M5^2)^(g/(g-1));
m_dot_5 = P5*M5*A5*sqrt(gamma/(R_air*T5));
%% Nozzle Design
% In order to get the aerodynamic sizing we want, we need to figure out the
% shape of the nozzle that will achieve the desired expansion.
P6 = 5*P1; % Pa, desired exit pressure
M6 = sqrt((2/(g-1)) * ((1+0.5*(g-1)*M5^2)*(P5/P6)^((g-1)/g) - 1));
T6 = T0_4/(1 + 0.5*(g-1)*M6^2);
u6 = M6*sqrt(g*R_air*T6);
rho6 = P6/(R_air*T6);
% Required expansion wave theta to achieve this pressure change.
theta_expansion = PrandtlMeyerD(M6, gamma) - PrandtlMeyerD(M5, gamma);
mu6 = asind(1/M6);
n_nz_sections = 500; % Number of rays (waves) to propogate
turn_thetas = linspace(0,theta_expansion,n_nz_sections+1);
turn_thetas = turn_thetas(2:end);
throat_bottom_x = 0;
throat_bottom_y = 0;
throat_top_x = -A5*sind(theta_expansion);
throat_top_y = A5*cosd(theta_expansion);
[nozzle_x, nozzle_y] = deal(zeros(n_nz_sections+1,1));
[nozzle_theta_global, nozzle_nu, nozzle_M, nozzle_mu, waveslope, wallslope] = deal(zeros(n_nz_sections,1));
nozzle_x(1) = throat_top_x; nozzle_y(1) = throat_top_y;
for i = 1:n_nz_sections
nozzle_theta_global(i) = theta_expansion - turn_thetas(i);
nozzle_nu(i) = turn_thetas(i);
nozzle_M(i) = PrandtlMeyerInverseD(nozzle_nu(i),gamma);
nozzle_mu(i) = asind(1/nozzle_M(i));
wallslope(i) = tand(nozzle_theta_global(i));
waveslope(i) = tand(nozzle_mu(i) + nozzle_theta_global(i));
nozzle_x(i+1) = (nozzle_y(i) - throat_bottom_y - wallslope(i)*nozzle_x(i) + waveslope(i)*throat_bottom_x)/(waveslope(i)-wallslope(i));
nozzle_y(i+1) = wallslope(i)*(nozzle_x(i+1)-nozzle_x(i))+nozzle_y(i);
end
nozzle_coordinates = [throat_bottom_x throat_bottom_y; nozzle_x nozzle_y];
A6 = nozzle_y(n_nz_sections+1);
m_dot_6 = P6*M6*A6*sqrt(gamma/(R_air*T6));
%% Plot Nozzle
if PlotNozzle == 1
fig2 = figure;
hold on
plot(nozzle_x,nozzle_y,'-xk')
for i = 1:n_nz_sections
plot([throat_bottom_x nozzle_x(i)],[throat_bottom_y nozzle_y(i)],':r')
end
xlabel("Length [meters]")
ylabel("Height [meters]")
title("Nozzle Geometry")
fontsize(15,'points')
% Size margins and plot for screen
xmin = nozzle_x(1);
xmax = nozzle_x(n_nz_sections+1);
ymin = 0;
ymax = nozzle_y(n_nz_sections+1);
dx = xmax-xmin;
dy = ymax-ymin;
ar = dy/dx;
cushion = 0.05;
xlim([xmin - cushion*dx xmax + cushion*dx])
ylim([ymin - cushion*dy ymax + cushion*dy])
screenx = 1920*0.8;
screeny = 1080*0.8;
fig2.Position = [0 0.5*(screeny-screenx*ar) screenx screenx*ar];
end
Engine Performance Code in MATLAB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Final Project - Ramjet Engine %
% 5/10/2024 %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
clc
close all
clearvars -except n_sections P1 T1 gamma R_air theta_total theta_increment ...
theta_sum x_inlet y_inlet x_ns_top y_ns_top x_ns_cowl y_ns_cowl ...
section_lengths Cowl_Area n_nz_sections nozzle_x nozzle_y Lfv_H2 ...
fuel_air_massratio_stoic theta_expansion
% Mach number to evaluate performance at
M1 = 2.75;
PlotEngine = 1;
%% Inlet
% Calculate inlet properties
[M_inlet,P_inlet,T_inlet] = deal(zeros(n_sections+1,1)); beta = zeros(n_sections,1); % Allocate Memory
M_inlet(1) = M1; P_inlet(1) = P1; T_inlet(1) = T1; % Assign initial values
for i = 1:n_sections
% Call ObliqueShock function repeatedly
[M_inlet(i+1), P_ratio, T_ratio, beta(i)] = ObliqueShock(M_inlet(i), gamma, theta_increment);
P_inlet(i+1) = P_ratio*P_inlet(i); T_inlet(i+1) = T_ratio*T_inlet(i);
end
% Calculate angles with respect to global coordinate system
beta_global = zeros(n_sections,1); % Allocate Memory
for i = 1:n_sections
beta_global(i) = beta(i) + theta_sum(i);
end
% Calculate oblique shock lines up until the cowl y coordinate.
[x_beta,y_beta] = deal(zeros(n_sections,1));
for i = 1:n_sections
slope = -tand(beta_global(i));
x_beta(i) = x_ns_cowl;
y_beta(i) = y_inlet(i) + slope*(x_ns_cowl - x_inlet(i));
end
% Calculate line from last shock intersection and make sure the flow coming
% into the throat is uniform.
s1 = -tand(beta_global(n_sections-1));
s2 = -tand(beta_global(n_sections));
x_lastshockint = (y_inlet(n_sections)-y_inlet(n_sections-1)-s2*x_inlet(n_sections)+s1*x_inlet(n_sections-1))/(s1-s2);
y_lastshockint = s2*(x_lastshockint - x_inlet(n_sections)) + y_inlet(n_sections);
x_dodgecowl = x_lastshockint + 0.05*cosd(theta_total);
y_dodgecowl = y_lastshockint - 0.05*sind(theta_total);
%% Cowl (Normal Shock)
M2 = M_inlet(n_sections+1);
P2 = P_inlet(n_sections+1);
T2 = T_inlet(n_sections+1);
m_dot_2 = P2*M2*Cowl_Area*sqrt(gamma/(R_air*T2)); %kg/s/m Mass flow per unit depth
m_dot_air = m_dot_2;
u2 = M2*sqrt(gamma*R_air*T2);
% Normal Shock Relations to determine state after normal shock
g = gamma;
M2_p = sqrt((1 + 0.5*(g-1)*M2^2)/(g*M2^2 - 0.5*(g-1)));
P2_p = P2*(1 + (2*g*(M2^2 - 1))/(g+1));
T2_p = T2*(1 + (2*g*(M2^2 - 1))/(g+1))*((2 + (g-1)*M2^2)/((g+1)*M2^2));
m_dot_2_p = P2_p*M2_p*Cowl_Area*sqrt(gamma/(R_air*T2_p));
% Stagnation state
T0_2_p = T2_p*(1 + 0.5*(g-1)*M2_p^2);
P0_2_p = P2_p*(1 + 0.5*(g-1)*M2_p^2)^(g/(g-1));
%% Quasi-1D expansion of subsonic flow
% A/A* at 2 after the normal shock:
A_star_ratio_2p = (1/M2_p)*((2+(g-1)*M2_p^2)/(g+1))^(0.5*(g+1)/(g-1));
A_star_sec1 = Cowl_Area/A_star_ratio_2p;
% We can change this variable to set the flow characteristics going into
% the combustor.
A3 = 0.4; % Meters, area per unit depth before the flameholder
A_star_ratio_3 = A3/A_star_sec1;
% Use Newton's method to find Mach number at 3
M_search = 0.5; % Start search on the correct side of the curve
M_temp = 0; % Placeholder value
delta = 1; % Placeholder value for step difference
while delta > 0.0000000001
innerterm = (2+(g-1)*M_search^2)/(g+1); % Base term that gets reused
Astarfunction = (M_search^-2) * innerterm^((g+1)/(g-1)) - A_star_ratio_3^2;
dfdM = (2/M_search) * (innerterm)^(2/(g-1)) * (1 - innerterm/(M_search^2));
M_temp = M_search - 0.01*Astarfunction/dfdM;
% Had to make the scaling small (0.01) to make sure it doesn't
% overshoot to the negative side of the function.
delta = abs(M_temp-M_search);
M_search = M_temp;
end
M3 = M_search;
% Isentropic flow means stagnation state has not changed from 2p
T3 = T0_2_p/(1 + 0.5*(g-1)*M3^2);
P3 = P0_2_p/(1 + 0.5*(g-1)*M3^2)^(g/(g-1));
u3 = M3*sqrt(gamma*R_air*T3);
rho3 = P3/(R_air*T3);
m_dot_3 = P3*M3*A3*sqrt(gamma/(R_air*T3));
%% Flameholder
% Adiabatic Pressure Drop
P3_p = P3*(1-0.81*gamma*M3^2);
T3_p = T3;
A3_p = A3;
rho3_p = P3_p/(R_air*T3_p);
% Across the pressure drop, mass must be conserved
u3_p = rho3*u3/rho3_p;
M3_p = u3_p/sqrt(gamma*R_air*T3_p);
m_dot_3_p = P3_p*M3_p*A3_p*sqrt(gamma/(R_air*T3_p));
% Confirm adiabatic
T0_3_p = T3_p*(1 + 0.5*(g-1)*M3_p^2);
%% Combustor
M4 = 0.280816062266156; % Mach number after combustion specified by Quasi 1D choke point
A4 = A3_p;
P_ratio_comb = (1+g*M3_p^2)/(1+g*M4^2);
P4 = P3_p*P_ratio_comb;
P0_4 = P4*(1 + 0.5*(g-1)*M4^2)^(g/(g-1));
T4 = T3_p*P_ratio_comb^2*(M4/M3_p)^2;
T0_4 = T4*(1 + 0.5*(g-1)*M4^2);
rho4 = rho3_p*P_ratio_comb^-1*(M4/M3_p)^-2;
u4 = M4*sqrt(g*R_air*T4);
m_dot_4_1 = P4*M4*A4*sqrt(gamma/(R_air*T4));
m_dot_4_2 = rho4*u4*A4;
c_p = R_air*g/(g-1);
q_addn = (T0_4-T0_3_p)*c_p;
m_dot_fuel = q_addn*m_dot_air/Lfv_H2; % J/kg, Heat addition (per unit mass of air) due to fuel combustion
phi = m_dot_fuel/(m_dot_air*fuel_air_massratio_stoic); % Equivalence ratio
%% Quasi 1D Compression
% A/A* at 4 after combustion:
A_star_ratio_4 = (1/M4)*((2+(g-1)*M4^2)/(g+1))^(0.5*(g+1)/(g-1));
% Throat State:
A5 = A3/A_star_ratio_4;
M5 = 1;
T5 = T0_4/(1 + 0.5*(g-1)*M5^2);
P5 = P0_4/(1 + 0.5*(g-1)*M5^2)^(g/(g-1));
m_dot_5 = P5*M5*A5*sqrt(gamma/(R_air*T5));
%% Nozzle
[M6, Pratio, Tratio, mu5, mu6] = ExpansionWave(M5, gamma, theta_expansion);
P6 = P5*Pratio;
T6 = T5*Tratio;
rho6 = P6/(R_air*T6);
u6 = M6*sqrt(g*R_air*T6);
A6 = nozzle_y(n_nz_sections+1);
m_dot_6_1 = P6*M6*A6*sqrt(gamma/(R_air*T6));
m_dot_6_2 = rho6*u6*A6;
%% Calculate pressure drag on inlet surfaces
inlet_drag_x = zeros(n_sections,1);
for i = 1:n_sections
inlet_drag_x(i) = P_inlet(i+1)*section_lengths(i)*sind(theta_sum(i+1));
end
%% Calculate pressure drag at shock surface
shock_drag_x = P2*Cowl_Area*cosd(theta_total);
%% Inlet flow momentum x component
inlet_mv_x = -m_dot_air*u2*cosd(theta_total);
%% Calculate pressure drag on exterior surfaces
% List of thetas and lengths that construct the exterior of engine
% Determined these in CAD once the inlet and outlet points were confirmed
theta_ext_increment = [4 -20 -20 -4 -10 -23.61904278];
n_ext_sections = length(theta_ext_increment);
ext_lengths = [0.08 0.07 0.20970808 1.4 0.2 0.14274769];
[theta_ext_global, x_ext, y_ext] = deal(zeros(n_ext_sections+1,1));
theta_ext_global(1) = -theta_total; x_ext(1) = x_ns_cowl; y_ext(1) = y_ns_cowl;
for i = 1:n_ext_sections
theta_ext_global(i+1) = theta_ext_global(i) - theta_ext_increment(i);
x_ext(i+1) = x_ext(i) + ext_lengths(i)*cosd(theta_ext_global(i+1));
y_ext(i+1) = y_ext(i) + ext_lengths(i)*sind(theta_ext_global(i+1));
end
% Calculate mach and pressures along those sections
[M_ext, P_ext] = deal(zeros(n_ext_sections+1,1));
[mu1_ext, mu2_ext] = deal(zeros(n_ext_sections-1,1));
M_ext(1) = M2; P_ext(1) = P2;
% The first one is an oblique shock
[M_ext(2), P_ratio, ~, beta_ext] = ObliqueShock(M_ext(1),gamma,theta_ext_increment(1));
P_ext(2) = P_ratio*P_ext(1);
% The next ones are expansion waves
for i = 2:n_ext_sections
[M_ext(i+1), P_ratio, ~, mu1_ext(i-1), mu2_ext(i-1)] = ExpansionWave(M_ext(i),gamma,-theta_ext_increment(i));
P_ext(i+1) = P_ratio*P_ext(i);
end
% Calculate pressure drag
ext_drag_x = zeros(n_ext_sections,1);
% Pressure drag on exterior pointing in flow direction
for i = 1:n_ext_sections
ext_drag_x(i) = P_ext(i+1)*ext_lengths(i)*sind(-theta_ext_global(i));
end
%% Calculate pressure drag and flow momentum across the nozzle
% Since the pressure at the nozzle exit is so much larger than the pressure
% at the outlet cowl, we will assume that a control volume edge drawn
% between the outlet cowl edge and the end of the nozzle will have uniform
% flow and pressure across it governed by the final expansion properties of
% the nozzle. This should be equivalent to drawing the control volume edge
% across the throat and along the surface of the nozzle.
% Control Volume Outlet Edge
outlet_cv_edge_area = sqrt((nozzle_x(n_nz_sections+1))^2 + (nozzle_y(n_nz_sections+1))^2); % m, Area Per unit depth
% Nozzle pressure drag pointing in flow direction
nozzle_drag_x = -P1*outlet_cv_edge_area*sind(mu6);
% Nozzle flow momentum x component
nozzle_mv_x = m_dot_air*u6;
%% Calculate total thrust
Thrust = nozzle_mv_x + inlet_mv_x - (sum(inlet_drag_x) + shock_drag_x + sum(ext_drag_x) + nozzle_drag_x);
%% Calculate pressure, temperature, stagnation pressure, stagnation temperature, relative entropy along axial
%% Plot the above values at M=2.75,3,3.25
%% Plot entire engine at chosen operating condition
if PlotEngine == 1
fig = figure;
hold on
% Inlet top geometry
plot(x_inlet(1:n_sections),y_inlet(1:n_sections),'-k')
plot([x_inlet(n_sections) x_ns_top],[y_inlet(n_sections) y_ns_top],'-k')
% Oblique shocks
for i = 1:n_sections
plot([x_inlet(i) x_beta(i)],[y_inlet(i) y_beta(i)],Color='#F19024')
end
% Normal Shock
plot([x_ns_top x_ns_cowl],[y_ns_top y_ns_cowl],'r')
% Upper inside part
plot([x_ns_top x_ns_top+0.05*cosd(theta_total)],[y_ns_top y_ns_top-0.05*sind(theta_total)],'-k')
% Cowl inside part
plot([x_ns_cowl x_ns_cowl+0.05*cosd(theta_total)],[y_ns_cowl y_ns_cowl-0.05*sind(theta_total)],'-k')
% Last disturbed flow approaching inlet
plot([x_lastshockint x_dodgecowl],[y_lastshockint y_dodgecowl],'-g')
% Exterior Sections
plot(x_ext,y_ext,'-k')
% Exterior Oblique Shock
plot([x_ns_cowl x_ns_cowl+0.1*cosd(-theta_total-beta_ext)],[y_ns_cowl y_ns_cowl+0.1*sind(-theta_total-beta_ext)],'-',Color='#F19024')
% Exterior Expansion Waves
expwavelength = 0.1;
for i = 2:n_ext_sections
plot([x_ext(i)+expwavelength*cosd(theta_ext_global(i)-mu1_ext(i-1)) x_ext(i) x_ext(i)+expwavelength*cosd(theta_ext_global(i+1)-mu2_ext(i-1))],...
[y_ext(i)+expwavelength*sind(theta_ext_global(i)-mu1_ext(i-1)) y_ext(i) y_ext(i)+expwavelength*sind(theta_ext_global(i+1)-mu2_ext(i-1))],...
'-',color='#1CCAD8')
end
% Nozzle
nozzle_shift_x = x_ext(n_ext_sections+1);
nozzle_shift_y = y_ext(n_ext_sections+1);
plot(nozzle_x + nozzle_shift_x,nozzle_y + nozzle_shift_y,'-k')
plot([0 nozzle_x(1)] + nozzle_shift_x,[0 nozzle_y(1)] + nozzle_shift_y,'-r')
% Nozzle Expansion Waves
for i = 1:100:n_nz_sections
plot([0 nozzle_x(i+1)]+nozzle_shift_x,[0 nozzle_y(i+1)]+nozzle_shift_y,'-',color='#1CCAD8')
end
% Upper Line
plot([0 3.6],[0 0],'-k')
xlabel("Length [meters]")
ylabel("Height [meters]")
title("Engine Geometry")
fontsize(15,'points')
% Size margins and plot for screen
xmin = 0;
xmax = 3.6;
ymin = -0.45;
ymax = 0;
dx = xmax-xmin;
dy = ymax-ymin;
ar = dy/dx;
cushion = 0.05;
xlim([xmin - cushion*dx xmax + cushion*dx])
ylim([ymin - 0.1*dy ymax + cushion*dy])
screenx = 1920;
screeny = 1080;
fig.Position = [0 0.5*(screeny-screenx*ar) screenx screenx*ar];
end