Skip to content
Mikael Mortensen edited this page Sep 16, 2013 · 65 revisions

Taylor flow is a two-dimensional analytical solution to the Navier-Stokes equations. The domain is [0, 2] x [0, 2] and the solution is periodic in both x- and y-directions. An interesting feature of the Taylor Green solution is that the pressure and convective terms exactly balance leaving a viscous decay of the initial solution. The analytical velocities and pressure are given at all times as

Taylor-Green solution

where nu is the kinematic viscosity and t is time.

To implement the Taylor Green problem we need to initialize the solution and set up the periodic boundary conditions. The entire problem file TaylorGreen2D.py reads:

from Oasis import *

# Create a mesh here
mesh = RectangleMesh(0, 0, 2, 2, 20, 20)

# Override some problem specific parameters
NS_parameters.update(dict(
    nu = 0.01,         # Kinematic viscosity
    T = 1,             # End time
    dt = 0.001,        # Timestep
    folder = "taylorgreen2D_results",  # folder for storing results
    checkpoint = 1000, # Overwrite solution in Checkpoint folder each checkpoint tstep
    save_step = 10,    # Store solution in new folder each save_step tstep
    max_iter = 1,      # Number of velocity/pressure iterations on each timestep
    use_krylov_solvers = False, 
    velocity_degree = 2,
    pressure_degree = 1,
  )
)

class PeriodicDomain(SubDomain):
    
    def inside(self, x, on_boundary):
        # True if on left or bottom boundary AND NOT on one of the two corners (0, 1) and (1, 0)
        return bool((near(x[0], 0) or near(x[1], 0)) and 
              (not ((near(x[0], 0) and near(x[1], 2)) or 
                    (near(x[0], 2) and near(x[1], 0)))) and on_boundary)

    def map(self, x, y):
        if near(x[0], 2) and near(x[1], 2):
            y[0] = x[0] - 2.0
            y[1] = x[1] - 2.0
        elif near(x[0], 2):
            y[0] = x[0] - 2.0
            y[1] = x[1]
        else:
            y[0] = x[0]
            y[1] = x[1] - 2.0

constrained_domain = PeriodicDomain()

initial_fields = dict(
    u0='-sin(pi*x[1])*cos(pi*x[0])*exp(-2.*pi*pi*nu*t)',
    u1='sin(pi*x[0])*cos(pi*x[1])*exp(-2.*pi*pi*nu*t)',
    p='-(cos(2*pi*x[0])+cos(2*pi*x[1]))*exp(-4.*pi*pi*nu*t)/4.')
    
def initialize(q_, q_1, q_2, VV, t, nu, dt, **NS_namespace):
    """Initialize solution. 

    Use t=dt/2 for pressure since pressure is computed in between timesteps.

    """
    for ui in q_:
        deltat = dt/2. if ui is 'p' else 0.
        vv = project(Expression((initial_fields[ui]), t=t+deltat, nu=nu), VV[ui])
        q_[ui].vector()[:] = vv.vector()[:]
        if not ui == 'p':
            q_1[ui].vector()[:] = q_[ui].vector()[:]
            q_2[ui].vector()[:] = q_[ui].vector()[:]

def temporal_hook(q_, t, nu, VV, dt, plot_interval, tstep, **NS_namespace):
    """Function called at end of timestep.    

    Plot solution and compute error by comparing to analytical solution.
    Remember pressure is computed in between timesteps.

    """
    if tstep % plot_interval == 0:
        plot(q_['u0'], title='u')
        plot(q_['u1'], title='v')
        plot(q_['p'], title='p')
    err = {}
    for ui in q_:
        deltat = dt/2. if ui is 'p' else 0.
        vv = project(Expression((initial_fields[ui]), t=t-deltat, nu=nu), VV[ui])
        vv.vector().axpy(-1., q_[ui].vector())
        err[ui] = "{0:2.6f}".format(norm(vv.vector()))
    print "Error at time = ", t, " is ", err

Note that this module contains one mesh and one dictionary called NS_parameters. These are required by the Navier Stokes solver. There are more default parameters found in the NSdefault_hooks.py file. A solution that can be used for visualization will be saved every 10'th timestep (the save_step parameter) to a folder called ´taylorgreen2D_results/Timeseries´. There is also the possibility to store intermediate solutions to a Checkpoint folder. Here we store two previous timesteps of the solution vector and as such the solution can be used to obtain a clean restart (since two previous timesteps are required for the convection term). The solution in the Checkpoint folder is owerwritten continuously, so only the save_step option should be used to create animations etc. The solution is always stored together with the NS_parameters dictionary to allow for clean restarts and to remember solution parameters, like timestep, viscosity etc.

The module also contains a SubDomain named constrained_domain that sets up the two periodic directions. The remaining code is initialization and a function called temporal_hook that is called at the end of each timestep. Here we choose to plot the solution and print out the error by comparing to the analytical solution.

The top of the file NavierStokes.py must be modified to load the correct problem, here by entering the text:

from TaylorGreen2D import *

Running the code in IPython leads to:

In [1]: run NavierStokes
Memory use plain dolfin = 79040
  Inner iterations velocity pressure:
                 error u  error p
    Iter =    1, 1.28e-02 5.56e-02
    Iter =    2, 2.43e-03 1.63e-03
Error at time =  0.001  is  {'p': '0.149662', 'u1': '0.004524', 'u0': '0.004524'}
Error at time =  0.002  is  {'p': '0.150265', 'u1': '0.008806', 'u0': '0.008806'}
Error at time =  0.003  is  {'p': '0.150870', 'u1': '0.012861', 'u0': '0.012861'}
Error at time =  0.004  is  {'p': '0.151598', 'u1': '0.016700', 'u0': '0.016700'}
Error at time =  0.005  is  {'p': '0.152295', 'u1': '0.020335', 'u0': '0.020335'}
Error at time =  0.006  is  {'p': '0.152951', 'u1': '0.023776', 'u0': '0.023776'}
Error at time =  0.007  is  {'p': '0.153571', 'u1': '0.027034', 'u0': '0.027034'}
Error at time =  0.008  is  {'p': '0.154156', 'u1': '0.030119', 'u0': '0.030119'}
Error at time =  0.009  is  {'p': '0.154709', 'u1': '0.033039', 'u0': '0.033039'}
...

The pressure solution plotted in ParaView should look something like:

Taylor-Green pressure

You may now experiment with higher order elements, shorter timesteps or more velocity/pressure iterations on each timestep.

Clone this wiki locally