Equação de Langevin
Artur Uhlik Fröhlich e Leonardo Dasso Migotto
O objetivo deste trabalho é resolver computacionalmente a equação de Langevin utilizando o método BAOAB. Serão explorados os casos de partículas individuais livres, estudando os efeitos da variação do coeficiente de atrito no deslocamento quadrático médio e no momentum.
Equação de Langevin
Esta equação diferencial estocástica descreve a evolução de um sistema quando sujeito a forças do tipo determinísticas e estocásticas simultâneamente. A sua aplicação mais popular é relativa ao movimento Browniano, o movimento de uma partícula imersa em um fluído, sujeita à força de atrito excercida pelas partículas do fluído. Neste caso, a equação pode ser escrita como:
Na equação acima, é o coeficiente de atrito e é um ruído estocástico branco, que segue o Teorema Central do Limite com média 0 e deslocamento padrão relacionado à temperatura, a Constante de Boltzmann, e a massa da partícula. A partir desta expressão, é possível descobrir analiticamente a relação do coeficiente de difusão do fluído e os valores envolvidos na equação:
Onde é o coeficiente de difusão do meio, é a constante de Boltzmann, é a temperatura e é a massa da partícula. Outra relação presente no livro do Frenkel [1], que é uma das etapas do desenvolvimento da Relação de Einstein, é a do coeficiente de difusão e o deslocamento quadrático médio de uma partícula no meio:
Método BAOAB
O método numérico escolhido para realizar a integração da equação é conhecido como BAOAB, desenvolvido por Leimkuhler e Mattews [2] utilizado para resolver equações diferenciais estocásticas.
Ele é baseado na solução exata para o momentum,
e faz o uso de um método de separação das equações entre as denominadas A, B e O, respectivamente representadas:
O aqui representa um número aleatório Gaussiano que faz o papel do ruído estocástico.
A equação "A" realiza meio passo no tempo da distância, a "B" realiza um meio passo para o momentum e o "O" contabiliza a contribuição estocástica da equação.
Essas equações podem formar vários algoritmos diferentes de integração (ABAO, BABO, ABOBA, etc) mas o utilizado nesse trabalho será o BAOAB:
É importante lembrar que entre os dois últimos passos é necessário atualizar o termo , já que ele pode depender de termos já atualizados como ou .
Implementação do Método BAOAB
Um exemplo de implementação desse método feito foi para a partícula livre. Nota-se que nesse caso a partícula não é afetada por um campo potencial então os passos do método que envolvem a força serão desconsiderados.
A função a seguir se encontra em um código (orientado a objeto) de autoria do integrante Artur e que pode ser acessado aqui[3].
def baoab_livre(self, dt, exp, sqexp, sqt, G):
'''
dt: discretização do tempo
exp: termo referente a primeira exponencial da eq.3
sqexp: termo da raiz quadrada com exponencial da eq.3
sqt: termo da raiz quadrada com a Temperatura da eq.3
G: o vetor G da eq.3
'''
# 1/2 passo da distância
self.pos[0] += self.vel[0]*(dt/2)
self.pos[1] += self.vel[1]*(dt/2)
# Passo estocástico
self.vel[0] = exp*self.vel[0] + sqexp*sqt*G[0]
self.vel[1] = exp*self.vel[1] + sqexp*sqt*G[1]
# Atualização final da posição
self.pos[0] += self.vel[0]*(dt/2)
self.pos[1] += self.vel[1]*(dt/2)
Essa função representa um passo do laço temporal utilizando o método BAOAB. Os atributos em forma de lista ".pos" e ".vel" representam as componentes x e y respectivamente da posição e velocidade do objeto partícula.
Um exemplo de funcionamento do código é a animação a seguir:
O código foi executado usando a semente de números aleatórios 420.
Parâmetros das Simulações
A fim de estudar a implementação do Método BAOAB para a Equação de Langevin para uma partícula livre, nove simulações foram executadas com parâmetros bem definidos. Utilizando unidades reduzidas, os parâmetros recebidos pelo computador foram:
- ,
- ,
- ,
- ,
- ,
- ,
- .
As nove simulações englobam, ao todo, todas combinações de e para os valores escolhidos. Após todos os instantes da simulação serem calculados, o computador salva os valores de posição e momentum de cada um deles em arquivos numa pasta única, que contem, além destes valores, os valores utilizados na simulação. Assim, um programa diferente pode ser utilizado para continuar a execução da simulação ou também gerar gráficos dos valores salvos de posição e momentum. Isso permite uma análise facilitada dos dados, pois não requer a execução completa do programa para cada gráfico que se deseja criar.
Como esta é uma simulação com uma variável estocástica, alguns cuidados são extremamente importantes para fazer a execução correta da simulação. Os seguintes fatos devem ser tidos em mente ao executar simulações desta natureza:
- - As simulações devem sempre possuir sementes aleatórias diferentes, com pouquíssimas exceções. Utilizando os nossos programas de exemplo: caso os valores de e fossem trocados, mas a semente aleatória não, estaríamos utilizando os mesmos valores aleatórios para calcular o termo estocástico da equação, não gerando simulações completamente independentes. Além do mais, a "grande sacada" dessas simulações é que elas devem conseguir ser analisadas independente das sementes aleatórias.
- - Simulações com diferentes, porém todas outras variáveis iguais (incluindo a semente aleatória) não são equivalentes. Utilizando os nossos programas de exemplo: caso o mude, o número de números aleatórios a ser gerado será diferente, portanto ambas simulações, aparentemente equivalentes, terão ruídos aleatórios completamente diferentes e, portanto, não poderão ser comparadas tradicionalmente.
Implementação da Graficação do Momentum
Segue abaixo uma implementação de um graficador do histograma normalizado do momenta de uma partícula. A partir de um array de momenta de uma única partícula, o código faz os cálculos necessários, deixando o gráfico pronto para plotagem.
A função a seguir se encontra em um código de autoria do integrante Leonardo. Este código abre os dados gerados por outro programa, que salva todas informações em arquivos, e permite visualizar propriedades como o MSD, a distribuição do momentum, entre outras coisas.
if modo == "P":
p = np.sqrt(pxy[:, 0]**2 + pxy[:, 1]**2)
#MOMENTUM MAIS COMUM
n, bins, patches = plt.hist(p, 500, histtype = "step", density = True)
maior = np.argmax(n)
pcomum = (bins[maior + 1] + bins[maior])/2
plt.axvline(pcomum, color = "red", alpha = 0.4, label = f"Momentum mais provável: {pcomum:.3f}")
#MOMENTUM MEDIO
pmed = np.mean(p)
plt.axvline(pmed, c = "green", label = f"Momentum médio: {pmed:.3f}")
#MOMENTUM QUADRADO MEDIO
pqmed = np.sqrt(np.mean(p**2))
plt.axvline(pqmed, color = "purple", alpha = 0.4, label = f"Momentum rms: {pqmed:.3f}\n" + r"$\frac{p_{rms}}{\sqrt{T}}$" + f": {pqmed/np.sqrt(temp):.3f}")
plt.xlim(0, 7)
plt.ylim(0, 0.7)
plt.grid(alpha = 0.3)
plt.legend(loc = 1)
plt.title(f"Momentum em módulo\n" + r"$\gamma$ = " + f"{gaminha}, Temp. = {temp}, " + r'$\Delta t$ = '+f'{dt}, Instantes = {nsalvos}')
plt.ylabel(r'$P(x)$')
plt.xlabel(r'$\left|\vec{p}(t)\right|$')
Neste código, "pxy" é um array bidimensional, onde os índices da primeira dimensão representam o tempo, e a segunda dimensão possui dois índices: um para o momentum em e outro para o momentum em . O programa calcula o módulo do momentum, além de outros três valores (decididos durante as plotagem de testes por conta dos aspectos iniciais dos gráficos): o momentum mais provável, o momentum médio e a média do momentum quadrático (além de um cálculo dele relacionado à temperatura). Um histograma normalizado dos momenta é feito simultâneo ao cálculo do momentum mais provável, além de ajustes de elementos gráficos do gráfico. Por ser um histograma normalizado, ele segue a seguinte relação (onde é a função da curva do gráfico):
Análise do Momentum
Os gráficos abaixo, gerados pelo código acima, mostram a densidade de probabilidade de, escolhido um instante aleatório da simulação, qual momentum a partícula possui.
Uma observação rápida dos gráficos (mesmo que ignorando as retas destacadas na plotagem e as informações da legenda) torna evidente que a distribuição em questão é a Maxwell-Boltzmann[4]. Esta distribuição descreve a densidade de probabilidade da velocidade das moléculas de um gás ideal em equilíbrio termodinâmico. Dado que, utilizando , o valor de momentum e de velocidade são iguais em módulo, é coerente tratar essa distribuição exatamente como se trataria a de velocidade (mesmo que a massa tenha um peso nesta distribuição, este peso não a afeta neste caso). O gás o qual as distribuições estão descrevendo a probabilidade da velocidade é um gás ideal composto de átomos da partícula que está sendo simulada, de modo que, escolhendo uma partícula deste gás ideal hipotético, a função descreve a chance da partícula ter tal velocidade.
Como é característico da distribuição de Maxwell-Boltzmann, a temperatura "estende" o limite da função, diminuindo seu pico. Para comprovar a relação da temperatura com a distribuição, podemos utilizar (como já foi feito nos gráficos) a fórmula da :
Como está em evidência nos gráficos anteriores, retirar a proporção da raiz da temperatura dos valores de (que, como discutimos anteriormente, possui valor equivalente ao ) retorna sempre o mesmo valor.
Estes resultados são especialmente interessantes pois, ao simultar uma partícula "isolada" que sofre interações com o meio via uma variável estocástica, estamos obtendo resultados que outrora poderiam ser encontrados ao realizar uma simulação de dinâmica molecular [5] com potencial Lennard Jones [6]. Simulação esta, que requer acompanhar o movimento de muitas partículas e calcular as interações entre elas, sendo de maior complexidade de implementação.
Implementação do Deslocamento Quadrático Médio
Segue abaixo uma implementação do cálculo do deslocamento quadrático médio. A partir de um array de posições de uma única partícula, o código faz os cálculos necessários retornando um array com o MSD em cada instante de tempo, além de outras coisas.
A função a seguir se encontra em um código de autoria do integrante Leonardo. Este código abre os dados gerados por outro programa, que salva todas informações em arquivos, e permite visualizar propriedades como o MSD, a distribuição do momentum, entre outras coisas.
if modo == "MSD":
novotamanho = int(1 + ((nsalvos-1)/divisoes))
desviodividido = np.zeros(novotamanho)
indice = novotamanho - 1
xx = xy[:, 0]
yy = xy[:, 1]
for i in range(divisoes):
xini = xx[i*indice]
yini = yy[i*indice]
dx = xx[i*indice : 1+(i+1)*indice] - xini
dy = yy[i*indice : 1+(i+1)*indice] - yini
desviodividido += dx**2 + dy**2
desviodividido = (desviodividido/divisoes)[1:]
eixox = np.linspace(0, int(tmax/divisoes), int((nsalvos - 1)/divisoes) + 1)[1:]
difusivo = desviodividido[int(10/(dt*gaminha)):]
dezao = np.mean(difusivo/eixox[int(10/(dt*gaminha)):])/4
eixox = np.log10(eixox)
desviodividido = np.log10(desviodividido)
balistico = desviodividido[:int(1/(dt*gaminha))]
difusivo = desviodividido[int(10/(dt*gaminha)):]
bal = np.polyfit(eixox[:int(1/(dt*gaminha))], balistico, 1)
dif = np.polyfit(eixox[int(10/(dt*gaminha)):], difusivo, 1)
reta1 = bal[0]*eixox + bal[1]
reta2 = dif[0]*eixox + dif[1]
Neste código, "xy" é um array bidimensional, onde os índices da primeira dimensão representam o tempo, e a segunda dimensão possui dois índices: um para a posição em e outro para a posição em . Para melhorar a precisão do MSD, o programa permite "fatiar" o array original de posições, considerando cada fatia das distâncias como uma execução do programa independente (o número de fatias é a variável "divisoes", definida ao iniciar o programa). Um número maior de fatias resulta em um tempo reduzido percorrido pela partícula em cada fatia, porém retorna dados com maior precisão.
Após definir a posição relativa de cada fatia dentro do array original, o programa itera sobre ele, calculando os valores de MSD para cada instante de cada fatia e somando-os a um array vazio. Por fim, é feito a média dos MSDs pelo número de partículas neste array. Dado o modo preferido de visualizar os dados posteriormente e a necessidade de outras análises numéricas, o array é transformado para escala logarítmica. É importante destacar que, antes de transformar os arrays de tempo e MSD para a escala logarítmica, ambos perdem o valor de índice 0: isso é importante pois, ao transformar os tempos para esta escala, o valor do , e este valor é impossível de ser calculado computacionalmente.
Em seguida, dois arrays criados a partir do do MSD: um deles contém somente pontos do período de difusão balística, enquanto o outro possui pontos da difusão normal. É feita a regressão linear de cada um destes conjuntos de dados, a fim de encontrar a inclinação das retas ajustadas aos dados (e confirmar que estes períodos possuem, de fato, essas características difusivas, como veremos a seguir). Os intervalos para os períodos de difusão foram escolhidos após uma análise interna dos gráficos.
Estas informações são posteriormente colocadas em um gráfico utilizando outras funções. Estes gráficos poderão ser vistos na próxima seção.
Análise do MSD
Os resultados a serem analisados são originados do código (procedural) do integrante Leonardo.
Foram executadas diferentes vezes a simulação para compararmos a influência do e da temperatura no deslocamento quadrático médio:
De início pode-se notar que o escala de maneira proporcional a (balístico) no início do processo, e depois de um determinado tempo obtemos uma relação linear que é a parte normalmente difusiva do processo. Essas inclinações das retas nos regimes de difusão [7] balístico e difusivo são constantes em relação ao e à temperatura.
Nota-se também que a temperatura não gera aparente diferença nos gráficos de MSD mas sim no valor do coeficiente de difusão (comentado posteriormente).
Uma referência da literatura a respeito do MSD (mas no contexto de dinâmica molecular usando o potencial de Lennard-Jones) se encontra no livro do Frenkel, expressa por essa imagem:
Outro aspecto importante a se ressaltar é a concordância do cálculo do coeficiente de difusão através da equação (I) e da relação de Einstein em (II), obtida através dos pontos presentes na parte difusiva, que foram chamados respectivamente nas legendas dos gráficos de "D analítico" e "D calculado".
Uma relação interessante encontrada foi a maneira como escala a mudança de regimes, indicada pela reta perpendicular ao eixo do tempo. Podemos encontrar uma relação de proporção para o tempo em que o regime difusivo inicia na simulação a partir de uma relação dependente de :
Códigos
dfisads: https://github.com/Artur-UF/MetComp/tree/main/MetCompC/trabalho2 GitHub do Artur
Os códigos desenvolvidos pelo integrante Leonardo seguem abaixo. A semente aleatória para gerar os valores utilizados nesse trabalho estão comentados no arquivo lstart.
- lstart Programa utilizado para gerar valores sem partir de nenhum arquivo anterior. Os dados são identificados por um código numérico único, e serão salvos em uma pasta com nome de tal código, onde ali se encontrarão:
- - O arquivo xy#código.npy principal, que consiste no array de posição.
- - O arquivo pxy#código.npy principal, que consiste no array de momentum.
- - O arquivo valcódigo.npy, que consiste em um arquivo que é lido pelos outros programas contendo informações cruciais.
- - O arquivo info#código.txt, que contém tais informações cruciais em um formato legível ao usuário.
- - O arquivo state#código.txt, que contém o estado da semente aleatória.
Utilizado em conjunto com os programas "lleitor", você pode gerar imagens como as vistas neste trabalho.
- lcontinue Programa utilizado para gerar valores partindo de outros já antes calculados. Os arquivos com os dados novos (concatenados aos antigos) se encontrará na pasta original, substituindo os arquivos antigos. Recomendado para gerar valores por tempos longos sem precisar deixar o programa executando por longas horas sem intervalo.
- lleitor Programa utilizado para gerar as imagens estáticas vistas neste trabalho. Ela salvará as imagens em pastas com o nome do modo selecionado do programa, a fim de facilitar a comparação de diferentes simulações.
- lpos Programa utilizado para gerar as imagens do rastro da posição das partículas equivalentes fatiadas (imagens presentes no GIF). NÃO RECOMENDO O USO, ELE REQUER EDIÇÕES NAS LINHAS DE CÓDIGO PARA FUNCIONAR CORRETAMENTE.
- lpdf Programa utilizado para gerar as imagens da função de densidade de probabilidade do momentum da partícula (imagens presentes no GIF). NÃO RECOMENDO O USO, ELE REQUER EDIÇÕES NAS LINHAS DE CÓDIGO PARA FUNCIONAR CORRETAMENTE.
- lmsd Programa utilizado para gerar as imagens do MSD (imagens presentes no GIF). NÃO RECOMENDO O USO, ELE REQUER EDIÇÕES NAS LINHAS DE CÓDIGO PARA FUNCIONAR CORRETAMENTE.
Referências
- ↑ Daan Frenkel and Berend Smit. 2001. Understanding Molecular Simulation (2nd. ed.). Academic Press, Inc., USA.
- ↑ Leimkuhler, B., & Matthews, C. (2015). Molecular Dynamics: With Deterministic and Stochastic Numerical Methods. (Interdisciplinary Applied Mathematics; Vol. 39). Springer. https://doi.org/10.1007/978-3-319-16375-8
- ↑ Os códigos orientado a objeto: GitHub do Artur
- ↑ https://en.wikipedia.org/wiki/Maxwell%E2%80%93Boltzmann_distribution
- ↑ https://fiscomp.if.ufrgs.br/index.php/Din%C3%A2mica_Molecular
- ↑ https://fiscomp.if.ufrgs.br/index.php/Grupo_-_Lennard_Jones
- ↑ https://fiscomp.if.ufrgs.br/index.php/Medidas_din%C3%A2micas