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, u
D=0, u
E=1, x
0=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
Gráfico da solução numérica e analítica para a equação de Burgers víscida utilizando FTCS não conservativo.
Gráfico da solução numérica e analítica para a equação de Burgers víscida utilizando o método parabólico.
Comparação da última iteração de dois métodos numéricos, FTCS não conservativo (Burgers) e parabólico, para a equação de Burgers víscida, com sua solução analítica.
É 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
Gráfico 3D da solução da equação de Burgers para o caso invíscido com método FTCS não conservativo.
Gráfico 3D da solução da equação de Burgers para o caso invíscido com método FTCS conservativo.
É 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
- ↑ F. M. White, Viscous Fluid Flow, 3rd ed. New York, NY: McGraw-Hill Professional, 2005.
- ↑ 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
- ↑ Evans, Lawrence C. (2010). Partial differential equations. [S.l.]: Providence, R.I. : American Mathematical Society. pp. 175–176
- ↑
Meylan, M., 2020. Nonlinear PDE Meylan. Lecture 12. [online] Youtube.com. Disponível em: <https://youtu.be/CsnUKrLjtyQ> [Acessado em 30 Setembro de 2021]
- ↑ 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]
- ↑ Reis, F. D. A. A. Depinning transitions in interface growth models. Brazilian journal of physics, v. 33, n. 3, p. 501–513, 2003
- ↑ 7,0 7,1 7,2 Cameron, M. Notes on Burgers's Equation. University of Maryland, 2021
- ↑ 8,0 8,1 8,2 Landajuela, M. Burgers Equation. Basque Center for Applied Mathematics, 2011
- ↑ 9,0 9,1 LeVeque, Randall J. (1992). Numerical Methods for Conservation Laws (PDF). Boston: Birkhäuser. p. 125. ISBN 0-8176-2723-5.