Challenge

Week 7 - Simulation of a 1D Super-sonic nozzle flow simulation using Macormack Method

Aim: Simulation of a 1D Super-sonic nozzle flow simulation using Macormack Method.

Objective

  • simulate the isentropic flow through a quasi 1D subsonic-supersonic nozzle using both the conservative and non-conservative form of the fluid flow equations

The following plots are to be shown in report

  1. Steady-state distribution of primitive variables inside the nozzle
    2. Time-wise variation of the primitive variables
    3. Variation of Mass flow rate distribution inside the nozzle at different time steps during the time-marching process
    4. Comparison of normalized mass flow rate distributions of both forms

Expected Results:

Perform a grid independence test on the solution and explain all your findings.

The code must be modular. You need to write separate functions to solve both forms.

Discuss all the plots you are providing with observed inferences.

Note:

Refer CFD book by John Anderson for Initial and boundary conditions.

Description/Analysis

We consider the steady isentropic flow through a convergent-divergent nozzle as shown below. The flow at the inlet to the nozzle comes from a reservoir. The flow expands isentropically to supersonic speeds at the nozzle exit. The flow is locally subsonic at the convergent section of the nozzle, sonic at the throat and supersonic at the divergent section.

We assume 1d inviscid flow through the nozzle. Hence the resulting fluid flow equations to be converted to their algebraic form for the numerical solution of this problem are show below (reference pg 78 Section 2.8 Anderson).

Solutions to these equations are obtained using McCormack’s method which is suitable for inviscid flow problems. It is a 2nd order accurate explicit difference scheme in both the spatial and temporal components of the above fluid flow equations.

Initial Conditions/Boundary Conditions

Initial conditions should be chosen such that their values are close to that of the steady state solution. This would guarantee a faster time marching solution. Large deviations can lead to large gradients during the time marching procedure and hence make the solution go unstable.

For boundary conditions at the inflow side using the method of characteristics, we need to specify 2 flow field variables, density and temperature, as fixed with a third variable V for velocity as floating.

For boundary conditions at the outflow side due to supersonic conditions, all variables must float because the left and right running characteristics are both pointing out of the exit boundary and so all variables cannot be fixed.

Results/Discussion

Steady State Solution Comparison

The following plot shows a comparison of the conservative and non-conservative form of the fluid flow equations to predict the steady state primitive variables along the length of the nozzle.

Both fluid flow forms are generating the same results along the nozzle and this is to be expected as the choice of conservative or non-conservative should not matter for the case of isentropic subsonic-supersonic flow through a nozzle. Only in the case of shock capturing does the choice of which fluid flow form matters which is outside the scope of this challenge.

Time-wise variation of flow variables.

The following plot show the time-wise variation of the primitive variables for the 2 forms with steady values achieved approximately after 600 time steps.

The following plot shows the variation of mass flow rate along the nozzle at different time steps for the 2 forms.

For the non-conservative form we can see how at earlier time steps from nt=50-150 that the mass flow rate is quite unsteady. This is due to transient variations of the flow fields. However after 200 steps we see the mass flow rate stabilizing to a constant along the length of the nozzle which confirms our knowledge that the mass flow rate being the product of density*velocity *cross sectional area is a constant.

In the above plot, for the conservative form we see a milder variations of the mass flow rate due to the assumed initial condition. This is shown more clearly below for a time step of 1. Here the steady state mass flow distribution is much closer to the theoretical value (green) with little or no oscillation.

In the conservative form, the dependent variables (variables for which we directly calculate the numbers) are not the primitive variables. These primitive variables (rho, V, T and p) are decoded from the solutions vector (U1,U2,U3). This is also true for the mass flow rate. Ie, the mass flow itself is a dependent variable/primary result.

In contrast, the dependent variables in the non- conservative form are the primitive variables and hence the mass flow rate is obtained as a secondary result.

It is for those reasons outlined above that the conservative form calculates a more accurate mass flow rate along the nozzle length.

Grid Independence

The table below shows the values predicted by the program and the corresponding analytical value. It seems that grid independence is achieved for grid values 61 and 91 for the conservative case and also for the non-conservative case. However, there is no superiority of either form over the other. Both cases yield satisfactory results.

In terms of programming effort, the conservative form requires additional scripting due to the fact that the primitive variables need to be decoded from the solution vector.

M file

clear all
close all
clc
%grid points along nozzle of total length 3
N=31;
N_throat=((N-1)/2+1);
x=linspace(0,3,N);
dx=x(2)-x(1);
g=1.4;
%time step
nt=1400;
C=0.5;

%run m file. 10 plots will appear. Please give it some time to complete.
[nt]=conservative_form(N,x,dx,g,nt,C,N_throat);
[nt]=non_conservative_form(N,x,dx,g,nt,C,N_throat); 

Conservative function

function [nt]=conservative_form(N,x,dx,g,nt,C,N_throat);
%% 

%pre-allocation
dU1dtp=ones();
dU2dtp=ones();
dU3dtp=ones();
dU1dtc=ones();
dU2dtc=ones();
dU3dtc=ones();
rho_c=ones();
rho_c_throat=ones();
V_throat=ones();
T_throat=ones();
M_throat=ones();
J2=ones();
NN=N_throat;


nt_array=1:nt; %for plotting
%initial conditions @t=0 eqn7.120a
for u=1:length(x)
    if (x(u) >=0) && (x(u) <=0.5)
        rho_c(u)=1;
        T(u)=1;
    elseif (x(u)> 0.5) && (x(u)<=1.5)
        rho_c(u)=1-0.366*(x(u)-0.5);
        T(u)=1-0.167*(x(u)-0.5);
    elseif (x(u) >1.5) && (x(u)<=3.5)
        rho_c(u)=0.634-0.3879*(x(u)-1.5);
        T(u)=0.833-0.3507*(x(u)-1.5); 
    end
end
%Initial Conditions
A=1+2.2*(x-1.5).^2;%area
%0.59 is chosen to stabilize solution eqn7.121-122c
U1=rho_c.*A;
V=0.59./U1;
U2=rho_c.*A.*V;
U3=rho_c.*((T./(g-1))+(g.*V.^2./2)).*A;
%general flow field variables
% p=rho_c.*T;
% M=V./(T.^0.5);
% m_dot=rho_c.*A.*V;


%dt must be less than, or at best equal to,
%the time it takes a sound wave to move from one grid point to the next.
%% 
%outer loop
for k=1:nt
    
    % find the minimum timestep for each and every timestep
    for i=1:N
    u=T(i).^0.5;% speed of sound eqn7.75
    dt_min(i)=C*(dx/(u+V(i)));
    end
    dt=min(dt_min)
    
    % need a copy of data at the start and end of time step for 
    %comparison purposes.
    U1_old=U1;
    U2_old=U2;
    U3_old=U3;
    
    F1=U2;
    F2= (U2.^2./U1) + ((g-1)/g).*(U3-((g/2)*(U2.^2./U1)));
    F3= (g.*U3.*U2./U1)-(g*(g-1)/2).*(U2.^3./U1.^2);
    
    
    %predictor method on the spatial derevatives
    for j=2:N-1 % 2: doing calc on inner nodes
      
      
        %source term - non pure form
        J2(j)=(1/g).*(rho_c(j).*T(j)).*((A(j+1)-A(j))/dx);
        
        %predictor method on the spatial derevatives
        dU1dtp(j)=-(F1(j+1)-F1(j))/dx;%Continuity
        dU2dtp(j)=-(F2(j+1)-F2(j))/dx+J2(j);%Momentum
        dU3dtp(j)=-(F3(j+1)-F3(j))/dx;%Energy

        %solution update: THis is placed inside of this loop to ensure correct
        %matrix sizes
        U1(j)=U1_old(j)+dU1dtp(j)*dt;
        U2(j)=U2_old(j)+dU2dtp(j)*dt;
        U3(j)=U3_old(j)+dU3dtp(j)*dt;   
        
    end
    
    %corrector method on the spatial derevatives   
    for j=2:N-1 % 2: doing calc on inner nodes      
        
        J2(j)=(1/g)*(rho_c(j)*T(j))*((A(j)-A(j-1))/dx);%source term non pure
        dU1dtc(j)=-(F1(j)-F1(j-1))/dx;%Continuity
        dU2dtc(j)=-(F2(j)-F2(j-1))/dx +J2(j); %MOmentum
        dU3dtc(j)=-(F3(j)-F3(j-1))/dx;%Energy
        %update flux terms
        F1=U2;
        F2=U2.^2./U1+((g-1)./g)*(U3-((g./2)*(U2.^2./U1)));
        F3=g.*U3.*U2./U1-(g*(g-1)/2)*(U2.^3./U1.^2);
          
    end
    
    %compute average time deriviative
    dU1dt=0.5*(dU1dtp+dU1dtc);
    dU2dt=0.5*(dU2dtp+dU2dtc);
    dU3dt=0.5*(dU3dtp+dU3dtc);
    
    %final solution update after the predictor and corrector steps
    for j=2:N-1
        U1(j)=U1_old(j)+dU1dt(j)*dt;
        U2(j)=U2_old(j)+dU2dt(j)*dt;
        U3(j)=U3_old(j)+dU3dt(j)*dt;
    end
    
    % apply boundary conditions eqn 7.119a
    %Inlet
    U1(1)=A(1)*rho_c(1);      %u = a*rho_c. rho_c was missing
    U2(1)=2*U2(2)-U2(3);
    U3(1)=U1(1)*((T(1)/(g-1))+(g/2)*(U2(1)/U1(1)).^2);
    %Outlet
    U1(N)=2*U1(N-1)-U1(N-2);
    U2(N)=2*U2(N-1)-U2(N-2);
    U3(N)=2*U3(N-1)-U3(N-2);
    
    
    rho_c=U1./A;
    V=U2./U1;
    T=(g-1).*((U3./U1) - ((g/2).*V.^2));
    p=rho_c.*T;
    M=V./(T.^0.5);
    %mass flow rate also =U2
    m_dot=rho_c.*A.*V;
   
   %for grid indpendence check
   %flow field variables at the throat
   if NN==N_throat
       rho_c_throat(k)=rho_c(NN);
       V_throat(k)=V(NN);
       T_throat(k)=T(NN);
       M_throat(k)=V_throat/T_throat.^0.5;
       p_throat=rho_c_throat.*T_throat;
       m_dot_throat=rho_c_throat.*V_throat;
   end
   
     figure(1)
     movegui(figure(1),'northwest');
    if k==50
        plot(x,m_dot,'r')
        hold on
    elseif k==100
        plot(x,m_dot,'g')
        hold on
    elseif k==150
        plot(x,m_dot,'b')
        hold on
    elseif k==200
        plot(x,m_dot,'y')
        hold on
    elseif k==250
        plot(x,m_dot,'c')
        hold on
    elseif k==nt
        plot(x,m_dot,'k--o')
        hold on
    xlabel('length of nozzle')
    ylabel('mass flow rate ')
    legend('nt=50','nt=100','nt=150','nt=200','nt=250','nt=1400');
    title('Conservative Form of mass flow rate along the nozzle at different time steps');
    end
   
end
%% 


   figure(2)
   movegui(figure(2),'north');
   plot(x,rho_c,'r')
   hold on
   plot(x,V,'g')
   plot(x,T,'b')
   plot(x,M,'m')
   plot(x,p,'k')
   plot(x,m_dot,'c')
   xlabel('length of nozzle')
   ylabel('non-dimensional parameters')
   legend('Conservative Form density',...
          'Conservative Form Velocity',...
          'Conservative Form Temperature',...
          'Conservative Form Mach Number',...
          'Conservative Form pressure',...
          'Conservative Form mass flow rate');
   title('Conservative Form of primitive variables along the nozzle - Steady State'); 

   
   figure(3)
   movegui(figure(3),'northeast');
   plot(nt_array,rho_c_throat,'r')
   hold on
   plot(nt_array,V_throat,'g')
   plot(nt_array,T_throat,'b')
   plot(nt_array,M_throat,'m')
   plot(nt_array,p_throat,'k')
   plot(nt_array,m_dot_throat,'c')
   xlabel('number of time steps')
   ylabel('non-dimensional parameters')
   legend('Conservative Form density','Conservative Form Velocity','Conservative Form Temperature','Conservative Form Mach Number',...
          'Conservative Form pressure','Conservative Form mass flow rate')
   title(sprintf('Conservative Form Primitive/Flow field Variables - Time step =%d',k'));
   
   figure(7)
   plot(x,rho_c,'r')
   hold on
   plot(x,V,'g')
   plot(x,T,'b')
   plot(x,M,'m')
   plot(x,p,'k')
   plot(x,m_dot,'c')
   xlabel('length of nozzle')
   ylabel('non-dimensional parameters')
   legend('Conservative Form density',...
          'Conservative Form Velocity',...
          'Conservative Form Temperature',...
          'Conservative Form Mach Number',...
          'Conservative Form pressure',...
          'Conservative Form mass flow rate');
   title('Conservative Form of primitive variable along the nozzle - Steady State'); 

   
   figure(8)
   plot(nt_array,rho_c_throat,'r')
   hold on
   plot(nt_array,V_throat,'g')
   plot(nt_array,T_throat,'b')
   plot(nt_array,M_throat,'m')
   plot(nt_array,p_throat,'k')
   plot(nt_array,m_dot_throat,'c')
   xlabel('number of time steps')
   ylabel('non-dimensional parameters')
   legend('Conservative Form density','Conservative Form Velocity','Conservative Form Temperature','Conservative Form Mach Number',...
          'Conservative Form pressure','Conservative Form mass flow rate')
   title(sprintf('Conservative Form Primitive/Flow field Variables - Time step =%d',k'));
   
   figure(9)
   plot(x,m_dot,'r')
   hold on
   xlabel('length of nozzle')
   ylabel('non-dimensional parameters')
   legend('Conservative Form mass flow rate');
   title('Conservative Form of mass flow rate along the nozzle - Steady State'); 

   
   figure(10)
   plot(nt_array,m_dot_throat,'r')
   hold on 
   xlabel('number of time steps')
   ylabel('non-dimensional parameters')
   legend('Conservative Form mass flow rate')
   title(sprintf('Conservative Form mass flow rate - Time step =%d',k'));


end

NonConservative Function

function [nt]=non_conservative_form(N,x,dx,g,nt,C,N_throat);
%% 
nt_array=1:nt; %for plotting
%initial conditions
rho=1-0.3146*x;
T=1-0.2314*x; % T temperature
V=((0.1+1.09*x)).*T.^0.5; % .* for element wise calcs
a=1+2.2*(x-1.5).^2; %area
rho_exact=0.579*ones(1,N);
NN=N_throat;

%dt must be less than, or at best equal to,
%the time it takes a sound wave to move from one grid point to the next.
%% 


%outer loop
for k=1:nt
    
    % find the minimum timestep for each and every timestep
    for i=1:N
    u=T(i).^0.5;% speed of sound eqn7.75
    dt_min(i)=C*(dx/(u+V(i)));
    end
    dt=min(dt_min)
    % need a copy of data at the start and end of time step for 
    %comparison purposes.
    rho_old=rho;
    V_old=V;
    T_old=T;
    
    
    %predictor method
    for j=2:N-1 % 2: doing calc on inner nodes
        dVdx=(V(j+1)-V(j))/dx;
        drhodx=(rho(j+1)-rho(j))/dx;
        dTdx=(T(j+1)-T(j))/dx;
        dlog_adx=(log(a(j+1))-log(a(j)))/dx;
        
        %continuity eqn
        drhodt_p(j)=-rho(j)*dVdx-rho(j)*V(j)*dlog_adx-V(j)*drhodx;
        
        %Momentum Eqn
        dVdt_p(j)=-V(j)*dVdx-(1/g)*(dTdx+(T(j)/rho(j))*drhodx);
        
        %Energy Eqn
        dTdt_p(j)=-V(j)*dTdx-(g-1)*T(j)*(dVdx+V(j)*dlog_adx);
        
        %solution update: THis is placed inside of this loop to ensure correct
        %matrix sizes
        rho(j)=rho(j)+drhodt_p(j)*dt;
        V(j)=V(j)+dVdt_p(j)*dt;
        T(j)=T(j)+dTdt_p(j)*dt;
    end
    
    %corrector method
    for j=2:N-1 % 2: doing calc on inner nodes
        dVdx=(V(j)-V(j-1))/dx;
        drhodx=(rho(j)-rho(j-1))/dx;
        dTdx=(T(j)-T(j-1))/dx;
        dlog_adx=(log(a(j))-log(a(j-1)))/dx;
        
        %continuity eqn
        drhodt_c(j)=-rho(j)*dVdx-rho(j)*V(j)*dlog_adx-V(j)*drhodx;
        
        %Momentum Eqn
        dVdt_c(j)=-V(j)*dVdx-(1/g)*(dTdx+(T(j)/rho(j))*drhodx);
        
        %Energy Eqn
        dTdt_c(j)=-V(j)*dTdx-(g-1)*T(j)*(dVdx+V(j)*dlog_adx);
        
    end
    
    %compute average time deriviative
    drhodt=0.5*(drhodt_p+drhodt_c);
    dVdt=0.5*(dVdt_p+dVdt_c);
    dTdt=0.5*(dTdt_p+dTdt_c);
    
    %final solution update after the predictor and corrector steps
    for i=2:N-1
    rho(i)=rho_old(i)+drhodt(i)*dt;
    V(i)=V_old(i)+dVdt(i)*dt;
    T(i)=T_old(i)+dTdt(i)*dt;
    end
    
    % apply boundary conditions
    
    %inlet velocity f(N)
    V(1)=2*V(2)-V(3);
    
   %outlet f(N)
   V(N)=2*V(N-1)-V(N-2);
   rho(N)=2*rho(N-1)-rho(N-2);
   T(N)=2*T(N-1)-T(N-2);
   
    %for grid indpendence check
    if NN==N_throat
       %flow field variables at the throat
       rho_throat(k)=rho(NN);
       V_throat(k)=V(NN);
       T_throat(k)=T(NN);
       M_throat(k)=V_throat/T_throat.^0.5;
       p_throat=rho_throat.*T_throat;
       %area at throat =1
       m_dot_throat=rho_throat.*V_throat;
    end
   
   %general flow field variables
   p=rho.*T;
   M=V./(T.^0.5);
   m_dot=rho.*a.*V;
   
          figure(4)
    movegui(figure(4),'southwest');
    if k==50
        plot(x,m_dot,'r')
        hold on
    elseif k==100
        plot(x,m_dot,'g')
        hold on
    elseif k==150
        plot(x,m_dot,'b')
        hold on
    elseif k==200
        plot(x,m_dot,'y')
        hold on
    elseif k==250
        plot(x,m_dot,'c')
        hold on
    elseif k==nt
        plot(x,m_dot,'k--o')
        hold on
    xlabel('length of nozzle')
    ylabel('mass flow rate ')
    legend('nt=50','nt=100','nt=150','nt=200','nt=250','nt=1400');
    title('Non Conservative Form of mass flow rate along the nozzle at different time steps');
    end
   

   
end
%% 


   %spatial dependence of flow field variables.
   figure(5)
   movegui(figure(5),'south');
   plot(x,rho,'r')
   hold on
   plot(x,V,'g')
   plot(x,T,'b')
   plot(x,M,'m')
   plot(x,p,'k')
   plot(x,m_dot,'c')
   xlabel('length of nozzle')
   ylabel('non-dimensional parameters')
   legend('NonConservativeF density',...
          'NonConservativeF Velocity',...
          'NonConservativeF Temperature',...
          'NonConservativeF Mach Number',...
          'NonConservativeF pressure',...
          'NonConservativeF mass flow rate');
   title({' Non Conservative Form',...
          'Primitive variables along the nozzle - Steady State'});



   %time dependent variations of the flow fields variables at the throat
   figure(6)
   movegui(figure(6),'southeast');
   plot(nt_array,rho_throat,'r')
   hold on
   plot(nt_array,V_throat,'g')
   plot(nt_array,T_throat,'b')
   plot(nt_array,M_throat,'m')
   plot(nt_array,p_throat,'k')
   plot(nt_array,m_dot_throat,'c')
   xlabel('number of time steps')
   ylabel('non-dimensional parameters')
   legend('NonConservative Form Density',...
          'NonConservative Form Velocity',...
          'NonConservative Form Temperature',...
          'NonConservative Form Mach Number',...
          'NonConservative Form Pressure',...
          'NonConservative Form mass flow rate');
   title({'Non Conservative Form',...
          'Primitive variables at the throat as f(Time step) =%d',k'});
   
  

   figure(7)
   movegui(figure(7),'center');
   plot(x,rho,'+r')
   hold on
   plot(x,V,'+g')
   plot(x,T,'+b')
   plot(x,M,'+m')
   plot(x,p,'+k')
   plot(x,m_dot,'+c')
   xlabel('length of nozzle')
   ylabel('non-dimensional parameters')
   legend('ConservativeF density(NonConservativeF +)',...
          'ConservativeF Velocity(NonConservativeF +)',...
          'ConservativeF Temperature(NonConservativeF +)',...
          'ConservativeF Mach Number(NonConservativeF +)',...
          'ConservativeF pressure(NonConservativeF +)',...
          'ConservativeF mass flow rate(NonConservativeF +)');
   title({'Conservative and Non Conservative Form',...
          'Primitive variables along the nozzle - Steady State'});

   figure(8)
   movegui(figure(8),'center');
   plot(nt_array,rho_throat,'+r','marker','+','MarkerIndices',1:5:length(rho_throat))
   hold on
   %plot(nt_array,V_throat,'+g','marker','+','MarkerIndices',1:5:length(V_throat))
   plot(nt_array,V_throat,'+g')
   plot(nt_array,T_throat,'+b')
   plot(nt_array,M_throat,'+m')
   plot(nt_array,p_throat,'+k')
   plot(nt_array,m_dot_throat,'+c')
   xlabel('number of time steps')
   ylabel('non-dimensional parameters')
   legend('ConservativeF density(NonConservativeF +)',...
          'ConservativeF Velocity(NonConservativeF +)',...
          'ConservativeF Temperature(NonConservativeF +)',...
          'ConservativeF Mach Number(NonConservativeF +)',...
          'ConservativeF pressure(NonConservativeF +)',...
          'ConservativeF mass flow rate(NonConservativeF +)');
   title({'Conservative and Non Conservative Form',...
          'Primitive variables at the throat as f(Time step) =%d',k'});
      
   figure(9)
   movegui(figure(9),'center');
   plot(x,m_dot,'+b')
   hold on 
   plot(x,rho_exact,'g');
   xlabel('length of nozzle')
   ylabel('non-dimensional parameters')
   legend('ConservativeF mass flow rate(NonConservativeF +)');
   title({'Conservative and Non Conservative Form',...
          'mass flow along the nozzle along the nozzle with exact solution- Steady State'});

   figure(10)
   movegui(figure(10),'center');
   plot(nt_array,m_dot_throat,'+b','marker','+','MarkerIndices',1:5:length(m_dot_throat))
   xlabel('number of time steps')
   ylabel('non-dimensional parameters')
   legend('ConservativeF mass flow rate(NonConservativeF +)');
   title({'Conservative and Non Conservative Form',...
          'mass flow at the throat as f(Time step) =%d',k'});

end  
Your portfolio, next

Ship work like this
in your own course.

Talk to a career counsellor

Free 20-minute call. We'll map the right course to where you want to work.

By submitting you agree to our Terms and Privacy Policy.

Talk to a career counsellor

Free 20-minute call. We'll map the right course to where you want to work.

By submitting you agree to our Terms and Privacy Policy.