2/3 padding
for i = Int64(floor(nxe/2)+1):Int64(nx-floor(nxe/2)) for j = 1:ny
j1f[i,j] = 0.0
j2f[i,j] = 0.0
j3f[i,j] = 0.0
j4f[i,j] = 0.0
end~end
for i = 1:nx for j = Int64(floor(nye/2)+1):Int64(ny-floor(nye/2))
j1f[i,j] = 0.0
j2f[i,j] = 0.0
j3f[i,j] = 0.0
j4f[i,j] = 0.0
end~end
j1 = real(ifft(j1f))
j2 = real(ifft(j2f))
j3 = real(ifft(j3f))
j4 = real(ifft(j4f))
jacp = zeros(Float64,nx,ny)
for i = 1:nx for j = 1:ny
jacp[i,j] = j1[i,j]*j2[i,j] - j3[i,j]*j4[i,j]
end~end
jf = fft(jacp)
```


#### **10. Concluding Remarks**

The easy syntax and fast performance of Julia programming language make it one of the best candidates for engineering students, especially from a non-computer science background to develop codes to solve problems they are working on. We use Julia language as a tool to solve basic fluid flow problems which can be included in graduate-level CFD coursework. We follow a similar pattern as teaching a class, starting from basics and then building upon it to solve more advanced problems. We provide small pieces of code developed for simple fundamental problems in CFD. These pieces of codes can be used as a starting point and one can go about adding more features to solve complex problems. We make all codes, plotting scripts available online and open to everyone. All the codes are made available online to everyone on Github (https://github.com/surajp92/CFD\_Julia).

We explain fundamental concepts of finite difference discretization, explicit methods, implicit methods using one-dimensional heat equation in Section 2. We also outline the procedure to develop a compact finite difference scheme which gives higher-order accuracy with smaller stencil size. We present a multi-step Runge-Kutta method which has higher temporal accuracy than standard single step finite difference method. We illustrate numerical methods for hyperbolic conservation laws using the inviscid Burgers equation as the prototype example in Section 3. We demonstrate two shock-capturing methods: WENO-5 and CRWENO-5 methods for Dirichlet and periodic boundary conditions. Students can learn about WENO reconstruction and boundary points treatment which is also applicable to other problems.

We show an implementation of the finite difference method for the inviscid Burgers equation in its conservative form in Section 4. Students will get insight into a finite volume method implementation through this example. We present two approaches for computing fluxes at the interface using the flux splitting method and using Riemann solver. We use Riemann solver based on simple Rusanov scheme. Students can easily implement other methods by changing the function for Rusanov scheme. We also develop one-dimensional Euler solver and validate it for the Sod shock tube problem in Section 5. We borrow functions from heat equation codes, such as Runge-Kutta code for time integration. We use WENO-5 code developed for the inviscid Burgers equation for solving Euler equations. This will give students an understanding of how coding blocks developed for simple problems can be integrated to solve more challenging problems.

We illustrate different methods to solve elliptic equations using Poisson equation as an example in Section 6. We describe both direct methods and iterative methods to solve the Poisson equation. The direct solvers explained in this study are based on the Fourier transform. We demonstrate an implementation of FFT and FST for developing a Poisson solver for periodic and Dirichlet boundary condition respectively. We provide an overview of two iterative methods: Gauss-Seidel method and conjugate gradient method for the Poisson equation. Furthermore, we demonstrate the implementation of the V-cycle multigrid framework for the Poisson equation. A multigrid framework is a powerful tool for CFD simulations as it scales linearly, i.e., it has *O*(*N*) computational complexity.

We describe two-dimensional incompressible Navier-Stokes solver in Section 7 and validate it for two examples: lid-driven cavity problem and vortex-merger problem. We use streamfunction-vorticity formulation to develop the solver. This eliminates the pressure term in the momentum equation and we can use the collocated grid arrangement instead of the staggered grid arrangement. We use Arakawa numerical scheme for the Jacobian term. The lid-driven cavity problem has Dirichlet boundary condition and hence we use FST-based Poisson solver. We use FFT-based Poisson solver the vortex-merger problem since this problem is periodic in the domain. Most of the functions needed for developing the two-dimensional incompressible Navier-Stokes solver are taken from the heat equation and Poisson equation. Students will learn derivation of streamfunction-vorticity formulation, and its implementation through these examples and it can be easily extended to other problems like Taylor-Green vortex, decaying turbulence, etc. Furthermore, we develop hybrid incompressible Navier-Stokes solver for two-dimensional flow problems and is presented in Section 8. The solver is hybridized using explicit Runge-Kutta scheme and implicit Crank-Nicolson scheme for time integration. The solver is developed by solving the Navier-Stokes equation in Fourier space except for the nonlinear term. The nonlinear Jacobian term is computed in physical space using Arakawa finite difference scheme, converted to Fourier space and then used in Navier-Stokes equations. In addition, Section 9 provides the pseudo-spectral solver with 3/2 padding and 2/3 padding rule for two-dimensional incompressible Navier-Stokes equations. We also compare the computational performance of codes written in Julia and Python for the Navier-Stokes solvers. We should highlight that the codes are written in considering mostly pedagogical aspects (i.e., without performing any additional efforts in optimal coding and implementation practices) and cannot be viewed as a legitimate performance comparison of different languages, which is beyond the scope of this work.

**Author Contributions:** Data curation, S.P.; Visualization, S.P.; Supervision, O.S.; Writing—original draft, S.P.; and Writing—review and editing, S.P. and O.S.

**Funding:** This work received no external funding.

**Conflicts of Interest:** The authors declare no conflict of interest.

#### **Appendix A. Installation Instruction**

There are several ways to run Julia on your personal computer. One way is to install Julia on the computer and use it in the terminal using the built-in Julia command line. If you have a file in Julia, you can simly run the code from terminal using the command julia "filename.jl" . Another way is to run Julia code in the browser on JuliaBox.com with Jupyter notebooks. For this method, there is no installation required on the personal computer. One more way is to JuliaPro which includes Julia and the Juno IDE (https://juliacomputing.com/products/juliapro.html). This software contains a set of packages for plotting, optimization, machine learning, database, and much more.

Julia setup files can be downloaded from their website (https://julialang.org/downloads/). The website also includes instructions on how to install Julia on Windows, Linux, and mac operating systems. Some of the useful resources for learning Julia are listed below


Oftentimes, one will have to install additional packages to use some of the features in Julia. Pkg is Julia's built in package manager, and it handles operations such as installing, updating and removing packages. We will explain with the help of FTTW library as an example on how to add and use new package. Below are the three commands needed to open Julia using terminal, add new package, and then use that package.

✞ ☎

✝ ✆

```
julia # open julia
Pkg.add("FFTW") # add FFTW package
using FFTW # to use FFTW package
```
#### **Appendix B. Plotting Scripts**

We use PyPlot module in Julia for plotting all results. This module provides a Julia interface to the Matploltlib plotting library from Python. The Python Matplotlib has to be installed in order to use PyPlot package. Once the Matplotlib installed, you can just use Pkg.add("PyPlot") in Julia to install PyPlot and its dependencies. The detailed instruction for installing PyPlot package can be found on https://github.com/JuliaPy/PyPlot.jl.

We mention plotting scripts for two types of plots: XY plot and contour plot. These two plotting scripts are used in this paper to plot all the results. Also, once the student gets familiar with basic plotting scripts, one can easily extend it to more complex plots with examples available on the Internet. Some of the Internet resources that can be helpful for plotting are given below


The scripts for XY plotting and contour plotting are given in Listings A1 and A2, respectively.

```
Listing A1. Plotting script for XY plot. ✞ ☎
```

```