Challenge

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

The MacCormack method is a widely used discretization scheme for the numerical solution of hyperbolic partial differential equations.

The MacCormack method is a variation of the two-step Lax–Wendroff scheme but is much simpler in application. To illustrate the algorithm, consider the following first order hyperbolic equation:

`(partialu)/(partialt)+a(partialu)/(partialx)=0`

The application of MacCormack method to the above equation proceeds in two steps; a predictor step which is followed by a corrector step

Predictor step: In the predictor step, a "provisional" value of`u`at time level `n+1`(denoted by `u_i^(n+1)` is estimated as follows

`u_i^(n+1)=u_i^n-a(Deltat)/(Deltax)(u_(i+1)^n-u_i^n)`

The above equation is obtained by replacing the spatial and temporal derivatives in the previous first order hyperbolic equation using forward differences.

Corrector step: In the corrector step, the predicted value `u_i^(n+1)`is corrected according to the equation

`u_i^(n+1)=u_i^((n+1)/2)-a(Deltat)/(2Deltax)(u_i^(n+1)-u_(i+1)^(n+1))`

The corrector step uses backward finite difference approximations for spatial derivative. The time-step used in the corrector step is `(Deltat)/2` in contrast to the `Deltat`used in the predictor step.

Now, We consider the study, isentropic flow through a convergent divergent nozzle. The flow at the inlet to the nozzle comes from reservoir. The cross section area of the reservoir is very large and hence velocity is very small. The flow is locally subsonic the convergent section of the nozzle, Sonic at the throat and Supersonic at the divergent section.

Considering the small control volume of the nozzle as shown in figure

We assume that at a given section, where the cross section area is A, the flow properties are uniform across the section. Hence, although the area of the nozzle changes as a function of distance along the nozzle, X, and therefore in reality the flow field is two dimensional(the flow varies in the two dimensional x-y space), We make the assumption that the flow properties vary only with X; this is tantamount to asuming uniform flow properties across any given cross-section. Such flow is defined as Quasi one dimensional flow.

Conservative Form:

In fluid dynamics, the continuity equation states that the rate at which mass enters a system is equal to the rate at which mass leaves the system plus the accumulation of mass within the system.The differential form of the continuity equation is:

`(partialrho)/(partialt)+grad*(rhov)=0`

where

  • ρ is fluid density,
  • t is time,
  • v is the flow velocity vector field.

The time derivative can be understood as the accumulation (or loss) of mass in the system, while the divergence term represents the difference in flow in versus flow out. In this context, this equation is also one of the Euler equations (fluid dynamics). The Navier–Stokes equations form a vector continuity equation describing the conservation of linear momentum.

If the fluid is incompressible (volumetric strain rate is zero), the mass continuity equation simplifies to a volume continuity equation:

`nabla*u=0`

which means that the divergence of the velocity field is zero everywhere. Physically, this is equivalent to saying that the local volume dilation rate is zero, hence a flow of water through a converging pipe will adjust solely by increasing its velocity as water is largely incompressible.

Conservative Continuty equation:

`(partial(rho^'A^'))/(partialt)+(partial(rho^'A^'V^'))/(partialx^')=0`

Conservative Momentum equation:

`(partial(rho^'A^'V^'))/(partialt^')+(partial(rho^'A^'(U^')^2+(1/gamma)rho^'A^'))/(partialx^')=(1/gamma)rho^'((partialA^')/(partialx^'))`

Conservative Energy equation:

`(partial[rho^'(e^'/(gamma-1))+gamma/2+(V^')^2A^1])/(partialt^')+(partial[rho^'(e^'/(gamma-1))+gamma/2+(V^')^2)V^'A^'+rho^'A^'V^']/(partialx^')=0`

For air at standard conditions, `gamma=1.4`

Now, function code for conservative part :

%%CONSERVATIVE

function [mfr_c,mfr_throat_c]=conservative(L,n,nt,cfl)

x=linspace(0,L,n);
dx=x(2)-x(1);

% initial conditions rho, a, v, T, M, mfr, p
for ii=1:length(x)
    if (x(ii)<=0.5)
        rho(ii) = 1;
        T(ii) = 1;
    elseif (x(ii)>0.5) & (x(ii)<=1.5)
        rho(ii) = 1-0.366*(x(ii)-0.5);
        T(ii)= 1-0.167*(x(ii)-0.5);
    elseif (x(ii)>1.5) & (x(ii)<=3.5)
        rho(ii) = 0.634-0.3879*(x(ii)-1.5);
        T(ii)= 0.833-0.3507*(x(ii)-1.5);
    end
end
a= 1+2.2*(x-1.5).^2;     %area
g = 1.4;                 %gamma value
% solution vectors u1, u2, u3
u1 = rho.*a;
u2 = 0.59*ones(1,n);
v=u2./u1;
u3 = u1.*((T./(g-1))+(g/2).*v.^2);

M= v./(T.^0.5);          %mach number
mfr = rho.*a.*v;         %mass flow rate
p=rho.*T;                %pressure


for z=1:31               %to find dt value
    u=(T(z))^(1/2);
    dt(z) = cfl*dx/(u + v(z));
end
dt=min(dt);

for i = 1:length(nt)
    %storing initial values
    u1_old = u1;
    u2_old = u2;
    u3_old = u3;

    for j=2:n-1
        %flux vectors
        f1 = u2;
        f2 = ((u2.^2)./u1)+((g-1)/g)*(u3-((g/2)*(u2.^2)./u1));
        f3 = ((g.*u2.*u3)./u1) - ((g*(g-1)/2)*(u2.^3)./(u1.^2));
        %source term
        j1 = (1/g)*rho(j)*T(j)*((a(j+1)-a(j))/dx);
        %predictor step
        du1_dt_p(j)= -(f1(j+1)-f1(j))/dx;           %continuity
        du2_dt_p(j)= -(f2(j+1)-f2(j))/dx + j1;      %momentum
        du3_dt_p(j)= -(f3(j+1)-f3(j))/dx;           %energy
        %updating solution vectors
        u1(j) = u1_old(j) + du1_dt_p(j)*dt;
        u2(j) = u2_old(j) + du2_dt_p(j)*dt;
        u3(j) = u3_old(j) + du3_dt_p(j)*dt;
        %updating primitives
        rho=u1./a;
        T=(g-1).*(u3./u1-(g/2).*(u2./u1).^2);
        %updating flux terms
        f1=u2;
        f2=u2.^2./u1+((g-1)/g).*(u3-(g/2)*u2.^2./u1);
        f3=g.*u2.*u3./u1-0.5*g*(g-1).*u2.^3./u1.^2;
        %source term for corrector step
        j2 = (1/g)*(rho(j)*T(j)*(a(j)-a(j-1))/dx);
        %corrector step
        du1_dt_c(j) = -(f1(j)-f1(j-1))/dx;            %continuity
        du2_dt_c(j) = -(f2(j)-f2(j-1))/dx + j2;       %momentum
        du3_dt_c(j) = -(f3(j)-f3(j-1))/dx;            %energy
    end
    %average time derivatives
    du1_dt_avg = 0.5*(du1_dt_p + du1_dt_c);
    du2_dt_avg = 0.5*(du2_dt_p + du2_dt_c);
    du3_dt_avg = 0.5*(du3_dt_p + du3_dt_c);
    %final corected values
    for k = 2:n-1
        u1(k) = u1_old(k) + du1_dt_avg(k)*dt;
        u2(k) = u2_old(k) + du2_dt_avg(k)*dt;
        u3(k) = u3_old(k) + du3_dt_avg(k)*dt;
    end
    %entry boundary conditions
    u1(1) = a(1);
    u2(1) = 2*u2(2)-u2(3);
    u3(1) = u1(1)*(T(1)/(g-1) + (g*(u2(1)/u1(1))^2)/2);
    %exit boundary conditions
    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);

    %Final update of primitives
    rho = u1./a;
    v = u2./u1;
    T=(g-1).*(u3./u1-(g/2).*(u2./u1).^2);
    p = rho.*T;
    M= v./T.^0.5;
    error(k) =  abs(v(k)-v(k-1));
    mfr= u2;
    %to find values at throat point
    T_throat(i) = T(16);
    rho_throat(i) = rho(16);
    v_throat(i) = v(16);
    M_throat(i) = M(16);
    p_throat(i) = p(16);
    error_throat(i)=error(16);
    mfr_throat(i)=mfr(16);

    if n==31
        for ii=[1:50:201 700]
            if ii==i
                figure(4);
                plot(x,mfr,'linewidth',1);
                hold on
                title ('Conservative form');
                xlabel('Length of nozzle');
                ylabel('Mass flow rate');
            elseif i==1400
                plot(x,mfr,'*r');
                legend('@1','@51','@101','@151','@201','@700','@1400');
            end
        end
    end

end
if n==31
    figure(5)
    plot(x,v,'b',x,p,'r',x,T,'k',x,rho,'m',x,M,'g',x,mfr,'c');
    ylim([-0.1 3.7])
    title (sprintf('Time step = %d',i));
    xlabel('Length of nozzle');
    ylabel('Non dimensional parameters');
    legend('Velocity','Pressure','Temperature','Density','Mach number','Mass flow rate','location','northwest');
    title('Conservative form');

    figure (6)
    plot(nt,rho_throat) ;
    hold on
    plot(nt,v_throat,'linewidth',1);
    plot(nt,p_throat,'linewidth',1);
    plot(nt,T_throat,'linewidth',1);
    plot(nt,M_throat,'linewidth',1);
    plot(nt,mfr_throat,'linewidth',1);
    xlabel('Time steps (nt)');
    ylabel('Non dimensional parameters');
    legend('Density','Velocity','Pressure','Temperature','Mach number','Mass flow rate');
    title('Conservative form - Time-wise variation of the primitive variables at throat point i=16');
end
mfr_c=mfr;
mfr_throat_c=mfr_throat;

end

Non-Conservative Form:

The reason they are conservative or non-conservative has to do with the splitting of the derivatives. Consider the conservative derivative:

`rho(partialu)/(partialx)`

When we discretize this, using a simple numerical derivative just to highlight the point, we get:

`rho(partialu)/(partialx)=((rhou)_i-(rhou)_(i-1))/(Deltax)`

Now, in non-conservative form, the derivative is split apart as:

`rho(partialu)/(partialx)+u(partialrho)/(partialx)`

Using the same numerical approximation, we get:

`rho(partialu)/(partialx)+u(partialrho)/(partialx)=rho_i(u_i-u_(i-1))/(Deltax)+u_i(rho_i-rho_(i-1))/(Deltax)`
While the original derivative is mathematically the same, the discrete form is not the same. Of particular difficulty is the choice of the terms multiplying the derivative.
Continuity Equation:
`(partialrho^')/(partialt^')=-rho^'(partialV^')/(partialx^')-rho^'V^'(partiallnA^')/(partialx^')-v^'(partialrho^')/(partialx^')`
Momentum Equation:
`(partialV^')/(partialt^')=V^'(partialV^')/(partialx^')-1/gamma((partialT^')/(partialx^')+T^'/rho^'+(partialrho^')/(partialx^')`
Energy Equation:
`(partialT^')/(partialt')=-V(partialT^')/(partialx^')-(gamma-1)T^'[(partialV^')/(partialx^')+V(partiallnA^')/(partialx^"]`
Code Function for Non-Coservative:
%% NON CONSERVATIVE

function [mfr_nc,mfr_throat_nc]=non_conservative(L,n,nt,cfl)

x=linspace(0,L,n);
dx=x(2)-x(1);

% initial conditions rho, a, v, T, mfr, M, p
rho = 1-0.3146*x;
T= 1-0.2314*x;
v=(0.1+1.09*x).*T.^0.5;
a= 1+2.2*(x-1.5).^2;
M= v./(T.^0.5);
mfr = rho.*a.*v;
p=rho.*T;

g = 1.4;

for z=1:31
    u=(T(z))^(1/2);
    dt(z) = cfl*dx/(u + v(z));
end
dt=min(dt);

for i = 1:length(nt)
    %storing initial values
    rho_old = rho;
    T_old = T;
    v_old = v;
    for j=2:n-1
        %predictor step
        drho_dx = (rho(j+1)-rho(j))/dx;
        dv_dx = (v(j+1)-v(j))/dx;
        dT_dx = (T(j+1)-T(j))/dx;
        da_dx = (log(a(j+1))-log(a(j)))/dx;
        drho_dt_p(j) = -rho(j)*dv_dx - rho(j)*v(j)*da_dx - v(j)*drho_dx;        %continuty
        dv_dt_p(j) = -v(j)*dv_dx - (1/g)*(dT_dx + (T(j)/rho(j))*drho_dx);       %momentum
        dT_dt_p(j) = -v(j)*dT_dx - (g-1)*T(j)*(dv_dx + v(j)*da_dx);             %energy
        %updating primitives
        v(j) = v_old(j) + dv_dt_p(j)*dt;
        rho(j) = rho_old(j) + drho_dt_p(j)*dt;
        T(j) = T_old(j) + dT_dt_p(j)*dt;
        %corrector step
        drho_dx = (rho(j)-rho(j-1))/dx;
        dv_dx = (v(j)-v(j-1))/dx;
        dT_dx = (T(j)-T(j-1))/dx;
        da_dx = (log(a(j))-log(a(j-1)))/dx;
        drho_dt_c(j) = -rho(j)*dv_dx - rho(j)*v(j)*da_dx - v(j)*drho_dx;        %continuity
        dv_dt_c(j) = -v(j)*dv_dx - (1/g)*(dT_dx + (T(j)/rho(j))*drho_dx);       %momentum
        dT_dt_c(j) = -v(j)*dT_dx - (g-1)*T(j)*(dv_dx + v(j)*da_dx);             %energy
    end
    %average time derivatives
    drho_dt_avg = 0.5*(drho_dt_p + drho_dt_c);
    dv_dt_avg = 0.5*(dv_dt_p + dv_dt_c);
    dT_dt_avg = 0.5*(dT_dt_p + dT_dt_c);
    %final corected values
    for k = 2:n-1
        rho(k) = rho_old(k) + drho_dt_avg(k)*dt;
        v(k) = v_old(k) + dv_dt_avg(k)*dt;
        T(k) = T_old(k) + dT_dt_avg(k)*dt;
    end
    %entry boundary conditions
    v(1) = 2*v(2)-v(3);
    %exit boundary conditions
    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);
    %Final update of primitives
    p = rho.*T;
    M= v./T.^0.5;
    error(k) =  abs(v(k)-v(k-1));
    mfr= rho.*a.*v;
    %to find values at throat point
    T_throat(i) = T(16);
    rho_throat(i) = rho(16);
    v_throat(i) = v(16);
    M_throat(i) = M(16);
    p_throat(i) = p(16);
    error_throat(i)=error(16);
    mfr_throat(i)=mfr(16);
    if n==31
        for ii=[1:50:201 700]
            if ii==i
                figure(1)
                plot(x,mfr,'linewidth',1);
                hold on
                title ('Non-Conservative form');
                xlabel('Length of nozzle');
                ylabel('Mass flow rate') ;
            elseif i==1400
                plot(x,mfr,'*r');
                legend('@1','@51','@101','@151','@201','@700','@1400') ;
            end
        end
    end

end
if n==31
    figure(2)
    plot(x,v,'b',x,p,'r',x,T,'k',x,rho,'m',x,M,'g',x,mfr,'c');
    ylim([-0.1 3.7]);
    title (sprintf('Time step = %d',i));
    xlabel('Length of nozzle');
    ylabel('Non dimensional parameters');
    legend('Velocity','Pressure','Temperature','Density','Mach number','Mass flow rate','location','northwest');
    title('Non-Conservative form - Steady-state distribution of primitive variables inside the nozzle');

    figure (3)
    plot(nt,rho_throat)  ;
    hold on
    plot(nt,v_throat,'linewidth',1);
    plot(nt,p_throat,'linewidth',1) ;
    plot(nt,T_throat,'linewidth',1)  ;
    plot(nt,M_throat,'linewidth',1);
    plot(nt,mfr_throat,'linewidth',1);
    xlabel('Time steps (nt)');
    ylabel('Non dimensional parameters');
    legend('Density','Velocity','Pressure','Temperature','Mach number','Mass flow rate');
    title('Non-Conservative form - Time-wise variation of the primitive variables at throat point i=16');
end
mfr_nc=mfr;
mfr_throat_nc=mfr_throat;
end

Now, the main code

where we took the folloeing parameters

length of the nozzle is 3

number of grid points are 31, 65 and 87

number of time steps are 1:1400

cfl number is 0.5

now the main code for the program is

close all
clear all
clc

L=3;                    %length of nozzle
n=[31 65 87];           %number of grid points
nt=1:1400;              %number of time steps
cfl = 0.5;              %CFL number
for i = 1:length(n)
    x=linspace(0,L,n(i));
    [mfr_nc,mfr_throat_nc]=non_conservative(L,n(i),nt,cfl);
    [mfr_c,mfr_throat_c]=conservative(L,n(i),nt,cfl);
    figure(7)
    subplot(2,1,1);
    plot(nt,mfr_throat_c,'b',nt,mfr_throat_nc,'r','linewidth',1)
    xlabel('Time steps (nt)');
    ylabel('Mass flow rate');
    legend('Conservative form','Non-Conservative form');
    title('Mass flow rate using "Non-Conservative form vs Conservative form" at throat point i=16');

    subplot (2,1,2);
    plot(x,mfr_c,'b',x,mfr_nc,'r','linewidth',1);
    xlabel('Length of nozzle');
    ylabel('Mass flow rate');
    legend('Conservative form','Non-Conservative form');
    title('Mass flow rate using "Non-Conservative form vs Conservative form" in the nozzle');

    figure(8)
    subplot (2,1,1);
    plot(nt,mfr_throat_c,'linewidth',1);
    hold on
    xlabel('Time steps (nt)');
    ylabel('Mass flow rate');
    legend('@31 nodes','@65 nodes','@87 nodes');
    title('Conservative form: Grid independency test at throat point i=16');
    subplot (2,1,2);
    plot(nt,mfr_throat_nc,'linewidth',1);
    hold on
    xlabel('Time steps (nt)');
    ylabel('Mass flow rate');
    legend('@31 nodes','@65 nodes','@87 nodes');
    title('Non-Conservative form: Grid independency test at throat point i=16');
    figure(9)
    subplot (2,1,1);
    plot(x,mfr_c,'linewidth',1);
    hold on
    xlabel('Length of nozzle');
    ylabel('Mass flow rate');
    legend('@31 nodes','@65 nodes','@87 nodes');
    title('Conservative form: Grid independency test along the nozzle');
    subplot (2,1,2);
    plot(x,mfr_nc,'linewidth',1);
    hold on
    xlabel('Length of nozzle');
    ylabel('Mass flow rate');
    legend('@31 nodes','@65 nodes','@87 nodes');
    title('Non-Conservative form: Grid independency test along the nozzle');

end

Output:

  • The figure 1 depicts the varitaions of massl flow rate with respect to the number of time steps. As figure depicts the more number of times steps the more accurate result and if less number of time steps the results are not accurate non conservative form.
  • Figure 2 depicts the steady state distribution of the mass flow rate in the non conservative form of the primitve variables present in the nozzle.
  • Figure 3 depicts the Time-wise variation of the primitive variables at throat point i=16 in the non conservative form of the primitve variables present in the nozzle.
  • Figure 4 depicts depicts the varitaions of massl flow rate with respect to the number of time steps. As figure depicts the more number of times steps the more accurate result and if less number of time steps the results are not accurate in the conservative form.
  • Figure 5 depicts the steady state distribution of the mass flow rate in the conservative form of the primitve variables present in the nozzle.
  • Figure 6 depicts the Time-wise variation of the primitive variables at throat point i=16 in the non conservative form of the primitve variables present in the nozzle.
  • Figure 7 depicts the sub plots of mass flow rate at time steps 1 to 1400 of conservative and non conservative at throat point i=16 and through out the nozzle.
  • Figure 8 depicts the subplots of mass flow rate at time steps 1:1400 of coservative and non conservative in grid independecy test at throat point i=16.
  • Figure 9 depicts the subplots of mass flow rate at time steps 1:1400 of coservative and non conservative in grid independecy test along the nozzle.
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.