Equação de Burgers

De Física Computacional
Ir para navegação Ir para pesquisar

Grupo: Eduardo Pedroso, Luis Gustavo Lang Gaiato e William Machado Pantaleão

Um dos maiores desafios no campo dos sistemas complexos é a compreensão do fenômeno de turbulência. Simulações computacionais contribuíram bastante para o entendimento dessa área, no entanto, ainda não existe nenhuma teoria que explique com sucesso esse comportamento e permita prever outros importantes fenômenos como misturas, convecção e combustão turbulentas, com base nas equações fundamentais da dinâmica de fluidos. Isso se deve ao fato de que a equação para os fluidos mais simples (incompressíveis) já deve levar em consideração propriedades não lineares [1]. Da equação de Navier–Stokes:

Devido à incompressibilidade, a pressão é definida pela equação de Poisson:

Em 1939 o cientista alemão Johannes Martinus Burgers simplificou a equação de Navier-Stokes, removendo o termo de pressão. A equação ficou conhecida como equação de Burgers. Em uma dimensão, onde corresponde ao campo de velocidades e ao coeficiente de difusão:

A equação de Burgers é não linear e, portanto, espera-se um comportamento similar ao da turbulência. No entanto, foi demonstrado posteriormente que a equação de Burgers homogênea, não possuí a propriedade mais importante atribuída ao fenômeno de turbulência: o comportamento caótico em relação à pequenas mudanças nas condições iniciais. Utilizando a transformação de Hopf-Cole, que transforma a equação de Burgers em uma equação parabólica linear é possível observar essa característica [2].

Do ponto de vista numérico, isso é bastante importante, pois permite a comparação das soluções da equação não linear numérica com o resultado analítico. Dessa forma, pode-se investigar a qualidade do método numérico utilizado.

A transformação de Hopf-Cole

A transformação de Hopf-cole mapeia a solução da equação de Burgers na equação do calor [2] [3] [4]:

Escrevendo a equação, para uma certa condição inicial :

Assim, reescrevendo a equação:

Busca-se uma solução que satisfaça:

Como:

Tem-se, que:

Aplicando a transformação de Hopf-Cole:

Assim, os seguintes resultados são obtidos:

Dessa forma:

A equação se torna a própria equação de difusão. É necessário, no entanto, transformar também as condições de contorno; assim:

Podendo ser reescrito como:

Cuja solução:

Dessa forma, é preciso resolver:

Utilizando a transformada de Fourier:

Cuja solução:

Aplicando o teorema da convolução:

Onde:

Em :

Onde:

Modelo de Deposição - Crescimento de Interfaces

Figura 1: Modelo de deposição balística (pertencente à família dos modelos KPZ). O modelo é como um tetris onde os blocos caem e se fixam no primeiro contato [5].

Um exemplo de aplicação é no crescimento de interfaces por deposição, já que a equação de Burgers é equivalente a equação conhecida como, equação de Kardar-Parisi-Zhang (equação KPZ), um modelo de crescimento de uma superfície sólida por deposição de vapor (ou erosão de material de uma superfície sólida), que mostra a evolução da altura da camada com o tempo [6].

A equação é obtida a partir da equação de advecção simples para uma superfície se movimentando com velocidade :

A velocidade é assumida como sendo proporcional ao gradiente de (superfície evolui na direção do gradiente). A difusão da superfície é descrita pelo termo de difusão.

A equação KPZ unidimensional corresponde a versão estocástica da equação de Burgers com campo pela substituição .

Solução de Onda Viajante

Pode-se ainda, encontrar uma solução para a equação de Burgers na forma de uma onda viajante. Assim:

Dessa forma:

Substituindo na equação de Burgers:

Impondo condições em , tais que:

Onde . Dessa forma, tem-se que:

Desse modo, a velocidade de choque é a mesma para o caso de viscosidade nula . Continuando:

Integrando ambas as partes, utilizando que:

Utilizando o fato de que :

Portanto:

Onde:

Multiplicando e dividindo por :

Dessa forma:

Portanto:

Gráfico

O perfil da função pode ser plotado para diferentes viscosidades. Sabe-se, que conforme a viscosidade se aproxima de zero (fluido invíscido) a função se aproxima de uma função degrau de argumento (ver Apêndice).

Figura 2: Os perfis da solução da equação de Burgers víscida para, uD=0, uE=1, x0=0 e ν variando de 0.4 a 0.02 em 50 passos. Nota-se, que a função se aproxima de uma função degrau quando ν->0.

O gráfico pode ser construido utilizando o código em Python abaixo.

# -*- coding: utf-8 -*-
import matplotlib.pyplot as plt
import matplotlib.cm as cm
from matplotlib.animation import FuncAnimation
from matplotlib.lines import Line2D
import numpy as np
from numpy import ndarray
from typing import Any

u, v, x0 = 0, 1, 0 # define-se os parametros iniciais u=u_D, v=u_E

def w(y:ndarray, nu:float): # funcao w(y)
    arg = (y*(v-u))/(4*nu)
    return((u+v)/2 - ((v-u)/2) *np.tanh(arg))

fig = plt.figure(figsize=(12.8, 3.7), dpi=80) # resolucao da gráfico
ax = plt.axes(xlim=(-2, 2), ylim=(0, 1), # características dos eixos
              xlabel=r'$y$', ylabel=r'$w(y)$')
line, = ax.plot([], [], lw=3) # cria-se um obejto 'Line' de 2D
plt.title('Perfis da Equação '+r'$w(y)$'+ # titulo do grafico
          ' para Diferentes Viscosidades')

def init(): # funcao que inicia a animacao caso o frame=0 seja vazio
    line.set_data([], [])
    return line,

def animate(frame, *fargs: Any) -> tuple[Line2D]: # função que plota os gráficos
    c = 0 # inicializa a variavel
    y = np.linspace(-2,2,100) # cria um array de valores para y
    nu_list = np.linspace(0.4,0.02,50) # cria um array de valores de viscosidade
    w_arr = w(y, nu_list[frame]) # cria o array de w(y, nu)
    colour = iter(cm.rainbow(np.linspace(0,2,110))) # iteravel utilizado para as cores
    
    for k in range(frame + 1): # avanca o objeto iteravel em (frame + 1) vezes
        c = next(colour)
    plt.plot(y, w_arr, color=c) # faz com que as linhas sejam mantidas entre frames
    ax.text(1, 0.9, r'$\nu = $'+f'{nu_list[frame]:.3f}', fontsize=15, # adiciona nu
            bbox=dict(edgecolor='white', facecolor='white', alpha=1))
    line.set_color(c) # define a cor da linha com base no iteravel atualizado
    line.set_data(y, w_arr) # define o novo conjunto de dados
    return(line,) # retorna o objeto iteravel [Linha2D]

anim = FuncAnimation(fig, animate, init_func=init, # funcao que produz a animacao
                     frames=50, interval=100, blit=True) # 50 frames com intervalo de 0.1s
anim.save('shockwaveee.gif', writer='imagemagick') # gera um .GIF (e renderizacao)

Velocidade de Choque

Tomando a viscosidade como nula, a equação de Burgers pode ser reescrita como[7]:

Cuja solução é dada na forma e uma onda viajante:

Onde é uma função degrau:

Onde . Essa onda é chamada de onda de choque, e possui velocidade de propagação . Por conservação, a condição de salto:

Por outro lado:

Portanto:

Dessa forma:

O caso generalizado da velocidade de propagação de uma onda de choque é conhecido como condição de Rankine-Hugoniot, e pode ser obtido, desse resultado, pela aplicação das leis de conservação hiperbólicas:

Assim:

A condição de Rankine-Hugoniot, onde o salto da função é definido como choque.

Métodos Numéricos

Discretizando a equação de Burgers para um fluido víscido, a partir da abordagem FTCS (Foward Time Central Space), com a derivada à direita:

Se:

Conservação

Um dos problemas no cálculo de soluções descontinuas para equações como a de Burgers pode ser exemplificado da seguinte forma; supondo a equação de Burgers na forma quasi-linear com viscosidade nula:[7]

Com condições iniciais:

Pela discretização feita anteriormente:

Pelas condições iniciais, para e para :

Como resultado, . Dessa forma, ; isso significa que o método propaga a descontinuidade (onda de choque) com a velocidade incorreta, ; ou seja, não ocorre propagação após o choque.

Método Conservativo

A partir da discretização para o caso víscido, se , então:

Assim como para o caso anterior, esse método não é conservativo e é adequado apenas para soluções que não possuem descontinuidades. Considerando a lei de conservação:

Discretizando:

Onde a função corresponde à uma função de argumentos, chamada de fluxo numérico [7] [8] [9]. Assim, para e , :

Lax-Friedrichs

O método de Lax-Friedrichs para sistemas não lineares possui a forma:

Para o caso da equação de Burgers invíscida:

Lax-Wendroff

O método de Lax-Wendroff é um método de segunda ordem e possui a forma [9][8]:

Onde corresponde à matriz jacobiana , avaliada em . Para a equação de Burgers (invíscida) , assim:

Método parabólico

Considerando a equação de Burgers víscida com , na forma [8]:

Integrando de até :

Aproximando-se cada termo da equação acima tem-se que, para o primeiro termo:

Para o segundo:

Para o terceiro:

Substituindo as aproximações na expressão completa e definindo :

Discretizando a derivada em relação a tempo por um método FTCS:

Onde é calculada numericamente como a média entre e .

Implementação

Foram implementados quatro métodos: para o caso víscido, FTCS não conservativo e o método parabólico; para o caso invíscido, FTCS não conservativo e FTCS conservativo. Definindo a condição de contorno periódica , e utilizando as condições iniciais:

Caso 1:

Caso 2:

Derivando e substituindo na equação:

Cuja solução analítica é

Derivando e substituindo na equação:

Importando os Módulos Utilizados

# -*- coding: utf-8 -*-
from math import pi, exp
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.cm as cm

Array2D = np.ndarray # typing...

Burgers FTCS Víscido Não Conservativo

def burgers(nt:int, nx:int, tmax:float, xmax:float, v:float, caso:int) -> tuple:
    # incrementos
    dt = tmax/(nt-1) # no tempo
    dx = xmax/(nx-1) # no espaco

    # arrays
    u = np.zeros((nx, nt)) # velocidade
    x = np.zeros(nx)
    ipos = np.zeros(nx) 
    ineg = np.zeros(nx) 

    # condicoes de contorno periodicas
    for i in range(0, nx): # i:     0,  1, ... nx-2, nx-1, nx
        x[i] = i*dx
        ipos[i] = i+1 # 1, 2, ... nx-2, nx+1
        ineg[i] = i-1 # -1, 0, ... nx-2, nx-1

    ipos[-1] = 0 # ipos[nx-1] = 0, u_{n+1}
    ineg[0] = nx-1 # u_{n-1}

    # condicoes iniciais
    if caso == 1:
      for i in range(0,nx):
        if x[i] <= 1:
            u[i,0] = 1
        elif x[i] > 1:
            u[i,0] = 0
    elif caso == 2:
      for i in range(0, nx):
          phi = exp( -(x[i]**2)/(4*v) ) + exp( -(x[i]-2*pi)**2 / (4*v) )
          dphi = (-(0.5*x[i]/v)*exp( -(x[i]**2) / (4*v) ) - (0.5*(x[i]-2*pi) / v )
                  *exp(-(x[i]-2*pi)**2 / (4*v) ))
          u[i,0] = -2*v*(dphi/phi) + 4

    # solucao numerica
    for n in range(0, nt-1):
        for i in range(0,nx):
            u[i,n+1] = (u[i,n]-u[i,n]*(dt/dx)*(u[i,n]-u[int(ineg[i]),n])+
                        v*(dt/dx**2)*(u[int(ipos[i]),n]-2*u[i,n]+u[int(ineg[i]),n]))
    return((u, x))

Burgers Víscido Parabólico

def burgParab(nt:int, nx:int, tmax:float, xmax:float, v:float, caso:int) -> tuple:
    def f(u):
        return((u**2)/2)
    # incrementos
    dt = tmax/(nt-1)
    dx = xmax/(nx-1)

    # arrays
    u = np.zeros((nx,nt))
    x = np.zeros(nx)
    ipos = np.zeros(nx)
    ineg = np.zeros(nx)

    # condicoes de contorno periodicas
    for i in range(0,nx):
        x[i] = i*dx
        ipos[i] = i+1
        ineg[i] = i-1

    ipos[nx-1] = 0
    ineg[0] = nx-1
    
    # condicoes iniciais
    if caso == 1:
      for i in range(0,nx):
        if x[i] <= 1:
            u[i,0] = 1
        elif x[i] > 1:
            u[i,0] = 0
    elif caso == 2:
      for i in range(0,nx):
        phi = exp( -(x[i]**2)/(4*v) ) + exp( -(x[i]-2*pi)**2 / (4*v) )
        dphi = (-(0.5*x[i]/v)*exp( -(x[i]**2) / (4*v) ) - (0.5*(x[i]-2*pi) / v )
                *exp(-(x[i]-2*pi)**2 / (4*v) ))
        u[i,0] = -2*v*(dphi/phi) + 4

    # solucao numerica
    for n in range(0,nt-1):
        for i in range(0,nx):
            fminus = 0.5*(f(u[i,n]) + f(u[int(ineg[i]),n]))
            fplus = 0.5*(f(u[i,n]) + f(u[int(ipos[i]),n]))
            u[i,n+1] = u[i,n]+dt*(v*((u[int(ipos[i]),n]-(2*u[i,n])+u[int(ineg[i]),n])/(dx**2))-(fplus-fminus)/dx)
    return((u, x))

Burgers Víscido Analítico (Caso 2)

def burgers_analytical(nt:int, nx:int, tmax:float, xmax:float, v:float) -> tuple:
    # incrementos
    dt = tmax/(nt-1)
    dx = xmax/(nx-1)

    # arrays
    u_a = np.zeros((nx, nt))
    x = np.zeros(nx)
    t = np.zeros(nt)

    # len(x)
    for i in range(0, nx):
        x[i] = i*dx

    # solucao analitica
    for n in range(0, nt):
        t = n*dt

        for i in range(0, nx):
            phi = (exp( -(x[i]-4*t)**2/(4*v*(t+1)) ) + 
                   exp( -(x[i]-4*t-2*pi)**2/(4*v*(t+1)) ))

            dphi = ( -0.5*(x[i]-4*t)/(v*(t+1))*exp( -(x[i]-4*t)**2/(4*v*(t+1)) )
                -0.5*(x[i]-4*t-2*pi)/(v*(t+1))*exp( -(x[i]-4*t-2*pi)**2/(4*v*(t+1)) ) )

            u_a[i,n] = -2*v*(dphi/phi) + 4

    return((u_a, x))

Burgers FTCS Invíscido Não Conservativo

def burgInv(nt:int, nx:int, tmax:float, xmax:float) -> tuple:
    # incrementos
    dt = tmax/(nt-1)
    dx = xmax/(nx-1)

    # arrays
    u = np.zeros((nx,nt))
    x = np.zeros(nx)
    ipos = np.zeros(nx)
    ineg = np.zeros(nx)

    # condicoes de contorno periodicas
    for i in range(0,nx):
        x[i] = i*dx
        ipos[i] = i+1
        ineg[i] = i-1

    ipos[nx-1] = 0
    ineg[0] = nx-1
    
    # condicoes iniciais
    for i in range(0,nx):
        if x[i]<=1:
            u[i,0] = 1
        elif x[i]>1:
            u[i,0] = 0

    # solucao numerica
    for n in range(0,nt-1):
        for i in range(0,nx):
            u[i,n+1] = (u[i,n]-u[i,n]*(dt/dx)*(u[i,n]-u[int(ineg[i]),n]))
    return((u, x))

Burgers FTCS Invíscido Conservativo

def burgCons(nt:int, nx:int, tmax:float, xmax:float) -> tuple:
    # incrementos
    dt = tmax/(nt-1)
    dx = xmax/(nx-1)

    # arrays
    u = np.zeros((nx,nt))
    x = np.zeros(nx)
    ipos = np.zeros(nx)
    ineg = np.zeros(nx)

    # condicoes de contorno periodicas
    for i in range(0,nx):
        x[i] = i*dx
        ipos[i] = i+1
        ineg[i] = i-1

    ipos[nx-1] = 0
    ineg[0] = nx-1
    
    # condicoes iniciais
    for i in range(0,nx):
        if x[i]<=1:
            u[i,0] = 1
        elif x[i]>1:
            u[i,0] = 0

    # solucao numerica
    for n in range(0,nt-1):
        for i in range(0,nx):
            u[i,n+1] = (u[i,n]-(dt/dx)*((1/2 * (u[i,n])**2)-(1/2 * (u[int(ineg[i]),n])**2)))
    return((u, x))

Gráfico de e vs.

def plot(u_a:Array2D, u:Array2D, x:list, nt:int, title:str):
    plt.figure(figsize=(8, 6), dpi=80)
    colour=iter(cm.rainbow(np.linspace(0, 20, nt)))
    for n in range(0, nt, 20):
        c=next(colour)
        plt.plot(x, u[:, n], 'ko', markerfacecolor=c, alpha=0.5)
        plt.plot(x, u_a[:, n], linestyle='-', c=c, label=f'i={n}')
    plt.legend()
    plt.xlabel(r'$x$')
    plt.ylabel(r'$u$')
    plt.ylim([0, 8.0])
    plt.xlim([0, max(x)])
    plt.title(title)
    plt.show()

Gráfico de , e vs. na Última Iteração

def plotErro(u:Array2D, u_p:Array2D, u_a:Array2D, x:list, v:float):
    plt.plot(x, u[:,-10], label='Burgers', color='blue')
    plt.plot(x, u_a[:,-10], label='Analítico', color='red')
    plt.plot(x, u_p[:,-10], label='Parabólico', color='green')
    plt.legend()
    plt.xlabel(r'$x$')
    plt.ylabel(r'$u$')
    plt.title('Última Interação com ' + r'$\nu=$' + f'{v}')
    plt.show()

Gráfico 3D Invíscido (Caso 1)

def plot3D(num:int, arr:Array2D, xmax:int, tmax:int, n:int, nx:int, title:str, cond:int) -> None:
    w_arr = np.zeros((num,len(arr[:,-1])))
    x_arr = np.zeros((num,len(arr[:,-1])))
    for i in range(num):
        if cond == 0:
          w, x_res = burgInv(nt, nx, n*i/num, xmax)
        elif cond == 1:
          w, x_res = burgCons(nt, nx, n*i/num, xmax)
        elif cond == 2:
          w, x_res = burgParab(nt, nx, n*i/num, xmax, 0.1,2)
        elif cond == 3:
          w, x_res = burgers(nt, nx, n*i/num, xmax, 0.1, 2)
        w_arr[i] = w[:,-1]
        x_arr[i] = x_res
    ax = plt.axes(projection='3d')
    times = np.zeros((num,1))
    t = 0
    dt = 0
    for i in range(num):
        times[i,0] = t + dt
        dt += tmax/num
    ax.plot_surface(x_arr, times, w_arr, rstride=1, cstride=1,
                    cmap='jet', edgecolor='none')
    plt.title(title)
    ax.set_xlabel('x')
    ax.set_ylabel('t')
    ax.set_zlabel('u')
    plt.show()

Exemplo de Utilização

Define-se , , , e :

nt, nx, tmax, xmax, v = 200, 200, 0.5, 2*pi, 0.1

Chama-se as funções de cálculo e constrói os gráficos:

u, x = burgers(nt, nx, tmax, xmax, v, 2) # ftcs nao conservativo
u_a, x = burgers_analytical(nt, nx, tmax, xmax, v) # ftcs analítico
u_p, x = burgParab(nt, nx, tmax, xmax, v, 2) # # ftcs parabolico
title = str(r'($\nu=$'+f'{v}, '+r'$n_t$'+f'={nt}, '+
     r'$n_x$'+f'={nx}, '+r'$t_{max}$'+f'={tmax})')
plot(u_a, u, x, nt, 'Não Conservativo ' + title) # grafico de u e u_a vs. x
plot(u_a, u_p, x, nt, 'Parabólico ' + title) # grafico de u_p e u_a vs. x
plotErro(u, u_p, u_a, x, v)

Como Resultado

É possível observar que o método não conservativo propaga a descontinuidade com velocidade incorreta, ao contrário do método parabólico. De fato, como demonstrado anteriormente (ver Conservação), no caso em que , pelo método não conservativo a velocidade é nula.

Para o Caso Invíscido

u_nc, x_nc = burgInv(nt, 100, 4, 2)
title = 'Burgers Invíscido (não conservativo) - Caso 1'
plot3D(200, u_nc, 2, 4, 2, 100, title, 0)

u_c, x_c = burgCons(nt, 100, 4, 2)
title = 'Burgers Invíscido (conservativo) - Caso 1'
plot3D(200, u_c, 2, 4, 2, 100, title, 1)

Como Resultado

É possível observar o comportamento discutido acima, quando a descontinuidade não se propaga pelo método FTCS não conservativo; em oposição ao método conservativo, onde se propaga.

Apêndice

O limite bilateral da função não existe no ponto na qual é centrada (assim como a função degrau de Heavisde). Dessa forma, pode-se calcular o os dois limites unilaterais para entender o comportamento da função, assim:

Aplicando a regra do produto:

Para o segundo caso:

Aplicando a regra do produto:

Ao analisar gráfico, verifica-se esse comportamento (ver figura 2)..

Referências

  1. F. M. White, Viscous Fluid Flow, 3rd ed. New York, NY: McGraw-Hill Professional, 2005.
  2. 2,0 2,1 Hopf, E. (1950). The partial differential equation ut + uux = μxx. Communications on Pure and Applied Mathematics, 3(3), 201–230. doi:10.1002/cpa.3160030302
  3. Evans, Lawrence C. (2010). Partial differential equations. [S.l.]: Providence, R.I. : American Mathematical Society. pp. 175–176
  4. Meylan, M., 2020. Nonlinear PDE Meylan. Lecture 12. [online] Youtube.com. Disponível em: <https://youtu.be/CsnUKrLjtyQ> [Acessado em 30 Setembro de 2021]
  5. Halpin-Healy, T., 2015. KPZ growth model - ballistic deposition (BD). [online] Youtube.com. Disponível em: <https://youtu.be/pdeswgu9rS8> [Acessado em 30 Setembro de 2021]
  6. Reis, F. D. A. A. Depinning transitions in interface growth models. Brazilian journal of physics, v. 33, n. 3, p. 501–513, 2003
  7. 7,0 7,1 7,2 Cameron, M. Notes on Burgers's Equation. University of Maryland, 2021
  8. 8,0 8,1 8,2 Landajuela, M. Burgers Equation. Basque Center for Applied Mathematics, 2011
  9. 9,0 9,1 LeVeque, Randall J. (1992). Numerical Methods for Conservation Laws (PDF). Boston: Birkhäuser. p. 125. ISBN 0-8176-2723-5.