CFD ASSIGNMENT
Mallinath Ogirala
SE22UMEE014
Steady 1-dimensional diffusion equation with source term:
L = 0.5 ; % domain length
Nx = 25; % number of cells
m = createMesh1D(Nx, L);
x = [Link].x; % extract the cell center positions
%%
% The next step is to define the boundary condition:
BC = createBC(m); % all Neumann boundary condition structure
[Link].a = 0; [Link].b=1; % switch the left boundary to Dirichlet
[Link].c=100; % value = 0 at the left boundary
[Link].a = 0; [Link].b=1; % switch the right boundary to Dirichlet
[Link].c=200; % value = 1 at the right boundary
S = 1000;
D_val = 1; % diffusion coefficient value
D = createCellVariable(m, D_val); % assign dif. coef. to all the cells
Dave = harmonicMean(D); % convert a cell variable to face variable
Mdiff = diffusionTerm(Dave); % diffusion term
[Mbc, RHSbc] = boundaryCondition(BC); % boundary condition discretization
M = -Mdiff+Mbc; % matrix of coefficient for central scheme
RHS = RHSbc+S; % right hand side vector
c = solvePDE(m, M, RHS); % solve for the central scheme
figure(5);
plot(x, [Link](2:Nx+1));
legend('central');
Discussions and interpretations:
The plot of the 1D convection-diffusion equation showcases the concentration profile from x=0
to x=5 with Dirichlet boundary conditions at both ends (100 and 200, respectively). Given the
uniform diffusion coefficient D=1, the concentration profile is expected to increase smoothly,
primarily due to diffusion. The addition of a source term S=1000 shifts the profile upward,
creating a concave shape that reflects the source’s impact on diffusion. Since the grid resolution
is relatively coarse (25 cells), the central difference scheme provides a stable solution with
minimal numerical artifacts. The harmonic mean used for D prepares the model for handling
variable diffusion coefficients, which would maintain continuity and stability in more complex
cases.
Steady 1-dimensional convection-diffusion equation:
% convection diffusion equation 1-D
%% Eg 5.1
% Then we define the domain and mesh size:
L = 1; % domain length
Nx = 25; % number of cells
meshstruct = createMesh1D(Nx, L);
x = [Link].x; % extract the cell center positions%%
% The next step is to define the boundary condition:
BC = createBC(meshstruct); % all Neumann boundary condition structure
[Link].a = 0; [Link].b=1; % switch the left boundary to Dirichlet
[Link].c=1; % value = 0 at the left boundary
[Link].a = 0; [Link].b=1; % switch the right boundary to Dirichlet
[Link].c=0; % value = 1 at the right boundary
% Now we define the transfer coefficients:
D_val = 1.0; % diffusion coefficient value
D = createCellVariable(meshstruct, D_val); % assign dif. coef. to all the cells
Dave = harmonicMean(D); % convert a cell variable to face variable
u = 2.5; % velocity value
u_face = createFaceVariable(meshstruct, u); % assign velocity value to cell faces
Mconv = convectionTerm(u_face); % convection term, central, second order
Mconvupwind = convectionUpwindTerm(u_face); % convection term, upwind, first order
Mdiff = diffusionTerm(Dave); % diffusion term
[Mbc, RHSbc] = boundaryCondition(BC); % boundary condition discretization
M = Mconv-Mdiff+Mbc; % matrix of coefficient for central scheme
Mupwind = Mconvupwind-Mdiff+Mbc; % matrix of coefficient for upwind scheme
RHS = RHSbc; % right hand side vector
c = solvePDE(meshstruct, M, RHS); % solve for the central scheme
c_upwind = solvePDE(meshstruct, Mupwind, RHS); % solve for the upwind scheme
c_analytical = 1-(1-exp(u*x/D_val))/(1-exp(u*L/D_val)); % analytical solution
figure(5);
plot(x, [Link](2:Nx+1), x, c_upwind.value(2:Nx+1), '--',...
x, c_analytical, '.');
legend('central', 'upwind', 'analytical','.');
Discussions and Interpretations:
The code solves the 1D convectiondiffusion equation with Dirichlet boundary conditions. It uses both
central and upwind schemes to compute the numerical solutions, comparing them with the analytical
solution. Plot Analysis: The plot displays the concentration profiles for central and upwind schemes
alongside the analytical solution. The comparison highlights the accuracy and stability of the numerical
methods, with the upwind scheme effectively handling convection-dominated scenarios
Transient One-Dimensional Diffusion Equation:
clc;
clear;
L = 0.02;
Nx = 20;
m = createMesh1D(Nx, L);
x = [Link].x;
BC = createBC(m);
[Link].a = 1; [Link].b=0; [Link].c=0;
[Link].a = 0; [Link].b=1; [Link].c=0;
D_val = 1.e-6 ;
D = createCellVariable(m, D_val);
Dave = harmonicMean(D);
alfa_val = 1;
alfa = createCellVariable(m, alfa_val);
c_init = 200;
c_old = createCellVariable(m, c_init, BC);
c = c_old;
dt = 0.5;
final_t = 100;
Mdiff = diffusionTerm(Dave);
[Mbc, RHSbc] = boundaryCondition(BC);
for t=0.:dt:final_t
[M_trans, RHS_trans] = transientTerm(c_old, dt, alfa);
M = M_trans-Mdiff+Mbc;
RHS = RHS_trans+RHSbc;
c = solvePDE(m,M, RHS);
c_analytical = 1-erf(x/(2*sqrt(D_val*t)));
c_old = c;
if (mod(t/dt,10) == 0)
plot(x, [Link](2:Nx+1)); hold on;
% plot(x, [Link](2:Nx+1), 'o', x, c_analytical); hold on;
end
End
Discussions and interpretations:
This code solves the transient diffusion equation, modeling how concentration (or temperature)
diffuses over time in a 1D domain. Dirichlet boundary conditions ensure the concentration at
both ends remains zero. Plot Analysis: The plot shows the concentration profile at different time
steps, comparing numerical and analytical solutions. The results validate the numerical method's
accuracy in simulating the diffusion process.
Transient One-Dimensional Convection:
clc; clear;
% define a 1D domain and mesh
W = 1;
Nx = 500;
mesh1 = createMesh1D(Nx, W);
x = [Link].x;
% define the boundaries
BC = createBC(mesh1); % all Neumann
[Link] = 1;
[Link] = 1;
% Initial values
phi_old = createCellVariable(mesh1, 0.0, BC);
% Creating triangular initial profile
% From previous FD Code
n_base = ceil(0.1*Nx);
n_peak = ceil(0.5*n_base);
peak_height = 2.0;
phi_old.value(1:n_peak) = (peak_height/x(n_peak))*x(1:n_peak);
phi_old.value(n_peak+1:n_base) = (peak_height/x(n_peak))*(x(n_base)-
x(n_peak+1:n_base));
phi = phi_old;
phiuw_old = phi_old;
% initial values for upwind scheme
phiuw = phi;
% keep the initial values for visualization
phiinit = phi_old;
% velocity field
u = 0.3;
uf = createFaceVariable(mesh1, u);
% diffusion field
D = 1e-2;
Df = createFaceVariable(mesh1, D);
% transient term coefficient
alfa = createCellVariable(mesh1,1.0);
% upwind convection term
Mconvuw = convectionUpwindTerm1D(uf);
% define the BC term
[Mbc, RHSbc] = boundaryCondition(BC);
FL = fluxLimiter('Superbee');
% solver
dt = 0.001; % time step
final_t = W/u;
t = 0;
while t<final_t
t = t+dt;
% inner loop for TVD scheme
for j = 1:5
[Mt, RHSt] = transientTerm(phi_old, dt, alfa);
[Mconv, RHSconv] = convectionTvdTerm1D(uf, phi, FL);
M = Mconv+Mt+Mbc;
RHS = RHSt+RHSbc+RHSconv;
phi = solvePDE(mesh1, M, RHS);
end
[Mtuw, RHStuw] = transientTerm(phiuw_old, dt, alfa);
Muw = Mconvuw+Mtuw+Mbc;
RHSuw = RHStuw+RHSbc;
phiuw = solvePDE(mesh1, Muw, RHSuw);
phiuw_old = phiuw;
phi_old = phi;
% figure();
plot(x, [Link](2:Nx+1), x, [Link](2:Nx+1), '-o', x, ...
[Link](2:Nx+1),'-x');
pause(0.005);
end
Discussions and Interpretations:
This code demonstrates the evolution of a triangular profile under the influence of convection
and diffusion. Using a flux limiter (Superbee) ensures numerical stability and accuracy, reducing
numerical dispersion and preventing nonphysical oscillations near steep gradients. Plot Analysis:
The plot shows the initial triangular profile evolving over time. The convection term moves the
profile, while diffusion causes it to spread out. The Superbee flux limiter effectively prevents
oscillations, ensuring a stable solution.
Pressure-Velocity Coupling using SIMPLE Method for a lid-driven
cavity:
function SteadyLidDrivenCavityExample( varargin )
addpath('Functions'); % add Functions folder to the search path
% get the reference solution values
testSol=GhiaSolution;
%% User Input
USE_CHOW_INTERP=1; % flag to choose the rhie-chow interpolation
p_relax=1; % pressure relaxation
velo_relax=0.9; % velocity relaxation
u_init=0; % initial guess for x-velocity
v_init=0; % initial guess for y-velocity
p_init=0; % initial guess for pressure
Re=100; % Reynolds number % 100, 400 and 1000 is possible
lidVelo=1; % velocity of the lid [m/s]
rho=1000; % density [kg/m^3]
cavityLength=0.1; % length of one edge of the quare cavity [m]
mu=rho*cavityLength*lidVelo/Re;% dynamic viscosity
nIter=250; % number of iterations
% First create the mesh
m=createMesh2D(51,51,cavityLength,cavityLength);
U_BC = createBC(m);
V_BC = createBC(m);
% Assign boundary conditions u-momentum equation
U_BC.top.a(:) = 0; U_BC.top.b(:)=1; U_BC.top.c(:)=lidVelo; % lid velocity in x-
direction at the top of the cavity
U_BC.left.a(:) = 0; U_BC.left.b(:)=1; U_BC.left.c(:)=0; % zero x-velocity at left
boundary
U_BC.right.a(:) = 0; U_BC.right.b(:)=1; U_BC.right.c(:)=0; % zero x-velocity right
boundary
U_BC.bottom.a(:) = 0; U_BC.bottom.b(:)=1; U_BC.bottom.c(:)=0; % no-slip at bottom
boundary
% Assign boundary conditions for v-momentum equation
V_BC.top.a(:) = 0; V_BC.top.b(:)=1; V_BC.top.c(:)=0; % zero y-velocity at top
boundary
V_BC.bottom.a(:) = 0; V_BC.bottom.b(:)=1; V_BC.bottom.c(:)=0; % zero y-velocity at
bottom boundary
V_BC.left.a(:) = 0; V_BC.left.b(:)=1; V_BC.left.c(:)=0; % no-slip at left boundary
V_BC.right.a(:) = 0; V_BC.right.b(:)=1; V_BC.right.c(:)=0;
P_BC = createBC(m);
P_BC.bottom.a(end/2+0.5) = 0;
P_BC.bottom.b(end/2+0.5)=1;
P_BC.bottom.c(end/2+0.5)=0;
mu_faceVar=createFaceVariable(m,mu);
rho_faceVar=createFaceVariable(m,rho);
U_cellVar=createCellVariable(m,u_init,U_BC);
V_cellVar=createCellVariable(m,v_init,V_BC);
faceVelocity=createFaceVariable(m,[u_init v_init]);
p_cellVar=createCellVariable(m,p_init,P_BC);
pGradxCellVar=createCellVariable(m,0);
pGradyCellVar=pGradxCellVar;
%% main loop
for i=1:nIter
pGradMat = CDGradientCellTerm(p_cellVar);
U_cellVar_old=U_cellVar;
V_cellVar_old=V_cellVar;
% solve the momentum equations
[U_cellVar,V_cellVar,U_aP_sumMat,V_aP_sumMat]=MomentumEq(velo_relax,m,pGradMat,mu_fac
eVar,faceVelocity,U_cellVar_old,V_cellVar_old,U_BC,V_BC,rho,'SIMPLE');
[Link]=[Link]*0;
[Link]=[Link]*0;
[Link](2:end-1,2:end-1)=[Link];
[Link](2:end-1,2:end-1)=[Link];
if USE_CHOW_INTERP
faceVelocity=RhieChow(U_cellVar,V_cellVar,U_aP_sumMat,V_aP_sumMat,p_cellVar,pGradxCel
lVar,pGradyCellVar,U_BC,V_BC);
else
helpx=arithmeticMean(U_cellVar);
helpy=arithmeticMean(V_cellVar);
[Link]=[Link];
[Link]=[Link];
end
[RHS, ~, ~, ~] = divergenceTerm(faceVelocity);
U_aP_Face=arithmeticMean(U_aP_sumMat);
V_aP_Face=arithmeticMean(V_aP_sumMat);
diffusion_coeff_helpVar=U_aP_Face;
diffusion_coeff_helpVar.yvalue=V_aP_Face.yvalue;
diffusion_coeff.domain=m;
diffusion_coeff.xvalue=velo_relax*rho_faceVar.[Link].y(1)./diffusion_coeff_h
[Link];
diffusion_coeff.yvalue=velo_relax*rho_faceVar.[Link].x(1)./diffusion_coeff_h
[Link];
test_diff=diffusionTerm(diffusion_coeff);
% ---------------------------------------------------------------------
% ---------------------------------------------------------------------
% pressure boundary condition
[p_Mbc, p_RHSbc] = boundaryCondition2D(P_BC);
% ---------------------------------------------------------------------
% ---------------------------------------------------------------------
% solve for the new pressure correction
p_new=solvePDE(m,test_diff+p_Mbc, RHS+p_RHSbc);
% ---------------------------------------------------------------------
p_cellVarOld.value=p_cellVar.value;
p_cellVar.value=p_cellVar.value+p_relax*p_new.value;
p_max_error=max(max(abs(p_cellVarOld.value-p_cellVar.value)));
% ---------------------------------------------------------------------
% update the velocity
pGradMat = gradientCellTerm(p_new);
[Link]=[Link]*0;
[Link]=[Link]*0;
[Link](2:end-1,2:end-1)=[Link];
[Link](2:end-1,2:end-1)=[Link];
UU=U_cellVar.value-...
[Link].y(1)*[Link]./U_aP_sumMat.value;
VV=V_cellVar.value-...
[Link].x(1)*[Link]./V_aP_sumMat.value;
U_cellVar.value=velo_relax*UU+(1-velo_relax)*U_cellVar.value;
V_cellVar.value=velo_relax*VV+(1-velo_relax)*V_cellVar.value;
if ~mod(i,50)
contour([Link].x,[Link].y,hypot(U_cellVar.value(2:end-1,2:end-
1)',V_cellVar.value(2:end-1,2:end-1)'),'LevelList',0:0.1:1,'ShowText','on')
hold on
axis equal
xlabel('x [m]');
ylabel('y [m]');
hold off
ylim([0 0.1]);
xlim([0 0.1]);
drawnow
end
GhiaValues.u=interp1([Link].y,U_cellVar.value(end/2+0.5,2:end-
1),testSol.y*0.1,'linear','extrap');
GhiaValues.v=interp1([Link].x,V_cellVar.value(end/2+0.5,2:end-
1),testSol.x*0.1,'linear','extrap');
RMSEu=sqrt(sum((testSol.u{[Link]==Re}(:)-
GhiaValues.u(:)).^2)/numel(testSol.u{[Link]==Re}));
RMSEv=sqrt(sum((testSol.v{[Link]==Re}(:)-
GhiaValues.v(:)).^2)/numel(testSol.v{[Link]==Re}));
U_max_error=max(max(abs(U_cellVar_old.value-U_cellVar.value)));
V_max_error=max(max(abs(V_cellVar_old.value-V_cellVar.value)));
fprintf('i: %d\t p_max_error: %e\t (u, v)_max_error: (%e, %e)\t RMSE(u, v):
(%e, %e)\n',i,p_max_error,U_max_error,V_max_error,RMSEu,RMSEv);
end
figure
hold on;
h1=plot(testSol.u{[Link]==Re},testSol.y*0.1,'o');
h2=plot(U_cellVar.value(end/2+0.5,2:end-1),[Link].y);
legend([h1 h2],'Ghia et al. [1]','Numerical solution','location','southeast');
ylabel('y [m]')
xlabel('Velocity in x-direction [m/s]');
hold off;
figure;
hold on;
h1=plot(testSol.x*0.1,testSol.v{[Link]==Re},'o');
h2=plot([Link].x,V_cellVar.value(2:end-1,end/2+0.5));
legend([h1 h2],'Ghia et al. [1]','Numerical solution');
xlabel('x [m]')
ylabel('Velocity in y-direction [m/s]');
hold off;
end
function cellGrad=CDGradientCellTerm(phi)
Nx = [Link](1);
Ny = [Link](2);
DX = repmat([Link].x(2:end-1), 1, Ny);
DY = repmat([Link].y(2:end-1)', Nx, 1);
xvalue = ([Link](3:Nx+2,:)-[Link](1:Nx,:))./(2*DX(2));
yvalue = ([Link](:,3:Ny+2)-[Link](:,1:Ny))./(2*DY(2));
zvalue=[];
cellGrad=FaceVariable([Link], xvalue(:,2:end-1), yvalue(2:end-1,:), zvalue);
End
Discussions and Interpretations:
This function implements a numerical simulation of the steady-state lid-driven cavity flow
problem using a SIMPLE algorithm. The Reynolds number is user-defined, and the code uses
Rhie-Chow interpolation for velocity at faces, which improves pressure-velocity coupling. The
mesh is created as a 2D grid with Dirichlet boundary conditions on the velocity fields: the top lid
moves at a specified speed, while other boundaries enforce no-slip conditions. During the
iterative process, the code updates velocity and pressure fields, ensuring convergence by tracking
error metrics. The plot updates every 50 iterations, visualizing the velocity magnitude. At the
end, the solution is compared to the benchmark Ghia solution, providing insight into the
accuracy through Root Mean Squared Error (RMSE) values. The final plots display the x- and y-
velocity profiles along the centerlines, offering a direct comparison to the reference, showcasing
the model's performance.