├── LICENSE ├── PM_EQUATION.m ├── A_M_RELATION.m ├── README.md └── MoC_Nozzle_v2.m /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 jte0419 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /PM_EQUATION.m: -------------------------------------------------------------------------------- 1 | % SOLVE PRANDTL-MEYER EQUATION 2 | % Written by: JoshTheEngineer 3 | % YouTube : www.youtube.com/JoshTheEngineer 4 | % Website : www.joshtheengineer.com 5 | % Started: 12/07/15 6 | % Updated: 12/07/15 - Started code 7 | % - Works as intended 8 | % 11/??/17 - Updated to faster Mach number solver 9 | % 10 | % PURPOSE 11 | % Solves the Prandtl-Meyer equation for either the Mach number or 12 | % the P-M angle, depending on the inputs 13 | % 14 | % INPUTS 15 | % - v : Prandtl-Meyer angle [deg] 16 | % - M : Mach number [] 17 | % - g : Ratio of specific heats [] 18 | % 19 | % OUTPUTS 20 | % - sol : If M is input, then output is v [deg] 21 | % If v is input, then output is M [] 22 | 23 | function [sol] = PM_EQUATION(v,M,g) 24 | 25 | % For convenience 26 | gm1 = g-1; 27 | gp1 = g+1; 28 | 29 | % ---------------------- Solve for the P-M angle -------------------------- 30 | if (v == 0) 31 | term1 = sqrt(gp1/gm1); 32 | term2 = atand(sqrt(gm1*(M^2-1)/gp1)); 33 | term3 = atand(sqrt(M^2-1)); 34 | 35 | sol = term1*term2 - term3; 36 | end 37 | 38 | % --------------------- Solve for the Mach number ------------------------- 39 | if (M == 0) 40 | dM = 0.1; 41 | M = 1; 42 | res = 1; 43 | while (res > 0.01) 44 | M2 = M + dM; 45 | funv1 = (-v*(pi/180)+(sqrt(gp1/gm1)*... 46 | atan((sqrt(gm1*(M^2-1)/gp1)))-atan(sqrt(M^2-1)))); 47 | funv2 = (-v*(pi/180)+(sqrt(gp1/gm1)*... 48 | atan((sqrt(gm1*(M2^2-1)/gp1)))-atan(sqrt(M2^2-1)))); 49 | dv_dm = (funv2-funv1)/dM; 50 | 51 | M = M - funv1/dv_dm; 52 | res = abs(funv1); 53 | end 54 | sol = M; 55 | end 56 | -------------------------------------------------------------------------------- /A_M_RELATION.m: -------------------------------------------------------------------------------- 1 | function [sol] = A_M_RELATION(ARatio,M,g,SubSup) 2 | % ========================================================================= 3 | % - Solve for either the area ratio or Mach number 4 | % - Depends on what the user inputs are 5 | % ========================================================================= 6 | 7 | % Set initial value of solution so it doesn't error out 8 | sol = inf; 9 | 10 | % Get and set convenient variables 11 | gp1 = g + 1; 12 | gm1 = g - 1; 13 | gm1o2 = gm1/2; 14 | 15 | % Solve for area ratio or Mach number 16 | if (ARatio == 0) % SOLVE: AREA RATIO 17 | sol = sqrt((1/(M^2))*(((2/gp1)*(1+gm1o2*M^2)).^(gp1/gm1))); % Solve 18 | elseif (M == 0) % SOLVE: MACH NUMBER 19 | if (strcmpi(SubSup,'Sub')) % Subsonic value 20 | problem.objective = @(M) sqrt((1/(M^2))*(((2/gp1)*... 21 | (1+gm1o2*M^2))^(gp1/gm1))) - ARatio; % Objective function 22 | problem.x0 = [1e-6 1]; % Solver bounds 23 | problem.solver = 'fzero'; % Find the zero 24 | problem.options = optimset(@fzero); % Default options 25 | sol = fzero(problem); % Solve 26 | elseif (strcmpi(SubSup,'Sup')) % Supersonic value 27 | problem.objective = @(M) sqrt((1/(M^2))*(((2/gp1)*... 28 | (1+gm1o2*M^2))^(gp1/gm1))) - ARatio; % Objective function 29 | problem.x0 = [1 50]; % Solver Bounds 30 | problem.solver = 'fzero'; % Find the zero 31 | problem.options = optimset(@fzero); % Default options 32 | sol = fzero(problem); % Solve 33 | end 34 | end 35 | 36 | if (sol == inf) 37 | fprintf('Error in A_M_RELATION function\n\n'); 38 | end -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Rocket_Nozzle_Design 2 | 3 | This respository contains code to design a rocket nozzle using the method of characteristics (MoC). I will be making a GUI for this in the near future. For now, the user can change values in the *INPUTS* section in the main file. 4 | 5 | ## How To Run 6 | 7 | To run the code, you'll need to download [MoC_Nozzle.m](MoC_Nozzle.m), [PM_EQUATION.m](PM_EQUATION.m), and [A_M_RELATION.m](A_M_RELATION.m) into the same directory. Make any necessary changes in the MoC_Nozzle file, and then run it. 8 | 9 | I'm going to be making a full derivation video in the near future, along with a detailed guide on how to run the code and what variables you can change. CFD solutions will also be coming up shortly. 10 | 11 | Without going into too much depth, there are a few easy variables that can be changed at the moment. The first is the number of characteristics to use (*numChar*). The more characteristics, the more accurate your solution will be, but it will take longer to solve and plot. The ratio of specific heats (*g*) and specific gas constant (*R*) can also be changed. The most important change is switching between setting a desired Mach number (*Me_Set*) and a desired area ratio (*Ae_At_Set*). The one you don't want to use as an input needs to be set to zero! 12 | 13 | For example, the default input is to set the area ratio to a known value of 3. When we run the code, the derived exit Mach number from both the MoC and the quasi 1D analysis is 2.6374. Now we can check the code by going back to the inputs, and changing the input Mach number to 2.6374, running the code, and checking the output area ratio, which does indeed equal 3, as it should. 14 | 15 | ## Solutions 16 | 17 | Some solution times will be displayed in the command window, along with comparison to quasi-1D theory for A/A*. The program will also plot the nozzle outline, along with contours of Mach number. On the top half of the plotted nozzle, the characteristics mesh will also be plotted. 18 | 19 | At the very bottom of the code, there is a section titled *OUTPUT GEO MESH FILE FOR GMSH*. This will be used in a future YouTube video of mine, in which I will use the nozzle designed here as an input to a CFD program. In order to ouput the mesh file (for use in the open-source GMSH), you need to change the variable *outputMesh* to 1. It will generate a file named *Output_Mesh.geo* in the same directory that the code is in. 20 | -------------------------------------------------------------------------------- /MoC_Nozzle_v2.m: -------------------------------------------------------------------------------- 1 | % METHOD OF CHARACTERISTICS - NOZZLE DESIGN 2 | % Written by: JoshTheEngineer 3 | % YouTube : www.youtube.com/JoshTheEngineer 4 | % Website : www.joshtheengineer.com 5 | % Started: 06/24/16 6 | % Updated: 06/24/16 - Started code 7 | % - Works as expected, but no plotting yet 8 | % 06/25/16 - Plotting works now 9 | % 06/27/16 - Added comments 10 | % 11/18/17 - Updated inputs 11 | % - Fixed gamma issue 12 | % - Outputs GMSH file (still need to mesh and export) 13 | 14 | % PURPOSE 15 | % - Solve for the isentropic nozzle geometry from the givens 16 | % - Can either solve using defined exit Mach number or defined area ratio 17 | % 18 | % GIVENS 19 | % - g : Specific heat ratio [] 20 | % - Ae_At : Area ratio of nozzle [] 21 | % - Me : Exit Mach number [] 22 | 23 | clear; 24 | clc; 25 | 26 | %% INPUTS 27 | 28 | % Geometry 29 | Dstar = 2; % Diameter [length] 30 | geom = 1; % [1] Point, [2] Circle 31 | radius = 1; % Radius of throat [length] 32 | M1 = 1.0001; % Throat Mach number (> 1) [] 33 | numChar = 50; % Number of characteristic lines to use [#] 34 | g = 1.4; % Specific heat ratio [] 35 | R = 287; % Specific gas constant [J/kg*K] 36 | outputMesh = 0; % Output mesh [1 = Yes, 0 = No] 37 | 38 | % Set knowns 39 | Me_Set = 0; % Exit Mach number [] 40 | Ae_At_Set = 3; % Nozzle area ratio [] 41 | 42 | if (Me_Set == 0) 43 | Me_Set = A_M_RELATION(Ae_At_Set,0,g,'Sup'); 44 | elseif (Ae_At_Set == 0) 45 | Ae_At_Set = A_M_RELATION(0,Me_Set,g,'Sup'); 46 | end 47 | 48 | % Get anle array based on maximum throat turn angle 49 | thetaMax = PM_EQUATION(0,Me_Set,g)/2; % Maximum throat expansion angle [deg] 50 | theta = linspace(0,thetaMax,numChar)'; % Throat turn angle array [deg] 51 | 52 | % Nozzle specific properties 53 | gm1o2 = (g-1)/2; 54 | togp1 = 2/(g+1); 55 | gogm1 = g/(g-1); 56 | 57 | P0 = 7e6; 58 | T0 = 3558; 59 | Ps = P0*(togp1^gogm1); 60 | Ts = T0*(togp1); 61 | as = sqrt(g*R*Ts); 62 | Pe = P0/((1+gm1o2*Me_Set^2)^gogm1); 63 | Te = T0/(1+gm1o2*Me_Set^2); 64 | 65 | fprintf('Ae/At = %2.3f \n',Ae_At_Set); 66 | fprintf('Me = %2.3f \n',Me_Set); 67 | fprintf('P0 = %2.3f [Pa]\n',P0); 68 | fprintf('P* = %2.3f [Pa]\n',Ps); 69 | fprintf('Pe = %2.3f [Pa]\n',Pe); 70 | fprintf('T0 = %2.3f [K]\n',T0); 71 | fprintf('T* = %2.3f [K]\n',Ts); 72 | fprintf('Te = %2.3f [K]\n',Te); 73 | fprintf('a* = %2.3f [m/s]\n',as); 74 | 75 | %% INITIAL SETUP OF KNOWNS 76 | 77 | % Initialize solution variables 78 | dataENS = cell(numChar,numChar+1); % Expansion and non-simple regions 79 | dataS = cell(numChar,1); % Straightening region 80 | 81 | % ==================== 82 | % ==== EXPANSION ===== 83 | % ==================== 84 | for R = 1:1:numChar % Loop through all (-) characteristics 85 | dataENS{R,1}.M = 0; % Set all Mach numbers to zero 86 | if (R == 1) % For the first point 87 | dataENS{R,1}.M = M1; % Set the Mach number from the throat M 88 | end 89 | dataENS{R,1}.theta = theta(R); % Angle w.r.t horizontal [deg] 90 | dataENS{R,1}.nu = 0; % Prandtl-Meyer angle [deg] 91 | dataENS{R,1}.mu = 0; % Mach angle [deg] 92 | dataENS{R,1}.Kp = 0; % Plus characteristic constant [deg] 93 | dataENS{R,1}.Km = 0; % Minus characteristic constant [deg] 94 | dataENS{R,1}.X = 0; % Point X location 95 | dataENS{R,1}.Y = 0; % Point Y location 96 | dataENS{R,1}.dydx = 0; % Point slope 97 | dataENS{R,1}.tsip = 0; % Convenient avg parameter for (+) characteristic 98 | dataENS{R,1}.tsim = 0; % Convenient avg parameter for (-) characteristic 99 | 100 | % Apply geometry 101 | if (geom == 1) % Zero-radius corner (just a point) 102 | dataENS{R,1}.X = 0; % All X-values are at the throat 103 | dataENS{R,1}.Y = Dstar/2; % All Y-values are at half the throat diameter 104 | elseif (geom == 2) % Finite-radius corner 105 | dataENS{1,1}.X = 0; % First X-value is at the throat 106 | dataENS{1,1}.Y = Dstar/2; % First Y-value is at half the throat diameter 107 | 108 | for i = 2:1:numChar % For the rest of the starting line characteristics 109 | dx = radius*sind(theta(i)); % Change in X depends on radius and angle 110 | dy = radius - radius*cosd(theta(i)); % Change in Y depends on radius and angle 111 | 112 | dataENS{i,1}.X = dataENS{1,1}.X + dx; % Add the X change to the first X point 113 | dataENS{i,1}.Y = dataENS{1,1}.Y + dy; % Add the Y change to the first Y point 114 | end 115 | end 116 | end 117 | 118 | % ===================== 119 | % ==== NON-SIMPLE ===== 120 | % ===================== 121 | for R = 1:1:numChar % Loop over all (-) characteristics 122 | for L = 2:1:numChar+1 % Loop over all (+) characteristics 123 | dataENS{R,L}.M = 0; % Mach number 124 | dataENS{R,L}.theta = 0; % Flow angle w.r.t horizontal [deg] 125 | dataENS{R,L}.nu = 0; % Prandtl-Meyer angle [deg] 126 | dataENS{R,L}.mu = 0; % Mach angle [deg] 127 | dataENS{R,L}.Kp = 0; % Plus characteristic constant [deg] 128 | dataENS{R,L}.Km = 0; % Minus characteristic constant [deg] 129 | dataENS{R,L}.X = 0; % Point X location 130 | dataENS{R,L}.Y = 0; % Point Y location 131 | dataENS{R,L}.dydx = 0; % Point slope 132 | dataENS{R,L}.tsip = 0; % Convenient avg parameter for (+) characteristic 133 | dataENS{R,L}.tsim = 0; % Convenient avg parameter for (-) characteristic 134 | end 135 | end 136 | 137 | % ======================== 138 | % ==== STRAIGHTENING ===== 139 | % ======================== 140 | for L = 1:1:numChar % Loop through all (+) characteristics 141 | dataS{L}.M = 0; % Mach number [] 142 | dataS{L}.theta = 0; % Flow angle w.r.t. horizontal[deg] 143 | dataS{L}.nu = 0; % Prandtl-Meyer angle [deg] 144 | dataS{L}.mu = 0; % Mach angle [deg] 145 | dataS{L}.Kp = 0; % Plus characteristic constant [deg] 146 | dataS{L}.Km = 0; % Minus characteristic constant [deg] 147 | dataS{L}.X = 0; % Point X location 148 | dataS{L}.Y = 0; % Point Y location 149 | dataS{L}.dydx = 0; % Point slope 150 | dataS{L}.tsi = 0; % Convenient nozzle contour parameter 151 | dataS{L}.tsip = 0; % Convenient avg parameter for (+) characteristic 152 | dataS{L}.tsim = 0; % Convenient avg parameter for (-) characteristic 153 | end 154 | 155 | %% EXPANSION REGION 156 | 157 | % Indicate that we are solving expansion region, start section timer 158 | fprintf('Solving expansion region: '); % Print to the command window 159 | tic; % Start the timer 160 | 161 | % Starting point Prandtl-Meyer angle [deg] 162 | dataENS{1,1}.nu = PM_EQUATION(0,dataENS{1,1}.M,g); % PM angle from the throat Mach number [deg] 163 | 164 | % Loop through all characteristics (Rows) 165 | for R = 1:1:numChar % Loop over all the (-) characteristics 166 | % All throat K+ characteristic constants 167 | dataENS{R,1}.Kp = dataENS{1,1}.theta - dataENS{1,1}.nu; % All the same - coming from throat [deg] 168 | 169 | % PM angles, Mach numbers, Mach angles, and K- characterstic constants 170 | if (R ~= 1) % If we are not on the first characteristic (values already defined) 171 | dataENS{R,1}.nu = dataENS{R,1}.theta - dataENS{R,1}.Kp; % Prandtl-Meyer angle (don't need first characteristic) 172 | dataENS{R,1}.M = PM_EQUATION(dataENS{R,1}.nu,0,g); % Mach number (don't need first characteristic) 173 | end 174 | dataENS{R,1}.mu = asind(1/dataENS{R,1}.M); % Mach angle [deg] 175 | dataENS{R,1}.Km = dataENS{R,1}.theta + dataENS{R,1}.nu; % Minus characteristic constant [deg] 176 | end 177 | 178 | % Print the time taken to solve the expansion region 179 | fprintf('%2.2f [sec]\n',toc); % Print the time 180 | 181 | %% NON-SIMPLE REGION 182 | 183 | % Indicate that we are solving non-simple region, start section timer 184 | fprintf('Solving non-simple region: '); % Print to the command window 185 | tic; % Start the timer 186 | 187 | startR = 1; % Index for starting value of (-) characteristic 188 | for L = 2:1:numChar+1 % Loop through all (+) characteristics 189 | for R = startR:1:numChar % Loop through appropriate (-) characteristics 190 | 191 | if (L-1 == R) % If we are on the first (+) characteristic 192 | dataENS{R,L}.nu = dataENS{R,1}.Km; % Prandtl-Meyer angle from (-) constant [deg] 193 | dataENS{R,L}.M = PM_EQUATION(dataENS{R,L}.nu,0,g); % Mach number from PM equation using nu as input [] 194 | dataENS{R,L}.mu = asind(1/dataENS{R,L}.M); % Mach angle [deg] 195 | dataENS{R,L}.Kp = dataENS{R,L}.theta - dataENS{R,L}.nu; % Plus characteristic constant [deg] 196 | dataENS{R,L}.Km = dataENS{R,L}.theta + dataENS{R,L}.nu; % Minus characteristic constant [deg] 197 | else % For all other (+) characteristics 198 | dataENS{R,L}.Kp = dataENS{R-1,L}.Kp; % Plus characteristic constant [deg] 199 | dataENS{R,L}.Km = dataENS{R,L-1}.Km; % Minus characteristic constant [deg] 200 | dataENS{R,L}.theta = 0.5*(dataENS{R,L}.Km + dataENS{R,L}.Kp); % Angle w.r.t. horizontal [deg] 201 | dataENS{R,L}.nu = 0.5*(dataENS{R,L}.Km - dataENS{R,L}.Kp); % Prandtl-Meyer angle [deg] 202 | dataENS{R,L}.M = PM_EQUATION(dataENS{R,L}.nu,0,g); % Mach number [] 203 | dataENS{R,L}.mu = asind(1/dataENS{R,L}.M); % Mach angle [deg] 204 | end 205 | end 206 | 207 | startR = startR + 1; % Increment the starting (-) characteristic counter 208 | end 209 | 210 | % Print the time taken to solve the non-simple region 211 | fprintf('%2.2f [sec]\n',toc); % Print the time 212 | 213 | %% STRAIGHTENING REGION 214 | 215 | % Indicate that we are solving straightening region, start section timer 216 | fprintf('Solving straightening region: '); % Print to the command window 217 | tic; % Start the timer 218 | 219 | % Solve for the fully straightened variables 220 | dataS{end}.theta = 0; % Flow is back to horizontal [deg] 221 | dataS{end}.M = dataENS{end,end}.M; % Exit Mach number [] 222 | dataS{end}.nu = PM_EQUATION(0,dataENS{end}.M,g); % Exit Prandtl-Meyer angle [deg] 223 | dataS{end}.mu = asind(1/dataS{end}.M); % Exit Mach angle [deg] 224 | dataS{end}.Kp = dataS{end}.theta - dataS{end}.nu; % Exit plus characteristic constant [deg] 225 | dataS{end}.Km = dataS{end}.theta + dataS{end}.nu; % Exit minus characteristic constant [deg] 226 | 227 | % Using known exit values, solve for the rest of the straightening region 228 | for L = 1:1:numChar-1 229 | dataS{L}.Km = dataS{end}.Km; % Minus characteristic constant [deg] 230 | dataS{L}.Kp = dataENS{L,L+1}.Kp; % Plus characteristic constant [deg] 231 | dataS{L}.theta = 0.5*(dataS{L}.Km + dataS{L}.Kp); % Flow angle w.r.t horizontal [deg] 232 | dataS{L}.nu = 0.5*(dataS{L}.Km - dataS{L}.Kp); % Prandtl-Meyer angle [deg] 233 | dataS{L}.M = PM_EQUATION(dataS{L}.nu,0,g); % Mach number [] 234 | dataS{L}.mu = asind(1/dataS{L}.M); % Mach angle [deg] 235 | end 236 | 237 | % Print the time taken to solve the straightening region 238 | fprintf('%2.2f [sec]\n',toc); % Print the time 239 | 240 | %% FIND SHAPE OF NOZZLE [X and Y] 241 | 242 | % Indicate that we are finding the nozzle shape, start section timer 243 | fprintf('Solving for nozzle shape: '); % Print to the command window 244 | tic; % Start the timer 245 | 246 | % ========================================== 247 | % ==== Expansion and Non-Simple Regions ==== 248 | % ========================================== 249 | startR = 1; 250 | for L = 2:1:numChar+1 251 | for R = startR:1:numChar 252 | if (L-1 == R) 253 | dataENS{R,L}.tsim = tand(0.5*((dataENS{R,L-1}.theta-dataENS{R,L-1}.mu)+... 254 | (dataENS{R,L}.theta-dataENS{R,L}.mu))); 255 | dataENS{R,L}.X = dataENS{R,L-1}.X - (dataENS{R,L-1}.Y/dataENS{R,L}.tsim); 256 | else 257 | dataENS{R,L}.tsim = tand(0.5*((dataENS{R,L-1}.theta-dataENS{R,L-1}.mu)+... 258 | (dataENS{R,L}.theta-dataENS{R,L}.mu))); 259 | 260 | dataENS{R,L}.tsip = tand(0.5*((dataENS{R-1,L}.theta+dataENS{R-1,L}.mu)+... 261 | (dataENS{R,L}.theta+dataENS{R,L}.mu))); 262 | 263 | num = dataENS{R-1,L}.Y - dataENS{R,L-1}.Y + ... 264 | (dataENS{R,L}.tsim*dataENS{R,L-1}.X) - ... 265 | (dataENS{R,L}.tsip*dataENS{R-1,L}.X); 266 | den = dataENS{R,L}.tsim - dataENS{R,L}.tsip; 267 | 268 | dataENS{R,L}.X = num/den; 269 | dataENS{R,L}.Y = dataENS{R,L-1}.Y + ... 270 | dataENS{R,L}.tsim*(dataENS{R,L}.X-dataENS{R,L-1}.X); 271 | end 272 | end 273 | startR = startR + 1; 274 | end 275 | 276 | % ============================== 277 | % ==== Straightening Region ==== 278 | % ============================== 279 | for L = 1:1:numChar 280 | if (L == 1) 281 | dataS{L}.tsi = tand(0.5*(dataS{L}.theta + dataENS{numChar,1}.theta)); 282 | dataS{L}.tsip = tand(dataENS{numChar,L+1}.theta + dataENS{numChar,L+1}.mu); 283 | 284 | num = (dataS{L}.tsi*dataENS{numChar,L}.X) - ... 285 | (dataS{L}.tsip*dataENS{numChar,L+1}.X) - ... 286 | (dataENS{numChar,L}.Y) + ... 287 | (dataENS{numChar,L+1}.Y); 288 | den = dataS{L}.tsi - dataS{L}.tsip; 289 | 290 | dataS{L}.X = num/den; 291 | dataS{L}.Y = dataENS{numChar,L}.Y + ... 292 | (dataS{L}.tsi*dataS{L}.X) - ... 293 | (dataS{L}.tsi*dataENS{numChar,L}.X); 294 | else 295 | dataS{L}.tsi = tand(0.5*(dataS{L-1}.theta + dataS{L}.theta)); 296 | dataS{L}.tsip = tand(dataENS{numChar,L+1}.theta + dataENS{numChar,L+1}.mu); 297 | 298 | num = (dataS{L}.tsi*dataS{L-1}.X) - ... 299 | (dataS{L}.tsip*dataENS{numChar,L+1}.X) - ... 300 | (dataS{L-1}.Y) + ... 301 | (dataENS{numChar,L+1}.Y); 302 | den = dataS{L}.tsi - dataS{L}.tsip; 303 | 304 | dataS{L}.X = num/den; 305 | dataS{L}.Y = dataS{L-1}.Y + dataS{L}.tsi*(dataS{L}.X - dataS{L-1}.X); 306 | end 307 | end 308 | 309 | % Print the time taken to solve the nozzle shape 310 | fprintf('%2.2f [sec]\n',toc); % Print the time 311 | 312 | %% DISPLAY SOME RESULTS 313 | 314 | M_exit = dataS{numChar}.M; 315 | MoC_A_Astar = (dataS{numChar}.Y)/(Dstar/2); 316 | A_Astar = A_M_RELATION(0,M_exit,g,'Sup'); 317 | 318 | fprintf('============ RESULTS ==============\n'); 319 | fprintf('Me [MoC] : %1.5f\n',M_exit); 320 | fprintf('Me [Q1D] : %1.5f\n',Me_Set); 321 | fprintf('A/A* [MoC] : %1.5f\n',MoC_A_Astar); 322 | fprintf('A/A* [Q1D] : %1.5f\n',Ae_At_Set); 323 | 324 | %% PLOT PATCHES OF MACH NUMBER 325 | 326 | % Indicate that we are plotting the nozzle 327 | fprintf('Plotting...\n'); % Print to the command window 328 | 329 | % Set up the figure 330 | figure(1); % Select the appropriate figure 331 | cla; hold on; grid on; % Get ready for plotting 332 | c = colorbar; % Turn on the colorbar object 333 | % axis('equal'); 334 | xlim('auto'); % Auto-limit the X-axis 335 | ylim('auto'); % Auto-limit the Y-axis 336 | 337 | % Starting Constant Section 338 | % - Where the input Mach number is M1 339 | % - Have not reached the first characteristic 340 | ss(1,1) = 0; % \ 341 | ss(1,2) = Dstar/2; % |-> 342 | ss(1,3) = M1; % / 343 | ss(2,1) = ss(1,1); % \ 344 | ss(2,2) = -ss(1,2); % |-> 345 | ss(2,3) = ss(1,3); % / 346 | ss(3,1) = dataENS{1,2}.X; % \ 347 | ss(3,2) = dataENS{1,2}.Y; % |-> 348 | ss(3,3) = M1; % / 349 | 350 | % Ending Constant Section 351 | % - Where the output Mach number is Mexit 352 | % - Have reached horizontal, fully expanded flow 353 | se(1,1) = dataENS{numChar,numChar+1}.X; % \ 354 | se(1,2) = dataENS{numChar,numChar+1}.Y; % |-> 355 | se(1,3) = dataENS{numChar,numChar+1}.M; % / 356 | se(2,1) = dataS{numChar}.X; % \ 357 | se(2,2) = 0; % |-> 358 | se(2,3) = dataS{numChar}.M; % / 359 | se(3,1) = dataS{numChar}.X; % \ 360 | se(3,2) = dataS{numChar}.Y; % |-> 361 | se(3,3) = dataS{numChar}.M; % / 362 | 363 | % Plot the starting and ending uniform flow patches 364 | patch(se(:,1),se(:,2),se(:,3),'EdgeColor','none','FaceColor','interp'); % Plot the upper starting patch 365 | patch(se(:,1),-se(:,2),se(:,3),'EdgeColor','none','FaceColor','interp'); % Plot the lower starting patch 366 | patch(ss(:,1),ss(:,2),ss(:,3),'EdgeColor','none','FaceColor','interp'); % Plot the upper ending patch 367 | patch(ss(:,1),-ss(:,2),ss(:,3),'EdgeColor','none','FaceColor','interp'); % Plot the lower ending patch 368 | 369 | % Expansion and non-simple region patch plotting 370 | sol = []; % Initialize the solution array 371 | for R = 1:1:numChar-1 372 | for L = 1:1:numChar 373 | if (R == L-1) % If we are on the nozzle edge 374 | sol(1,1) = dataENS{R,L}.X; 375 | sol(1,2) = dataENS{R,L}.Y; 376 | sol(1,3) = dataENS{R,L}.M; 377 | sol(2,1) = dataENS{R+1,L}.X; 378 | sol(2,2) = dataENS{R+1,L}.Y; 379 | sol(2,3) = dataENS{R+1,L}.M; 380 | sol(3,1) = dataENS{R+1,L+1}.X; 381 | sol(3,2) = dataENS{R+1,L+1}.Y; 382 | sol(3,3) = dataENS{R+1,L+1}.M; 383 | else % If we are not on the nozzle edge 384 | sol(1,1) = dataENS{R,L}.X; 385 | sol(1,2) = dataENS{R,L}.Y; 386 | sol(1,3) = dataENS{R,L}.M; 387 | sol(2,1) = dataENS{R+1,L}.X; 388 | sol(2,2) = dataENS{R+1,L}.Y; 389 | sol(2,3) = dataENS{R+1,L}.M; 390 | sol(3,1) = dataENS{R+1,L+1}.X; 391 | sol(3,2) = dataENS{R+1,L+1}.Y; 392 | sol(3,3) = dataENS{R+1,L+1}.M; 393 | sol(4,1) = dataENS{R,L+1}.X; 394 | sol(4,2) = dataENS{R,L+1}.Y; 395 | sol(4,3) = dataENS{R,L+1}.M; 396 | end 397 | patch(sol(:,1),sol(:,2),sol(:,3),'EdgeColor','none',... 398 | 'FaceColor','interp'); 399 | patch(sol(:,1),-sol(:,2),sol(:,3),'EdgeColor','none',... 400 | 'FaceColor','interp'); 401 | end 402 | end 403 | 404 | % Straightening region patch plotting 405 | sol = []; 406 | for L = 1:1:numChar 407 | if (L == 1) 408 | sol(1,1) = dataENS{numChar,L}.X; 409 | sol(1,2) = dataENS{numChar,L}.Y; 410 | sol(1,3) = dataENS{numChar,L}.M; 411 | sol(2,1) = dataS{L}.X; 412 | sol(2,2) = dataS{L}.Y; 413 | sol(2,3) = dataS{L}.M; 414 | sol(3,1) = dataENS{numChar,L+1}.X; 415 | sol(3,2) = dataENS{numChar,L+1}.Y; 416 | sol(3,3) = dataENS{numChar,L+1}.M; 417 | else 418 | sol(1,1) = dataENS{numChar,L}.X; 419 | sol(1,2) = dataENS{numChar,L}.Y; 420 | sol(1,3) = dataENS{numChar,L}.M; 421 | sol(2,1) = dataS{L-1}.X; 422 | sol(2,2) = dataS{L-1}.Y; 423 | sol(2,3) = dataS{L-1}.M; 424 | sol(3,1) = dataS{L}.X; 425 | sol(3,2) = dataS{L}.Y; 426 | sol(3,3) = dataS{L}.M; 427 | sol(4,1) = dataENS{numChar,L+1}.X; 428 | sol(4,2) = dataENS{numChar,L+1}.Y; 429 | sol(4,3) = dataENS{numChar,L+1}.M; 430 | end 431 | patch(sol(:,1),sol(:,2),sol(:,3),'EdgeColor','none',... 432 | 'FaceColor','interp'); 433 | patch(sol(:,1),-sol(:,2),sol(:,3),'EdgeColor','none',... 434 | 'FaceColor','interp'); 435 | end 436 | 437 | %% PLOT THE NOZZLE AND CHARACTERISTICS 438 | 439 | % Maximum Mach number so we can set Z-axis level correctly 440 | maxM = dataS{numChar}.M; 441 | 442 | % =============================== 443 | % ==== NOZZLE BOUNDING LINES ==== 444 | % =============================== 445 | 446 | xNoz = [dataENS{1,1}.X]; 447 | yNoz = [dataENS{1,1}.Y]; 448 | 449 | % Nozzle bounding lines in expansion section 450 | for R = 1:1:numChar-1 451 | xNoz = [xNoz; dataENS{R+1,1}.X]; 452 | yNoz = [yNoz; dataENS{R+1,1}.Y]; 453 | plot([dataENS{R,1}.X; dataENS{R+1,1}.X],... % Top surface 454 | [dataENS{R,1}.Y; dataENS{R+1,1}.Y],... 455 | 'k-','LineWidth',3); 456 | plot([dataENS{R,1}.X; dataENS{R+1,1}.X],... % Bottom surface 457 | [-dataENS{R,1}.Y; -dataENS{R+1,1}.Y],... 458 | 'k-','LineWidth',3); 459 | end 460 | 461 | % Nozzle bounding line between expansion and straightening sections 462 | plot([dataENS{numChar,1}.X; dataS{1}.X],... % Top surface 463 | [dataENS{numChar,1}.Y; dataS{1}.Y],'k-','LineWidth',3); 464 | plot([dataENS{numChar,1}.X; dataS{1}.X],... % Bottom surface 465 | [-dataENS{numChar,1}.Y; -dataS{1}.Y],'k-','LineWidth',3); 466 | 467 | % Nozzle bounding lines in straightening section 468 | xNoz = [xNoz; dataS{1}.X]; 469 | yNoz = [yNoz; dataS{1}.Y]; 470 | for L = 2:1:numChar 471 | xNoz = [xNoz; dataS{L}.X]; 472 | yNoz = [yNoz; dataS{L}.Y]; 473 | plot([dataS{L-1}.X; dataS{L}.X],... % Top surface 474 | [dataS{L-1}.Y; dataS{L}.Y],'k-','LineWidth',3); 475 | plot([dataS{L-1}.X; dataS{L}.X],... % Bottom surface 476 | [-dataS{L-1}.Y; -dataS{L}.Y],'k-','LineWidth',3); 477 | end 478 | 479 | % ============================== 480 | % ==== CHARACTERISTIC LINES ==== 481 | % ============================== 482 | 483 | % Non-simple region C- characteristics 484 | for R = 1:1:numChar 485 | Xvals = []; 486 | Yvals = []; 487 | for L = 1:1:R+1 488 | Xvals = [Xvals; dataENS{R,L}.X]; 489 | Yvals = [Yvals; dataENS{R,L}.Y]; 490 | end 491 | plot(Xvals,Yvals,'k-','LineWidth',2); 492 | end 493 | 494 | % Non-simple region C+ characteristics 495 | for L = 2:1:numChar 496 | Xvals = []; 497 | Yvals = []; 498 | for R = L-1:1:numChar 499 | Xvals = [Xvals; dataENS{R,L}.X]; 500 | Yvals = [Yvals; dataENS{R,L}.Y]; 501 | end 502 | plot(Xvals,Yvals,'k-','LineWidth',2); 503 | end 504 | 505 | % Expansion region C+ characteristics 506 | for L = 2:1:numChar+1 507 | Xvals = [dataENS{numChar,L}.X; dataS{L-1}.X]; 508 | Yvals = [dataENS{numChar,L}.Y; dataS{L-1}.Y]; 509 | plot(Xvals,Yvals,'k-','LineWidth',2); 510 | end 511 | 512 | % ========================== 513 | % ==== EXTRANEOUS STUFF ==== 514 | % ========================== 515 | 516 | % Nozzle centerline 517 | plot([0; dataS{numChar}.X],[0; 0],'k-','LineWidth',3); 518 | 519 | % Plot white dots (black outline) for intersections in non-simple region 520 | for L = 2:1:numChar+1 521 | for R = 1:1:numChar 522 | plot(dataENS{R,L}.X,dataENS{R,L}.Y,'o',... 523 | 'MarkerFaceColor','w','MarkerEdgeColor','k'); 524 | end 525 | end 526 | 527 | % PLOT JUST THE OUTSIDE CONTOUR 528 | plot(xNoz,yNoz,'o','MarkerFaceColor','g','MarkerEdgeColor','k'); 529 | 530 | % Colorbar properties 531 | caxis([M1 dataS{numChar}.M]); 532 | ylabel(c,'Mach Number','FontSize',24); 533 | set(c,'YTick',[M1 get(c,'YTick') dataS{numChar}.M]); 534 | 535 | fprintf('SOLUTION FINISHED!\n'); 536 | 537 | %% LOAD CFD CSV FILE AND COMPARE 538 | 539 | % flpth = 'C:\Users\Josh\Documents\MATLAB\Nozzle_Design\'; 540 | % flnm = 'CSV_Data.csv'; 541 | % fid = fopen([flpth flnm]); 542 | % 543 | % dataCFD = textscan(fid,'%f %f %f %f %f %f %f %f %f %f %f %f %f',... 544 | % 'HeaderLines',1,'CollectOutput',1,'Delimiter',','); 545 | % 546 | % % Get Mach number from symmetry line MoC 547 | % ind = 1; 548 | % for i = 1:1:numChar 549 | % for j = 1:1:numChar 550 | % if (i == j+1) 551 | % dataX(ind) = dataENS{i,j}.X; 552 | % dataM(ind) = dataENS{i,j}.M; 553 | % ind = ind + 1; 554 | % end 555 | % end 556 | % end 557 | % 558 | % figure(30); 559 | % cla; hold on; grid on; 560 | % plot(dataX,dataM,'k-','LineWidth',2); 561 | % plot(dataCFD{1,1}(:,11),dataCFD{1,1}(:,8),'ro'); 562 | 563 | %% OUTPUT GEO MESH FILE FOR GMSH 564 | 565 | if (outputMesh == 1) 566 | 567 | % Need to write the following 568 | % - Point array 569 | % - Line array 570 | % - Line Loop 571 | % - Plane Surface 572 | % - Physical Line (Inlet, Outlet, Symmetry, Nozzle) 573 | 574 | % Nozzle boundary points 575 | % - Get rid of repeated values (from point-corner for example) 576 | xNoz = unique(xNoz); % Nozzle boundary X-points 577 | yNoz = unique(yNoz); % Nozzle boundary Y-points 578 | 579 | % Number of nozzle points and lines 580 | numNozPts = length(xNoz); % Number of nozzle points 581 | numNozLns = length(xNoz)-1; % Number of nozzle lines 582 | 583 | % Clear any old variables 584 | clearvars Point; 585 | 586 | % ===== FILE PROPERTIES ===== 587 | fid = fopen('Output_Mesh.geo','w'); 588 | 589 | % ===== POINTS ===== 590 | 591 | % Nozzle bounday 592 | Point(:,1) = xNoz; 593 | Point(:,2) = yNoz; 594 | 595 | % Additional points 596 | addPts = [xNoz(end) 0; 597 | 0 0; 598 | xNoz(1) yNoz(1)]; 599 | Point = [Point; addPts]; 600 | 601 | Point(:,3) = 0.0; 602 | Point(:,4) = 1.0; 603 | 604 | % Number of points 605 | numPts = size(Point,1); 606 | 607 | % Write points to a file 608 | for i = 1:1:numPts 609 | fprintf(fid,'Point(%i) = {%g, %g, %g, %g};\r\n',... 610 | i,Point(i,1),Point(i,2),Point(i,3),Point(i,4)); 611 | end 612 | 613 | % ===== LINES ===== 614 | 615 | % Number of lines 616 | numLns = numPts; 617 | 618 | % Write lines to file 619 | for i = 1:1:numPts 620 | if (i ~= numLns) 621 | fprintf(fid,'Line(%i) = {%i, %i};\r\n',i,i,i+1); 622 | else 623 | fprintf(fid,'Line(%i) = {%i, %i};\r\n',i,i,1); 624 | end 625 | end 626 | 627 | % ===== LINE LOOP ===== 628 | 629 | % Create string with all lines 630 | lineStr = '1'; 631 | for i = 2:1:numLns 632 | lineStr = [lineStr ', ' num2str(i)]; 633 | end 634 | 635 | % Write to file 636 | fprintf(fid,'Line Loop(%i) = {%s};\r\n',numPts+1,lineStr); 637 | 638 | % ===== PLANE SURFACE ===== 639 | 640 | fprintf(fid,'Plane Surface(%i) = {%i};\r\n',numPts+2,numPts+1); 641 | 642 | % ===== PHYSICAL LINE ===== 643 | 644 | phyLineNozzle = '1'; 645 | for i = 2:1:numNozLns 646 | phyLineNozzle = [phyLineNozzle ', ' num2str(i)]; 647 | end 648 | 649 | fprintf(fid,'Physical Line("Outlet") = {%i};\r\n',numLns-3); 650 | fprintf(fid,'Physical Line("Symmetry") = {%i};\r\n',numLns-2); 651 | fprintf(fid,'Physical Line("Inlet") = {%i};\r\n',numLns-1); 652 | fprintf(fid,'Physical Line("Nozzle") = {%s};\r\n',phyLineNozzle); 653 | 654 | % ===== CHARACTERISTIC LENGTH ===== 655 | 656 | charVal = 0.2; 657 | charLength = '1'; 658 | for i = 2:1:numPts 659 | charLength = [charLength ', ' num2str(i)]; 660 | end 661 | 662 | fprintf(fid,'Characteristic Length {%s} = %f;\n',charLength,charVal); 663 | 664 | end 665 | 666 | fclose('all'); 667 | 668 | 669 | 670 | 671 | 672 | 673 | --------------------------------------------------------------------------------