Loading repository data…
Loading repository data…
BanVacc / repository
This repository is a beginner-friendly introduction to Computational Fluid Dynamics (CFD) for those interested in developing their own CFD solver. It includes Jupyter notebooks with detailed instructions.
This repository is specifically designed for individuals who are new to the field of Computational Fluid Dynamics (CFD) and want to develop their first CFD solver. Its primary objective is to provide an introduction to the basic principles of CFD through a series of Jupyter notebooks and detailed descriptions.
The Jupyter notebooks contain step-by-step instructions on how to implement various CFD techniques, such as Chorin's Projection Method for solving the incompressible Navier-Stokes equations, as well as the Heat-Transfer equation to account for thermal effects. These notebooks are accompanied by sample codes that users can modify to suit their specific needs.
During this step, we define the simulation parameters, including:
Some physical properties of the fluid are:
In numerical solutions, a mesh is a discretization of a continuous domain into smaller, finite-sized elements, which are used to numerically approximate the solution of partial differential equations (PDEs). You can think of a mesh as a collection of nodes and edges that define the shape of the computational domain, where each element in the mesh represents a small portion of the domain.
In FEniCS, there are two ways to define a mesh:
mshr module to create a mesh. This involves defining the domain using Constructive Solid Geometry's three basic operations (subtract, intersection, and union), and then generating a mesh with a specified density using the mshr.generate_mesh(your_domain,mesh_density) function..xml file using the FEniCS Mesh function. For example, you can define a mesh with mesh = fe.Mesh('path.xml'). This is useful when you have a mesh that was created by another software or method and want to import it into FEniCS for further computations.The purpose of boundary conditions is to determine unknown function values on domain's boundary. Most common boundary conditions are:
In FEniCS, we can create a list of boundary conditions. Note that for the first element, we pass a C++ expression string as the third argument, but for the second, we pass a Python function. FEniCS supports multiple ways to define boundary conditions.
def left_boundary(x, on_boundary): return on_boundary and near(x[0], 0.0)
velocity_boundary_conditions = [
fe.DirichletBC(V, Constant((0.0, 0.0)), 'on_boundary'),
fe.DirichletBC(V, Expression(('4.0*1.5*x[1]*(0.41 - x[1]) / pow(0.41, 2)', '0')), left_boundary)]
Boundary conditions are frequently applied in the following manner:
[bc.apply(A, b) for bc in bcs]
The initial conditions describe the field values at the start of the simulation, i.e., at $t=0$. To set the initial state for a field in FEniCS, we can use the following syntax:
# Set the initial circular conditions with a center at (0.5, 0.5) and a magnitude of 60
t_prev.interpolate(
Expression(
'60 * (0.5*(1 - tanh(8*(sqrt(pow(x[0]-0.5, 2) + pow(x[1]-0.5, 2)) - 0.1))))',degree=1)
)
Here, t_prev is assigned an initial condition using Expression() with an expression evaluated for each point in the domain. The example above defines circular initial conditions with a center at $(0.5,0.5)$ and a magnitude of 60.
In order to solve Partial Differential Equations (PDEs) using FEniCS, it is necessary to represent them in their weak form. For instance, the first step of the Chorin's Projection Method described in the Solution Strategy section can be represented in its weak form as follows:
\int\limits_\Omega \frac{\mathbf{u}^\star - \mathbf{u}^n}{\Delta t} \cdot v \,dx =
- \int\limits_\Omega \nabla\mathbf{u}^n \cdot v \,dx
- \int\limits_\Omega \nu\,\nabla\mathbf{u}\cdot\nabla\,v\,dx
The full derivation of equation weak forms can be found in the file. By expressing PDEs in their weak form, FEniCS can utilize finite element methods to discretize the problem and obtain a numerical solution.
In code:
# Weak form of the momentum equation
momentum_residuum = (
(1.0 / dt) * fe.inner(u - u_prev, v) * fe.dx
+ fe.inner(fe.grad(u_prev) * u_prev, v) * fe.dx
+ nu * fe.inner(fe.grad(u), fe.grad(v)) * fe.dx)
The aim of this project is to solve the incompressible Navier-Stokes equations using Chorin's Projection Method (steps 1-3). The solution strategy can be summarized in the following steps:
\frac{\mathbf{u}^\star - \mathbf{u}^n}{\Delta t} =
- (\mathbf{u}^{n}\cdot\nabla)\mathbf{u}^{n}
+ \nu\,\mathbf{u}\nabla^{2}\mathbf{u}^{n}
Velocity boundary conditions are then applied to $\mathbf{u}^{\star}$
\nabla \mathbf{u}^{\star} = \frac{\Delta t}{\rho} \nabla^{2}\,p$$
Apply pressure boundary conditions.
\mathbf{u}^{n+1}=\mathbf{u}^{\star} - \frac{\Delta t}{\rho}\nabla p^{n+1}
Velocity boundary conditions are then applied.
\frac{T^{n+1}-T^{n}}{\Delta t}
= \alpha \nabla^{2}T^{n} - \frac{1}{\rho C_{p}} (\mathbf{u}\cdot\nabla)T^{n}