# [전산유체역학] CFD with Python (Navier-Stokes Equation) | Morgan's Blog
1-D Linear Convection 1차원 선형 열전도 방정식은 가장 심플하면서도 가장 기초적인 방정식입니다. $$ \frac{\partial u}{\partial t} + c \frac{\partial u}{\partial x} = 0 $$ 이 식을 오일러 방정식으로 변환하여 수치해석적으로 해를 구할 수 있도록 변환을 해줍니다. $$ u_i^{n+1} = u_i^n - c \frac{\Delta t}{\Delta x}(u_i^n-u_{i-1}^n) $$ 이제 이 오일러 방정식을 파이썬으로 구현해봅니다. import numpy from matplotlib import pyplot import time, sys %matplotlib inline nx = 41 # try changing this number from 41 to 81 and Run All .
Author: Me

# [전산유체역학] CFD with Python (Navier-Stokes Equation)

July 10, 10000 · 4414 words · Me

## 1-D Linear Convection#

1차원 선형 열전도 방정식은 가장 심플하면서도 가장 기초적인 방정식입니다.

$$ \frac{\partial u}{\partial t} + c \frac{\partial u}{\partial x} = 0 $$

이 식을 오일러 방정식으로 변환하여 수치해석적으로 해를 구할 수 있도록 변환을 해줍니다.

$$ u_i^{n+1} = u_i^n - c \frac{\Delta t}{\Delta x}(u_i^n-u_{i-1}^n) $$

이제 이 오일러 방정식을 파이썬으로 구현해봅니다.

    import numpy
    from matplotlib import pyplot
    import time, sys
    %matplotlib inline

    nx = 41  # try changing this number from 41 to 81 and Run All ... what happens?
    dx = 2 / (nx-1)
    nt = 25    #nt is the number of timesteps we want to calculate
    dt = .025  #dt is the amount of time each timestep covers (delta t)
    c = 1      #assume wavespeed of c = 1

    u = numpy.ones(nx)      #numpy function ones()
    u[int(.5 / dx):int(1 / dx + 1)] = 2  #setting u = 2 between 0.5 and 1 as per our I.C.s

    un = numpy.ones(nx) #initialize a temporary array

    for n in range(nt):  #loop for values of n from 0 to nt, so it will run nt times
        un = u.copy() ##copy the existing values of u into un
        for i in range(1, nx): ## you can try commenting this line and...
        #for i in range(nx): ## ... uncommenting this line and see what happens!
            u[i] = un[i] - c * dt / dx * (un[i] - un[i-1])

    pyplot.plot(numpy.linspace(0, 2, nx), u);

## 1-D Convection Equation (Non-Linear)#

$$ \frac{\partial u}{\partial t} + u \frac{\partial u}{\partial x} = 0 $$

$$ u_i^{n+1} = u_i^n - u_i^n \frac{\Delta t}{\Delta x} (u_i^n - u_{i-1}^n) $$

    import numpy                 # we're importing numpy
    from matplotlib import pyplot    # and our 2D plotting library
    %matplotlib inline

    nx = 41
    dx = 2 / (nx - 1)
    nt = 20    #nt is the number of timesteps we want to calculate
    dt = .025  #dt is the amount of time each timestep covers (delta t)

    u = numpy.ones(nx)      #as before, we initialize u with every value equal to 1.
    u[int(.5 / dx) : int(1 / dx + 1)] = 2  #then set u = 2 between 0.5 and 1 as per our I.C.s

    un = numpy.ones(nx) #initialize our placeholder array un, to hold the time-stepped solution

    for n in range(nt):  #iterate through time
        un = u.copy() ##copy the existing values of u into un
        for i in range(1, nx):  ##now we'll iterate through the u array

         ###This is the line from Step 1, copied exactly.  Edit it for our new equation.
         ###then uncomment it and run the cell to evaluate Step 2

               ###u[i] = un[i] - c * dt / dx * (un[i] - un[i-1])

    pyplot.plot(numpy.linspace(0, 2, nx), u) ##Plot the results

## 1-D Diffusion Equation#

$$ \frac{\partial u}{\partial t}= \nu \frac{\partial^2 u}{\partial x^2} $$

$$ u_{i}^{n+1}=u_{i}^{n}+\frac{\nu\Delta t}{\Delta x^2}(u_{i+1}^{n}-2u_{i}^{n}+u_{i-1}^{n}) $$

    import numpy                 #loading our favorite library
    from matplotlib import pyplot    #and the useful plotting library
    %matplotlib inline

    nx = 41
    dx = 2 / (nx - 1)
    nt = 20    #the number of timesteps we want to calculate
    nu = 0.3   #the value of viscosity
    sigma = .2 #sigma is a parameter, we'll learn more about it later
    dt = sigma * dx**2 / nu #dt is defined using sigma ... more later!

    u = numpy.ones(nx)      #a numpy array with nx elements all equal to 1.
    u[int(.5 / dx):int(1 / dx + 1)] = 2  #setting u = 2 between 0.5 and 1 as per our I.C.s

    un = numpy.ones(nx) #our placeholder array, un, to advance the solution in time

    for n in range(nt):  #iterate through time
        un = u.copy() ##copy the existing values of u into un
        for i in range(1, nx - 1):
            u[i] = un[i] + nu * dt / dx**2 * (un[i+1] - 2 * un[i] + un[i-1])

    pyplot.plot(numpy.linspace(0, 2, nx), u);

## Burger’s Equation#

$$ \frac{\partial u}{\partial t} + u \frac{\partial u}{\partial x} = \nu \frac{\partial ^2u}{\partial x^2} $$

$$ u_i^{n+1} = u_i^n - u_i^n \frac{\Delta t}{\Delta x} (u_i^n - u_{i-1}^n) + \nu \frac{\Delta t}{\Delta x^2}(u_{i+1}^n - 2u_i^n + u_{i-1}^n) $$

    import numpy
    import sympy
    from sympy import init_printing
    from matplotlib import pyplot
    from sympy.utilities.lambdify import lambdify

    %matplotlib inline
    init_printing(use_latex=True)

    x, nu, t = sympy.symbols('x nu t')
    phi = (sympy.exp(-(x - 4 * t)**2 / (4 * nu * (t + 1))) +
           sympy.exp(-(x - 4 * t - 2 * sympy.pi)**2 / (4 * nu * (t + 1))))

    phiprime = phi.diff(x)

    u = -2 * nu * (phiprime / phi) + 4
    ufunc = lambdify((t, x, nu), u)

    ###variable declarations
    nx = 101
    nt = 100
    dx = 2 * numpy.pi / (nx - 1)
    nu = .07
    dt = dx * nu

    x = numpy.linspace(0, 2 * numpy.pi, nx)
    un = numpy.empty(nx)
    t = 0

    u = numpy.asarray([ufunc(t, x0, nu) for x0 in x])

    for n in range(nt):
        un = u.copy()
        for i in range(1, nx-1):
            u[i] = un[i] - un[i] * dt / dx *(un[i] - un[i-1]) + nu * dt / dx**2 *\
                    (un[i+1] - 2 * un[i] + un[i-1])
        u[0] = un[0] - un[0] * dt / dx * (un[0] - un[-2]) + nu * dt / dx**2 *\
                    (un[1] - 2 * un[0] + un[-2])
        u[-1] = u[0]

    u_analytical = numpy.asarray([ufunc(nt * dt, xi, nu) for xi in x])

    pyplot.figure(figsize=(11, 7), dpi=100)
    pyplot.plot(x,u, marker='o', lw=2, label='Computational')
    pyplot.plot(x, u_analytical, label='Analytical')
    pyplot.xlim([0, 2 * numpy.pi])
    pyplot.ylim([0, 10])
    pyplot.legend();

## 2-D Linear Convection#

$$ \frac{\partial u}{\partial t}+c\frac{\partial u}{\partial x} + c\frac{\partial u}{\partial y} = 0 $$

$$ u_{i,j}^{n+1} = u_{i,j}^n-c \frac{\Delta t}{\Delta x}(u_{i,j}^n-u_{i-1,j}^n)-c \frac{\Delta t}{\Delta y}(u_{i,j}^n-u_{i,j-1}^n) $$

    from mpl_toolkits.mplot3d import Axes3D    ##New Library required for projected 3d plots

    import numpy
    from matplotlib import pyplot, cm
    %matplotlib inline

    ###variable declarations
    nx = 81
    ny = 81
    nt = 100
    c = 1
    dx = 2 / (nx - 1)
    dy = 2 / (ny - 1)
    sigma = .2
    dt = sigma * dx

    x = numpy.linspace(0, 2, nx)
    y = numpy.linspace(0, 2, ny)

    u = numpy.ones((ny, nx)) ##create a 1xn vector of 1's
    un = numpy.ones((ny, nx)) ##

    ###Assign initial conditions

    ##set hat function I.C. : u(.5<=x<=1 && .5<=y<=1 ) is 2
    u[int(.5 / dy):int(1 / dy + 1),int(.5 / dx):int(1 / dx + 1)] = 2

    ###Plot Initial Condition
    ##the figsize parameter can be used to produce different sized images
    fig = pyplot.figure(figsize=(11, 7), dpi=100)
    ax = fig.gca(projection='3d')
    X, Y = numpy.meshgrid(x, y)
    surf = ax.plot_surface(X, Y, u[:], cmap=cm.viridis)

## 2-D Convection#

$$ \frac{\partial u}{\partial t} + u \frac{\partial u}{\partial x} + v \frac{\partial u}{\partial y} = 0 $$$$ \frac{\partial v}{\partial t} + u \frac{\partial v}{\partial x} + v \frac{\partial v}{\partial y} = 0 $$$$ u_{i,j}^{n+1} = u_{i,j}^n - u_{i,j} \frac{\Delta t}{\Delta x} (u_{i,j}^n-u_{i-1,j}^n) - v_{i,j}^n \frac{\Delta t}{\Delta y} (u_{i,j}^n-u_{i,j-1}^n) $$$$ v_{i,j}^{n+1} = v_{i,j}^n - u_{i,j} \frac{\Delta t}{\Delta x} (v_{i,j}^n-v_{i-1,j}^n) - v_{i,j}^n \frac{\Delta t}{\Delta y} (v_{i,j}^n-v_{i,j-1}^n) $$

    from mpl_toolkits.mplot3d import Axes3D
    from matplotlib import pyplot, cm
    import numpy
    %matplotlib inline

    ###variable declarations
    nx = 101
    ny = 101
    nt = 80
    c = 1
    dx = 2 / (nx - 1)
    dy = 2 / (ny - 1)
    sigma = .2
    dt = sigma * dx

    x = numpy.linspace(0, 2, nx)
    y = numpy.linspace(0, 2, ny)

    u = numpy.ones((ny, nx)) ##create a 1xn vector of 1's
    v = numpy.ones((ny, nx))
    un = numpy.ones((ny, nx))
    vn = numpy.ones((ny, nx))

    ###Assign initial conditions
    ##set hat function I.C. : u(.5<=x<=1 && .5<=y<=1 ) is 2
    u[int(.5 / dy):int(1 / dy + 1), int(.5 / dx):int(1 / dx + 1)] = 2
    ##set hat function I.C. : v(.5<=x<=1 && .5<=y<=1 ) is 2
    v[int(.5 / dy):int(1 / dy + 1), int(.5 / dx):int(1 / dx + 1)] = 2
    fig = pyplot.figure(figsize=(11, 7), dpi=100)
    ax = fig.gca(projection='3d')
    X, Y = numpy.meshgrid(x, y)

    ax.plot_surface(X, Y, u, cmap=cm.viridis, rstride=2, cstride=2)
    ax.set_xlabel('$x$')
    ax.set_ylabel('$y$');

## 2D Diffusion#

$$ \frac{\partial u}{\partial t} = \nu \frac{\partial ^2 u}{\partial x^2} + \nu \frac{\partial ^2 u}{\partial y^2} $$$$ \begin{split}u_{i,j}^{n+1} = u_{i,j}^n &+ \frac{\nu \Delta t}{\Delta x^2}(u_{i+1,j}^n - 2 u_{i,j}^n + u_{i-1,j}^n) \&+ \frac{\nu \Delta t}{\Delta y^2}(u_{i,j+1}^n-2 u_{i,j}^n + u_{i,j-1}^n)\end{split} $$

    import numpy
    from matplotlib import pyplot, cm
    from mpl_toolkits.mplot3d import Axes3D ##library for 3d projection plots
    %matplotlib inline
    ###variable declarations
    nx = 31
    ny = 31
    nt = 17
    nu = .05
    dx = 2 / (nx - 1)
    dy = 2 / (ny - 1)
    sigma = .25
    dt = sigma * dx * dy / nu

    x = numpy.linspace(0, 2, nx)
    y = numpy.linspace(0, 2, ny)

    u = numpy.ones((ny, nx))  # create a 1xn vector of 1's
    un = numpy.ones((ny, nx))

    ###Assign initial conditions
    # set hat function I.C. : u(.5<=x<=1 && .5<=y<=1 ) is 2
    u[int(.5 / dy):int(1 / dy + 1),int(.5 / dx):int(1 / dx + 1)] = 2

    ###Run through nt timesteps
    def diffuse(nt):
        u[int(.5 / dy):int(1 / dy + 1),int(.5 / dx):int(1 / dx + 1)] = 2

        for n in range(nt + 1):
            un = u.copy()
            u[1:-1, 1:-1] = (un[1:-1,1:-1] +
                            nu * dt / dx**2 *
                            (un[1:-1, 2:] - 2 * un[1:-1, 1:-1] + un[1:-1, 0:-2]) +
                            nu * dt / dy**2 *
                            (un[2:,1: -1] - 2 * un[1:-1, 1:-1] + un[0:-2, 1:-1]))
            u[0, :] = 1
            u[-1, :] = 1
            u[:, 0] = 1
            u[:, -1] = 1

        fig = pyplot.figure()
        ax = fig.gca(projection='3d')
        surf = ax.plot_surface(X, Y, u[:], rstride=1, cstride=1, cmap=cm.viridis,
            linewidth=0, antialiased=True)
        ax.set_zlim(1, 2.5)
        ax.set_xlabel('$x$')
        ax.set_ylabel('$y$');

    diffuse(14)

## Burgers’ Equation in 2D#

$$ \frac{\partial u}{\partial t} + u \frac{\partial u}{\partial x} + v \frac{\partial u}{\partial y} = \nu ; \left(\frac{\partial ^2 u}{\partial x^2} + \frac{\partial ^2 u}{\partial y^2}\right) $$$$ \frac{\partial v}{\partial t} + u \frac{\partial v}{\partial x} + v \frac{\partial v}{\partial y} = \nu ; \left(\frac{\partial ^2 v}{\partial x^2} + \frac{\partial ^2 v}{\partial y^2}\right) $$$$ \begin{split}v_{i,j}^{n+1} = & v_{i,j}^n - \frac{\Delta t}{\Delta x} u_{i,j}^n (v_{i,j}^n - v_{i-1,j}^n) - \frac{\Delta t}{\Delta y} v_{i,j}^n (v_{i,j}^n - v_{i,j-1}^n) \&+ \frac{\nu \Delta t}{\Delta x^2}(v_{i+1,j}^n-2v_{i,j}^n+v_{i-1,j}^n) + \frac{\nu \Delta t}{\Delta y^2} (v_{i,j+1}^n - 2v_{i,j}^n + v_{i,j-1}^n)\end{split} $$

    import numpy
    from matplotlib import pyplot, cm
    from mpl_toolkits.mplot3d import Axes3D
    %matplotlib inline
    ###variable declarations
    nx = 41
    ny = 41
    nt = 120
    c = 1
    dx = 2 / (nx - 1)
    dy = 2 / (ny - 1)
    sigma = .0009
    nu = 0.01
    dt = sigma * dx * dy / nu

    x = numpy.linspace(0, 2, nx)
    y = numpy.linspace(0, 2, ny)

    u = numpy.ones((ny, nx))  # create a 1xn vector of 1's
    v = numpy.ones((ny, nx))
    un = numpy.ones((ny, nx))
    vn = numpy.ones((ny, nx))
    comb = numpy.ones((ny, nx))

    ###Assign initial conditions

    ##set hat function I.C. : u(.5<=x<=1 && .5<=y<=1 ) is 2
    u[int(.5 / dy):int(1 / dy + 1),int(.5 / dx):int(1 / dx + 1)] = 2
    ##set hat function I.C. : u(.5<=x<=1 && .5<=y<=1 ) is 2
    v[int(.5 / dy):int(1 / dy + 1),int(.5 / dx):int(1 / dx + 1)] = 2
    ###(plot ICs)
    for n in range(nt + 1): ##loop across number of time steps
        un = u.copy()
        vn = v.copy()

        u[1:-1, 1:-1] = (un[1:-1, 1:-1] -
                         dt / dx * un[1:-1, 1:-1] *
                         (un[1:-1, 1:-1] - un[1:-1, 0:-2]) -
                         dt / dy * vn[1:-1, 1:-1] *
                         (un[1:-1, 1:-1] - un[0:-2, 1:-1]) +
                         nu * dt / dx**2 *
                         (un[1:-1,2:] - 2 * un[1:-1, 1:-1] + un[1:-1, 0:-2]) +
                         nu * dt / dy**2 *
                         (un[2:, 1:-1] - 2 * un[1:-1, 1:-1] + un[0:-2, 1:-1]))

        v[1:-1, 1:-1] = (vn[1:-1, 1:-1] -
                         dt / dx * un[1:-1, 1:-1] *
                         (vn[1:-1, 1:-1] - vn[1:-1, 0:-2]) -
                         dt / dy * vn[1:-1, 1:-1] *
                        (vn[1:-1, 1:-1] - vn[0:-2, 1:-1]) +
                         nu * dt / dx**2 *
                         (vn[1:-1, 2:] - 2 * vn[1:-1, 1:-1] + vn[1:-1, 0:-2]) +
                         nu * dt / dy**2 *
                         (vn[2:, 1:-1] - 2 * vn[1:-1, 1:-1] + vn[0:-2, 1:-1]))

        u[0, :] = 1
        u[-1, :] = 1
        u[:, 0] = 1
        u[:, -1] = 1

        v[0, :] = 1
        v[-1, :] = 1
        v[:, 0] = 1
        v[:, -1] = 1
    fig = pyplot.figure(figsize=(11, 7), dpi=100)
    ax = fig.gca(projection='3d')
    X, Y = numpy.meshgrid(x, y)
    ax.plot_surface(X, Y, u, cmap=cm.viridis, rstride=1, cstride=1)
    ax.plot_surface(X, Y, v, cmap=cm.viridis, rstride=1, cstride=1)
    ax.set_xlabel('$x$')
    ax.set_ylabel('$y$');

## 2D Laplace Equation#

$$ \frac{\partial ^2 p}{\partial x^2} + \frac{\partial ^2 p}{\partial y^2} = 0 $$$$ p_{i,j}^n = \frac{\Delta y^2(p_{i+1,j}^n+p_{i-1,j}^n)+\Delta x^2(p_{i,j+1}^n + p_{i,j-1}^n)}{2(\Delta x^2 + \Delta y^2)} $$

    import numpy
    from matplotlib import pyplot, cm
    from mpl_toolkits.mplot3d import Axes3D
    %matplotlib inline

    def plot2D(x, y, p):
        fig = pyplot.figure(figsize=(11, 7), dpi=100)
        ax = fig.gca(projection='3d')
        X, Y = numpy.meshgrid(x, y)
        surf = ax.plot_surface(X, Y, p[:], rstride=1, cstride=1, cmap=cm.viridis,
                linewidth=0, antialiased=False)
        ax.set_xlim(0, 2)
        ax.set_ylim(0, 1)
        ax.view_init(30, 225)
        ax.set_xlabel('$x$')
        ax.set_ylabel('$y$')

    def laplace2d(p, y, dx, dy, l1norm_target):
      l1norm = 1
      pn = numpy.empty_like(p)

      while l1norm > l1norm_target:
          pn = p.copy()
          p[1:-1, 1:-1] = ((dy**2 * (pn[1:-1, 2:] + pn[1:-1, 0:-2]) +
                            dx**2 * (pn[2:, 1:-1] + pn[0:-2, 1:-1])) /
                          (2 * (dx**2 + dy**2)))

          p[:, 0] = 0  # p = 0 @ x = 0
          p[:, -1] = y  # p = y @ x = 2
          p[0, :] = p[1, :]  # dp/dy = 0 @ y = 0
          p[-1, :] = p[-2, :]  # dp/dy = 0 @ y = 1
          l1norm = (numpy.sum(numpy.abs(p[:]) - numpy.abs(pn[:])) /
                  numpy.sum(numpy.abs(pn[:])))

      return p

    nx = 31
    ny = 31
    c = 1
    dx = 2 / (nx - 1)
    dy = 2 / (ny - 1)

    ##initial conditions
    p = numpy.zeros((ny, nx))  # create a XxY vector of 0's

    ##plotting aids
    x = numpy.linspace(0, 2, nx)
    y = numpy.linspace(0, 1, ny)

    ##boundary conditions
    p[:, 0] = 0  # p = 0 @ x = 0
    p[:, -1] = y  # p = y @ x = 2
    p[0, :] = p[1, :]  # dp/dy = 0 @ y = 0
    p[-1, :] = p[-2, :]  # dp/dy = 0 @ y = 1

    p = laplace2d(p, y, dx, dy, 1e-4)

    plot2D(x, y, p)

## 2D Poisson Equation#

$$ \frac{\partial ^2 p}{\partial x^2} + \frac{\partial ^2 p}{\partial y^2} = b $$$$ p_{i,j}^{n}=\frac{(p_{i+1,j}^{n}+p_{i-1,j}^{n})\Delta y^2+(p_{i,j+1}^{n}+p_{i,j-1}^{n})\Delta x^2-b_{i,j}^{n}\Delta x^2\Delta y^2}{2(\Delta x^2+\Delta y^2)} $$

    import numpy
    from matplotlib import pyplot, cm
    from mpl_toolkits.mplot3d import Axes3D
    %matplotlib inline
    # Parameters
    nx = 50
    ny = 50
    nt  = 100
    xmin = 0
    xmax = 2
    ymin = 0
    ymax = 1

    dx = (xmax - xmin) / (nx - 1)
    dy = (ymax - ymin) / (ny - 1)

    # Initialization
    p  = numpy.zeros((ny, nx))
    pd = numpy.zeros((ny, nx))
    b  = numpy.zeros((ny, nx))
    x  = numpy.linspace(xmin, xmax, nx)
    y  = numpy.linspace(xmin, xmax, ny)

    # Source
    b[int(ny / 4), int(nx / 4)]  = 100
    b[int(3 * ny / 4), int(3 * nx / 4)] = -100

    for it in range(nt):

        pd = p.copy()

        p[1:-1,1:-1] = (((pd[1:-1, 2:] + pd[1:-1, :-2]) * dy**2 +
                        (pd[2:, 1:-1] + pd[:-2, 1:-1]) * dx**2 -
                        b[1:-1, 1:-1] * dx**2 * dy**2) /
                        (2 * (dx**2 + dy**2)))

        p[0, :] = 0
        p[ny-1, :] = 0
        p[:, 0] = 0
        p[:, nx-1] = 0

    def plot2D(x, y, p):
        fig = pyplot.figure(figsize=(11, 7), dpi=100)
        ax = fig.gca(projection='3d')
        X, Y = numpy.meshgrid(x, y)
        surf = ax.plot_surface(X, Y, p[:], rstride=1, cstride=1, cmap=cm.viridis,
                linewidth=0, antialiased=False)
        ax.view_init(30, 225)
        ax.set_xlabel('$x$')
        ax.set_ylabel('$y$')

    plot2D(x, y, p)

## #

## Cavity Flow with Navier–Stokes#

$$ \frac{\partial \vec{v}}{\partial t}+(\vec{v}\cdot\nabla)\vec{v}=-\frac{1}{\rho}\nabla p + \nu \nabla^2\vec{v} $$$$ \frac{\partial u}{\partial t}+u\frac{\partial u}{\partial x}+v\frac{\partial u}{\partial y} = -\frac{1}{\rho}\frac{\partial p}{\partial x}+\nu \left(\frac{\partial^2 u}{\partial x^2}+\frac{\partial^2 u}{\partial y^2} \right) $$$$ \frac{\partial^2 p}{\partial x^2}+\frac{\partial^2 p}{\partial y^2} = -\rho\left(\frac{\partial u}{\partial x}\frac{\partial u}{\partial x}+2\frac{\partial u}{\partial y}\frac{\partial v}{\partial x}+\frac{\partial v}{\partial y}\frac{\partial v}{\partial y} \right) $$$$ \begin{split}p_{i,j}^{n} = & \frac{\left(p_{i+1,j}^{n}+p_{i-1,j}^{n}\right) \Delta y^2 + \left(p_{i,j+1}^{n}+p_{i,j-1}^{n}\right) \Delta x^2}{2\left(\Delta x^2+\Delta y^2\right)} \& -\frac{\rho\Delta x^2\Delta y^2}{2\left(\Delta x^2+\Delta y^2\right)} \& \times \left[\frac{1}{\Delta t}\left(\frac{u_{i+1,j}-u_{i-1,j}}{2\Delta x}+\frac{v_{i,j+1}-v_{i,j-1}}{2\Delta y}\right)-\frac{u_{i+1,j}-u_{i-1,j}}{2\Delta x}\frac{u_{i+1,j}-u_{i-1,j}}{2\Delta x} -2\frac{u_{i,j+1}-u_{i,j-1}}{2\Delta y}\frac{v_{i+1,j}-v_{i-1,j}}{2\Delta x}-\frac{v_{i,j+1}-v_{i,j-1}}{2\Delta y}\frac{v_{i,j+1}-v_{i,j-1}}{2\Delta y}\right]\end{split} $$

    import numpy
    from matplotlib import pyplot, cm
    from mpl_toolkits.mplot3d import Axes3D
    %matplotlib inline
    nx = 41
    ny = 41
    nt = 500
    nit = 50
    c = 1
    dx = 2 / (nx - 1)
    dy = 2 / (ny - 1)
    x = numpy.linspace(0, 2, nx)
    y = numpy.linspace(0, 2, ny)
    X, Y = numpy.meshgrid(x, y)

    rho = 1
    nu = .1
    dt = .001

    u = numpy.zeros((ny, nx))
    v = numpy.zeros((ny, nx))
    p = numpy.zeros((ny, nx))
    b = numpy.zeros((ny, nx))

    def build_up_b(b, rho, dt, u, v, dx, dy):

        b[1:-1, 1:-1] = (rho * (1 / dt *
                        ((u[1:-1, 2:] - u[1:-1, 0:-2]) /
                         (2 * dx) + (v[2:, 1:-1] - v[0:-2, 1:-1]) / (2 * dy)) -
                        ((u[1:-1, 2:] - u[1:-1, 0:-2]) / (2 * dx))**2 -
                          2 * ((u[2:, 1:-1] - u[0:-2, 1:-1]) / (2 * dy) *
                               (v[1:-1, 2:] - v[1:-1, 0:-2]) / (2 * dx))-
                              ((v[2:, 1:-1] - v[0:-2, 1:-1]) / (2 * dy))**2))

        return b

    def pressure_poisson(p, dx, dy, b):
        pn = numpy.empty_like(p)
        pn = p.copy()

        for q in range(nit):
            pn = p.copy()
            p[1:-1, 1:-1] = (((pn[1:-1, 2:] + pn[1:-1, 0:-2]) * dy**2 +
                              (pn[2:, 1:-1] + pn[0:-2, 1:-1]) * dx**2) /
                              (2 * (dx**2 + dy**2)) -
                              dx**2 * dy**2 / (2 * (dx**2 + dy**2)) *
                              b[1:-1,1:-1])

            p[:, -1] = p[:, -2] # dp/dx = 0 at x = 2
            p[0, :] = p[1, :]   # dp/dy = 0 at y = 0
            p[:, 0] = p[:, 1]   # dp/dx = 0 at x = 0
            p[-1, :] = 0        # p = 0 at y = 2

        return p

    def cavity_flow(nt, u, v, dt, dx, dy, p, rho, nu):
        un = numpy.empty_like(u)
        vn = numpy.empty_like(v)
        b = numpy.zeros((ny, nx))

        for n in range(nt):
            un = u.copy()
            vn = v.copy()

            b = build_up_b(b, rho, dt, u, v, dx, dy)
            p = pressure_poisson(p, dx, dy, b)

            u[1:-1, 1:-1] = (un[1:-1, 1:-1]-
                             un[1:-1, 1:-1] * dt / dx *
                            (un[1:-1, 1:-1] - un[1:-1, 0:-2]) -
                             vn[1:-1, 1:-1] * dt / dy *
                            (un[1:-1, 1:-1] - un[0:-2, 1:-1]) -
                             dt / (2 * rho * dx) * (p[1:-1, 2:] - p[1:-1, 0:-2]) +
                             nu * (dt / dx**2 *
                            (un[1:-1, 2:] - 2 * un[1:-1, 1:-1] + un[1:-1, 0:-2]) +
                             dt / dy**2 *
                            (un[2:, 1:-1] - 2 * un[1:-1, 1:-1] + un[0:-2, 1:-1])))

            v[1:-1,1:-1] = (vn[1:-1, 1:-1] -
                            un[1:-1, 1:-1] * dt / dx *
                           (vn[1:-1, 1:-1] - vn[1:-1, 0:-2]) -
                            vn[1:-1, 1:-1] * dt / dy *
                           (vn[1:-1, 1:-1] - vn[0:-2, 1:-1]) -
                            dt / (2 * rho * dy) * (p[2:, 1:-1] - p[0:-2, 1:-1]) +
                            nu * (dt / dx**2 *
                           (vn[1:-1, 2:] - 2 * vn[1:-1, 1:-1] + vn[1:-1, 0:-2]) +
                            dt / dy**2 *
                           (vn[2:, 1:-1] - 2 * vn[1:-1, 1:-1] + vn[0:-2, 1:-1])))

            u[0, :]  = 0
            u[:, 0]  = 0
            u[:, -1] = 0
            u[-1, :] = 1    # set velocity on cavity lid equal to 1
            v[0, :]  = 0
            v[-1, :] = 0
            v[:, 0]  = 0
            v[:, -1] = 0

        return u, v, p

    u = numpy.zeros((ny, nx))
    v = numpy.zeros((ny, nx))
    p = numpy.zeros((ny, nx))
    b = numpy.zeros((ny, nx))
    nt = 100
    u, v, p = cavity_flow(nt, u, v, dt, dx, dy, p, rho, nu)
    fig = pyplot.figure(figsize=(11,7), dpi=100)
    # plotting the pressure field as a contour
    pyplot.contourf(X, Y, p, alpha=0.5, cmap=cm.viridis)
    pyplot.colorbar()
    # plotting the pressure field outlines
    pyplot.contour(X, Y, p, cmap=cm.viridis)
    # plotting velocity field
    pyplot.quiver(X[::2, ::2], Y[::2, ::2], u[::2, ::2], v[::2, ::2])
    pyplot.xlabel('X')
    pyplot.ylabel('Y');

    u = numpy.zeros((ny, nx))
    v = numpy.zeros((ny, nx))
    p = numpy.zeros((ny, nx))
    b = numpy.zeros((ny, nx))
    nt = 700
    u, v, p = cavity_flow(nt, u, v, dt, dx, dy, p, rho, nu)
    fig = pyplot.figure(figsize=(11, 7), dpi=100)
    pyplot.contourf(X, Y, p, alpha=0.5, cmap=cm.viridis)
    pyplot.colorbar()
    pyplot.contour(X, Y, p, cmap=cm.viridis)
    pyplot.quiver(X[::2, ::2], Y[::2, ::2], u[::2, ::2], v[::2, ::2])
    pyplot.xlabel('X')
    pyplot.ylabel('Y');

    fig = pyplot.figure(figsize=(11, 7), dpi=100)
    pyplot.contourf(X, Y, p, alpha=0.5, cmap=cm.viridis)
    pyplot.colorbar()
    pyplot.contour(X, Y, p, cmap=cm.viridis)
    pyplot.streamplot(X, Y, u, v)
    pyplot.xlabel('X')
    pyplot.ylabel('Y');

## Channel Flow with Navier–Stokes#

$$ \frac{\partial u}{\partial t}+u\frac{\partial u}{\partial x}+v\frac{\partial u}{\partial y}=-\frac{1}{\rho}\frac{\partial p}{\partial x}+\nu\left(\frac{\partial^2 u}{\partial x^2}+\frac{\partial^2 u}{\partial y^2}\right)+F $$$$ \frac{\partial^2 p}{\partial x^2}+\frac{\partial^2 p}{\partial y^2}=-\rho\left(\frac{\partial u}{\partial x}\frac{\partial u}{\partial x}+2\frac{\partial u}{\partial y}\frac{\partial v}{\partial x}+\frac{\partial v}{\partial y}\frac{\partial v}{\partial y}\right) $$$$ \begin{split}p_{i,j}^{n} = & \frac{\left(p_{i+1,j}^{n}+p_{i-1,j}^{n}\right) \Delta y^2 + \left(p_{i,j+1}^{n}+p_{i,j-1}^{n}\right) \Delta x^2}{2(\Delta x^2+\Delta y^2)} \& -\frac{\rho\Delta x^2\Delta y^2}{2\left(\Delta x^2+\Delta y^2\right)} \& \times \left[\frac{1}{\Delta t} \left(\frac{u_{i+1,j}-u_{i-1,j}}{2\Delta x} + \frac{v_{i,j+1}-v_{i,j-1}}{2\Delta y}\right) - \frac{u_{i+1,j}-u_{i-1,j}}{2\Delta x}\frac{u_{i+1,j}-u_{i-1,j}}{2\Delta x} - 2\frac{u_{i,j+1}-u_{i,j-1}}{2\Delta y}\frac{v_{i+1,j}-v_{i-1,j}}{2\Delta x} - \frac{v_{i,j+1}-v_{i,j-1}}{2\Delta y}\frac{v_{i,j+1}-v_{i,j-1}}{2\Delta y}\right]\end{split} $$

    import numpy
    from matplotlib import pyplot, cm
    from mpl_toolkits.mplot3d import Axes3D
    %matplotlib inline

    def build_up_b(rho, dt, dx, dy, u, v):
        b = numpy.zeros_like(u)
        b[1:-1, 1:-1] = (rho * (1 / dt * ((u[1:-1, 2:] - u[1:-1, 0:-2]) / (2 * dx) +
                                          (v[2:, 1:-1] - v[0:-2, 1:-1]) / (2 * dy)) -
                                ((u[1:-1, 2:] - u[1:-1, 0:-2]) / (2 * dx))**2 -
                                2 * ((u[2:, 1:-1] - u[0:-2, 1:-1]) / (2 * dy) *
                                     (v[1:-1, 2:] - v[1:-1, 0:-2]) / (2 * dx))-
                                ((v[2:, 1:-1] - v[0:-2, 1:-1]) / (2 * dy))**2))

        # Periodic BC Pressure @ x = 2
        b[1:-1, -1] = (rho * (1 / dt * ((u[1:-1, 0] - u[1:-1,-2]) / (2 * dx) +
                                        (v[2:, -1] - v[0:-2, -1]) / (2 * dy)) -
                              ((u[1:-1, 0] - u[1:-1, -2]) / (2 * dx))**2 -
                              2 * ((u[2:, -1] - u[0:-2, -1]) / (2 * dy) *
                                   (v[1:-1, 0] - v[1:-1, -2]) / (2 * dx)) -
                              ((v[2:, -1] - v[0:-2, -1]) / (2 * dy))**2))

        # Periodic BC Pressure @ x = 0
        b[1:-1, 0] = (rho * (1 / dt * ((u[1:-1, 1] - u[1:-1, -1]) / (2 * dx) +
                                       (v[2:, 0] - v[0:-2, 0]) / (2 * dy)) -
                             ((u[1:-1, 1] - u[1:-1, -1]) / (2 * dx))**2 -
                             2 * ((u[2:, 0] - u[0:-2, 0]) / (2 * dy) *
                                  (v[1:-1, 1] - v[1:-1, -1]) / (2 * dx))-
                             ((v[2:, 0] - v[0:-2, 0]) / (2 * dy))**2))

        return b

    def pressure_poisson_periodic(p, dx, dy):
        pn = numpy.empty_like(p)

        for q in range(nit):
            pn = p.copy()
            p[1:-1, 1:-1] = (((pn[1:-1, 2:] + pn[1:-1, 0:-2]) * dy**2 +
                              (pn[2:, 1:-1] + pn[0:-2, 1:-1]) * dx**2) /
                             (2 * (dx**2 + dy**2)) -
                             dx**2 * dy**2 / (2 * (dx**2 + dy**2)) * b[1:-1, 1:-1])

            # Periodic BC Pressure @ x = 2
            p[1:-1, -1] = (((pn[1:-1, 0] + pn[1:-1, -2])* dy**2 +
                            (pn[2:, -1] + pn[0:-2, -1]) * dx**2) /
                           (2 * (dx**2 + dy**2)) -
                           dx**2 * dy**2 / (2 * (dx**2 + dy**2)) * b[1:-1, -1])

            # Periodic BC Pressure @ x = 0
            p[1:-1, 0] = (((pn[1:-1, 1] + pn[1:-1, -1])* dy**2 +
                           (pn[2:, 0] + pn[0:-2, 0]) * dx**2) /
                          (2 * (dx**2 + dy**2)) -
                          dx**2 * dy**2 / (2 * (dx**2 + dy**2)) * b[1:-1, 0])

            # Wall boundary conditions, pressure
            p[-1, :] =p[-2, :]  # dp/dy = 0 at y = 2
            p[0, :] = p[1, :]  # dp/dy = 0 at y = 0

        return p

    ##variable declarations
    nx = 41
    ny = 41
    nt = 10
    nit = 50
    c = 1
    dx = 2 / (nx - 1)
    dy = 2 / (ny - 1)
    x = numpy.linspace(0, 2, nx)
    y = numpy.linspace(0, 2, ny)
    X, Y = numpy.meshgrid(x, y)

    ##physical variables
    rho = 1
    nu = .1
    F = 1
    dt = .01

    #initial conditions
    u = numpy.zeros((ny, nx))
    un = numpy.zeros((ny, nx))

    v = numpy.zeros((ny, nx))
    vn = numpy.zeros((ny, nx))

    p = numpy.ones((ny, nx))
    pn = numpy.ones((ny, nx))

    b = numpy.zeros((ny, nx))

    udiff = 1
    stepcount = 0

    while udiff > .001:
        un = u.copy()
        vn = v.copy()

        b = build_up_b(rho, dt, dx, dy, u, v)
        p = pressure_poisson_periodic(p, dx, dy)

        u[1:-1, 1:-1] = (un[1:-1, 1:-1] -
                         un[1:-1, 1:-1] * dt / dx *
                        (un[1:-1, 1:-1] - un[1:-1, 0:-2]) -
                         vn[1:-1, 1:-1] * dt / dy *
                        (un[1:-1, 1:-1] - un[0:-2, 1:-1]) -
                         dt / (2 * rho * dx) *
                        (p[1:-1, 2:] - p[1:-1, 0:-2]) +
                         nu * (dt / dx**2 *
                        (un[1:-1, 2:] - 2 * un[1:-1, 1:-1] + un[1:-1, 0:-2]) +
                         dt / dy**2 *
                        (un[2:, 1:-1] - 2 * un[1:-1, 1:-1] + un[0:-2, 1:-1])) +
                         F * dt)

        v[1:-1, 1:-1] = (vn[1:-1, 1:-1] -
                         un[1:-1, 1:-1] * dt / dx *
                        (vn[1:-1, 1:-1] - vn[1:-1, 0:-2]) -
                         vn[1:-1, 1:-1] * dt / dy *
                        (vn[1:-1, 1:-1] - vn[0:-2, 1:-1]) -
                         dt / (2 * rho * dy) *
                        (p[2:, 1:-1] - p[0:-2, 1:-1]) +
                         nu * (dt / dx**2 *
                        (vn[1:-1, 2:] - 2 * vn[1:-1, 1:-1] + vn[1:-1, 0:-2]) +
                         dt / dy**2 *
                        (vn[2:, 1:-1] - 2 * vn[1:-1, 1:-1] + vn[0:-2, 1:-1])))

        # Periodic BC u @ x = 2
        u[1:-1, -1] = (un[1:-1, -1] - un[1:-1, -1] * dt / dx *
                      (un[1:-1, -1] - un[1:-1, -2]) -
                       vn[1:-1, -1] * dt / dy *
                      (un[1:-1, -1] - un[0:-2, -1]) -
                       dt / (2 * rho * dx) *
                      (p[1:-1, 0] - p[1:-1, -2]) +
                       nu * (dt / dx**2 *
                      (un[1:-1, 0] - 2 * un[1:-1,-1] + un[1:-1, -2]) +
                       dt / dy**2 *
                      (un[2:, -1] - 2 * un[1:-1, -1] + un[0:-2, -1])) + F * dt)

        # Periodic BC u @ x = 0
        u[1:-1, 0] = (un[1:-1, 0] - un[1:-1, 0] * dt / dx *
                     (un[1:-1, 0] - un[1:-1, -1]) -
                      vn[1:-1, 0] * dt / dy *
                     (un[1:-1, 0] - un[0:-2, 0]) -
                      dt / (2 * rho * dx) *
                     (p[1:-1, 1] - p[1:-1, -1]) +
                      nu * (dt / dx**2 *
                     (un[1:-1, 1] - 2 * un[1:-1, 0] + un[1:-1, -1]) +
                      dt / dy**2 *
                     (un[2:, 0] - 2 * un[1:-1, 0] + un[0:-2, 0])) + F * dt)

        # Periodic BC v @ x = 2
        v[1:-1, -1] = (vn[1:-1, -1] - un[1:-1, -1] * dt / dx *
                      (vn[1:-1, -1] - vn[1:-1, -2]) -
                       vn[1:-1, -1] * dt / dy *
                      (vn[1:-1, -1] - vn[0:-2, -1]) -
                       dt / (2 * rho * dy) *
                      (p[2:, -1] - p[0:-2, -1]) +
                       nu * (dt / dx**2 *
                      (vn[1:-1, 0] - 2 * vn[1:-1, -1] + vn[1:-1, -2]) +
                       dt / dy**2 *
                      (vn[2:, -1] - 2 * vn[1:-1, -1] + vn[0:-2, -1])))

        # Periodic BC v @ x = 0
        v[1:-1, 0] = (vn[1:-1, 0] - un[1:-1, 0] * dt / dx *
                     (vn[1:-1, 0] - vn[1:-1, -1]) -
                      vn[1:-1, 0] * dt / dy *
                     (vn[1:-1, 0] - vn[0:-2, 0]) -
                      dt / (2 * rho * dy) *
                     (p[2:, 0] - p[0:-2, 0]) +
                      nu * (dt / dx**2 *
                     (vn[1:-1, 1] - 2 * vn[1:-1, 0] + vn[1:-1, -1]) +
                      dt / dy**2 *
                     (vn[2:, 0] - 2 * vn[1:-1, 0] + vn[0:-2, 0])))

        # Wall BC: u,v = 0 @ y = 0,2
        u[0, :] = 0
        u[-1, :] = 0
        v[0, :] = 0
        v[-1, :]=0

        udiff = (numpy.sum(u) - numpy.sum(un)) / numpy.sum(u)
        stepcount += 1

    fig = pyplot.figure(figsize = (11,7), dpi=100)
    pyplot.quiver(X[::3, ::3], Y[::3, ::3], u[::3, ::3], v[::3, ::3]);

출처> CFD Python: 12 steps to Navier-Stokes :: Lorena A. Barba Group (lorenabarba.com)