MBA: Gás simples: mudanças entre as edições

De Física Computacional
Ir para navegação Ir para pesquisar
Jhordan (discussão | contribs)
Sem resumo de edição
Jhordan (discussão | contribs)
Sem resumo de edição
Linha 63: Linha 63:




Foi realizado uma simulação com 100 partículas por 1.000.000 de passos e o resultado encontra-se na figura abaixo assim como o código.  
Foi realizado uma simulação com 100 partículas por 500.000 de passos em uma grade 100x100 com fronteira toroidal, o resultado encontra-se na figura abaixo assim como o código.  


<div class="center">[[Ficheiro:mba_colisao.png|centro|miniaturadaimagem|750x750px|Resultado da simulação com a distribuição de Maxwell-Boltmann para kT=0.7  e m=1.]]</div>
<div class="center">[[Ficheiro:mba_colisao.png|centro|miniaturadaimagem|750x750px|Resultado da simulação com a distribuição de Maxwell-Boltmann para kT=0.7  e m=1.]]</div>
Linha 97: Linha 97:
         self.vel      = (v*np.cos(ang),v*np.sin(ang)) #Velocidade decomposta
         self.vel      = (v*np.cos(ang),v*np.sin(ang)) #Velocidade decomposta
         self.colisao=False
         self.colisao=False
        self.ac=False
                  
                  
     def prox_pos(self):
     def prox_pos(self):
Linha 128: Linha 129:
                 nvel1=(self.vel[0]+u1*d[0],self.vel[1]+u1*d[1])
                 nvel1=(self.vel[0]+u1*d[0],self.vel[1]+u1*d[1])
                 #partícula 2,pela conservação de momento:
                 #partícula 2,pela conservação de momento:
                 nvel2=(self.vel[0]+a.vel[0]-nvel1[0],self.vel[1]+a.vel[1]-nvel1[1])
                 nvel2=(self.vel[0]+a.vel[0]-nvel1[0],self.vel[1]+a.vel[1]-nvel1[1])  
                 #Atualiza as velocidades
                 #Se nenhuma partícula tinha se colidido
                self.vel=nvel1; a.vel=nvel2
                if (self.colisao==False and a.colisao==False):         
                  #Atualiza as velocidades
                  self.vel=nvel1; a.vel=nvel2
                  self.ac=True;a.ac=True 
                 #Registra a colisão
                 #Registra a colisão
                 self.colisao=True; a.colisao=True            
                 self.colisao=True; a.colisao=True      
                 break
                 break
         return ()                                           
         return ()                                           
Linha 138: Linha 142:
     def movimento(self):
     def movimento(self):
       if(self.colisao):
       if(self.colisao):
        self.colisao=False
         return (self.pos)
         return (self.pos)
       else:
       else:
Linha 195: Linha 198:
         """Avançar um passo do modelo"""
         """Avançar um passo do modelo"""
         self.schedule.step()                              #Avançamos os agentes
         self.schedule.step()                              #Avançamos os agentes
 
        #Limpamos as colisões
        for a in self.schedule.agents:                    #Vamos percorrer os agentes
          a.colisao=False
#Parâmetros
#Parâmetros
MAX =100000
MAX =1000000
N=100
N=100
modelo  =  {"Largura":10     ,"Altura":10     ,"Seed":1}  
modelo  =  {"Largura":100     ,"Altura":100     ,"Seed":1}  
M = Modelo(modelo,N)
M = Modelo(modelo,N)
for i in range(MAX):
for i in range(MAX):
Linha 206: Linha 211:
       #clear_output()  
       #clear_output()  
       print(str(100*(1+i)/MAX)+"%")
       print(str(100*(1+i)/MAX)+"%")
</pre>
Para visualizar os resultados, podemos usar:
<pre>
import numpy as np
import matplotlib.pyplot as plt


vel=[]
vel=[]
Linha 220: Linha 218:
     E+=a.vel[0]**2+a.vel[1]**2
     E+=a.vel[0]**2+a.vel[1]**2
print(E)
print(E)
a,b,c=plt.hist(vel,10, density=False, facecolor='g', alpha=0.75)
with open('velocidades0.2R100K1000.txt', 'w') as f:
    f.write(str(vel))
</pre>
 
Para plotar o gráfico, podemos então usar simplesmente:
<pre>
import numpy as np
import matplotlib.pyplot as plt
 
a,b,c=plt.hist(vel,8, density=True, facecolor='g', alpha=0.75)
m=1
m=1
K=0.7
K=0.7
x=np.arange(0,2, 0.00001)
x=np.arange(0,2.5, 0.00001)
y = (4/np.sqrt(np.pi))*((m/K)**(3/2))*(x**2)*np.exp(-m*(x**2)/K)
y = (4/np.sqrt(np.pi))*((m/K)**(3/2))*(x**2)*np.exp(-m*(x**2)/K)
plt.plot(x,y)
plt.plot(x,y)

Edição das 10h47min de 22 de julho de 2022

Anterior: Por que usar e o que são modelos baseados em indivíduos | Índice: Ecologia | Próximo: MBA: Caminhante aleatório

Colisão entre partículas

Considerando um modelo simples de gás ideal, não há força atuando sob as partículas, então a interação que ocorre entre as partículas se dá apenas por meio de colisões. Assim é necessário calcular a variação na velocidade de cada partícula após a colisão. Começando em uma dimensão, precisamos garantir a conservação do momento: m1v1′+m2v2′=m1v1″+m2v2″E da energia cinética: 12m1v1'2+12m2v2'2=12m1v1′'2+12m2v2′'2Colocando o referencial de forma que v2′=0, então as velocidades no novo referencial podem ser escritas como ui=vi−v2′, de forma que da conservação do momento ficamos com: u1″=u1′−m2m1u2″,(1) Elevando ao quadrado: u1′'2=u1'2−(m2m1)2u2′'2−2m2m1u1′u2″ Substituindo na equação de conservação de energia, podemos encontrar u2″: m1u1'2=m1u1′'2+m2u2′'2m1u1'2=m1[u1′2+(m2m1)2u2′'2−2m2m1u1′u2″]+m2u2′'2m1u1'2=m1u1′2+m22m1u2′'2−2m2u1′u2″+m2u2′'20=(m22m1+m2)u2′'2+(−2m2u1′)u2″0=(m2+m1m1)u2′'2+(−2u1′)u2″ Calculando as raízes do segundo grau, temos: u2″=2u1′±4u1'22(m2+m1m1)=m1m2+m1(u1′±u1′)E como uma solução é u2′=0, mas queremos a situação em que u2′≠0, logo: u2″=2m1m2+m1u1′Substituindo em (1), temos: u1″=u1′−m2m1(2m1m2+m1u1′)u1″=(1−2m2m2+m1)u1u1″=(m2+m1−2m2m2+m1)u1 Ou seja: u1″=m1−m2m2+m1u1′Retornando ao referencial original, sendo vi=ui+v2′, temos então para v1′: u1″=m1−m2m2+m1u1′v1″−v2′=m1−m2m2+m1(v1′−v2′)v1″=m1−m2m2+m1v1′+(1−m1−m2m2+m1)v2′v1″=m1−m2m2+m1v1′+2m2m2+m1v2′E de maneira análoga para v2′: u2″=2m1m2+m1u1′v2″−v2′=2m1m2+m1(v1′−v2′)v2″=2m1m2+m1v1′+(1−2m1m2+m1)v2′v2″=2m1m2+m1v1′+m2−m1m2+m1v2′Um caso especial ocorre se m1=m2,(2) então temos simplesmente: v1″=v2′,ev2″=v1′Em duas dimensões, podemos reduzir o problema a uma dimensão, considerando que toda a alteração na velocidade devido a colisão entre partículas ocorre apenas na componente paralela a reta que liga o centro das duas esferas. Considerando que a posição de cada partícula é dada por r→i, então um vetor entre as partículas pode ser escrito como d→=r→2−r→1. Podemos projetar ambas as velocidades então fazendo: ui′=v→i⋅d→|d→|=v→i⋅d^Obtemos o módulo da velocidade da partícula i na direção d→ e podemos trabalhar em uma única dimensão para encontrarmos a velocidade de ambas partículas após a colisão nesta dimensão. Ao fim podemos decompor novamente a velocidade final ui″ em ambos os eixos mantendo a mesma direção utilizando θ=arctan⁡(ΔyΔx), onde Δz=z2−z1 é a diferença entre a posição das partículas 2 e 1 na componente z.

Além disso vale lembrar que há a componente da velocidade perpendicular a d→, que vamos denotar como w→i=v→i−ui′(cos⁡θ,sin⁡θ). Esta componente perpendicular permanece inalterada e pode ser visualizada na figura ao lado.

Colisão entre duas partículas mostrando explicitamente os vetores relacionados à partícula 1.
Colisão entre duas partículas mostrando explicitamente os vetores relacionados à partícula 1.

Sendo assim, a velocidade final é dada por: v→i(f)=(ui″cos⁡θ,ui″sin⁡θ)+w→iv→i(f)=ui″(cos⁡θ,sin⁡θ)+v→i−ui′(cos⁡θ,sin⁡θ)v→i(f)=v→i+(ui″−ui′)(cos⁡θ,sin⁡θ)v→i(f)=v→i(f)=v→i+(ui″−ui′)a→Onde a→ é um vetor unitário que nos dá a direção entre os centros das partículas. Utilizando ad identidades: cos⁡(arctan⁡(x))=11+x2,sin⁡(arctan⁡(x))=x1+x2 ficamos então com: a→=(cos⁡θ,sin⁡θ)a→=(cos⁡arctan⁡(ΔyΔx),sin⁡arctan⁡(ΔyΔx))a→=(11+(ΔyΔx)2,(ΔyΔx)1+(ΔyΔx)2)a→=1Δx11+(ΔyΔx)2(Δx,Δy)a→=1ΔxΔx1Δx2+Δy2(Δx,Δy)Logo: a→=(Δx,Δy)Δx2+Δy2 E uma vez que d→=r→2−r→1=(x2−x1,y2−y1)=(Δx,Δy), então a→=d→/|d|=d^. Logo: v→i(f)=v→i+(ui″−ui′)d^Ou ainda mais explícito, se fizermos m1=m2, sendo as partículas i e j, onde i≠j, usando (2): v→i(f)=v→i+(uj′−ui′)d^v→i(f)=v→i+(v→j⋅d^−v→i⋅d^)d^ Temos então que: v→i(f)=v→i+[(v→j−v→i)⋅d^]d^Todo o cálculo exibido foi para uma partícula, para a segunda partícula, o cálculo é análogo.

Distribuição de velocidades para um gás a certa temperatura

A distribuição das velocidads das molécula de um gá é dada pela distribuição de Maxwell-Boltmann [1]

f(v)=(m2πkT)3/24πv2exp⁡(−mv2kT)


Foi realizado uma simulação com 100 partículas por 500.000 de passos em uma grade 100x100 com fronteira toroidal, o resultado encontra-se na figura abaixo assim como o código.

Resultado da simulação com a distribuição de Maxwell-Boltmann para kT=0.7 e m=1.


Código

Aqui chamamos a atenção que a principal diferença do código discutido para autômatos celulares é que agora não temos mais uma grade, mas um modelo contínuo. Também temos a possibilidade de configuração se as fronteiras serão toroidais (extremidades conectadas) ou não. Quanto aos resultados, precisamos considerar que é um modelo simplificado. O Calculo foi realizado para uma colisão entre duas partículas se dentro do mesmo passo deveria ocorrer mais de uma colisão, ou com mais de uma partícula simultaneamente o código falha, para remediar isso, o ideal é utilizar passos de tempo pequenos e/ou uma caixa relativamente grande em relação a quantidade de partículas. Além disso ao optarmos por uma fronteira toroidal ou não, estamos considerando uma caixa fechada ou não, ao considerarmos precisamos considerar a colisão com as fronteira também. Nesse último caso, uma simplificação simples é considerar que esta colisão ocorre primeiro. Novamente o problema surge se houver duas colisões no mesmo passo (agora podendo colidir com a fronteira), logo a recomendação é a mesma.

#Bibliotecas necessárias
from mesa import Agent, Model                                      #Classes Agente e Modelo
from mesa.time import SimultaneousActivation                       #Agendador simultâneo
from mesa.space import ContinuousSpace                             #Malha multigrid
import random                                                      #Número aleatórios
from mesa.datacollection import DataCollector                      #Importamos um coletor de dados
import matplotlib.pyplot as plt                                    #Plotar gráicos             
#from IPython.display import clear_output                           #Configurações de saída do Colab         
import numpy as np    

#AGENTE---------------------------------------------------------------------------------
class Agente(Agent):
    """Classe do agente"""
    def __init__(self, modelo,largura,altura):
        """Bibliotecas necessárias"""
        #modelo     - Modelo que ao qual o agente pertence
        super().__init__(self, modelo)        #Necessário para funcionar o modelo
        self.ppos     = (0,0)                 #Poição que irá se mover
        self.rai= 0.2                         #Raio
        self.lim=(largura,altura)             #Limites da caixa
        v=1                                   #Módulo da velocidade inicial
        ang = 2*random.random()*np.pi
        self.vel      = (v*np.cos(ang),v*np.sin(ang)) #Velocidade decomposta
        self.colisao=False
        self.ac=False
                
    def prox_pos(self):
      dt=0.01
      nx=self.pos[0]+self.vel[0]*0.01
      ny=self.pos[1]+self.vel[1]*0.01
      #Testar os limites
      if (nx < 0):
        nx = - nx; self.vel=(-self.vel[0],self.vel[1])
      if (nx > self.lim[0]):
        nx = 2*self.lim[0] - nx; self.vel=(-self.vel[0],self.vel[1])
      if (ny < 0):
        ny = - ny; self.vel=(self.vel[0], - self.vel[1])
      if (ny > self.lim[1]):
        ny = 2*self.lim[1] - ny;self.vel=(self.vel[0], - self.vel[1])

      return((nx,ny))
    
    def prox_vel(self):
        """Movimentação dos animais"""
        #Checamos se há colisão com outro animal:
        agentes = self.model.continuous.get_neighbors(pos=self.ppos, radius = 3*self.rai, include_center=True)
        for a in agentes:                                         #Percorremos a lista
          if (a.unique_id!=self.unique_id): 
              d=np.sqrt((a.ppos[0]-self.ppos[0])**2+(a.ppos[1]-self.ppos[1])**2)
              if (d<=2*self.rai):
                #Direção da colisão
                d=(a.ppos[0]-self.ppos[0],a.ppos[1]-self.ppos[1])/np.sqrt((a.ppos[0]-self.ppos[0])**2+(a.ppos[1]-self.ppos[1])**2)
                #Partícula 1
                u1=(a.vel[0]-self.vel[0])*d[0]+(a.vel[1]-self.vel[1])*d[1]
                nvel1=(self.vel[0]+u1*d[0],self.vel[1]+u1*d[1])
                #partícula 2,pela conservação de momento:
                nvel2=(self.vel[0]+a.vel[0]-nvel1[0],self.vel[1]+a.vel[1]-nvel1[1])   
                #Se nenhuma partícula tinha se colidido
                if (self.colisao==False and a.colisao==False):          
                  #Atualiza as velocidades
                  self.vel=nvel1; a.vel=nvel2
                  self.ac=True;a.ac=True   
                #Registra a colisão
                self.colisao=True; a.colisao=True       
                break
        return ()                                          

    def movimento(self):
      if(self.colisao):
        return (self.pos)
      else:
        return(self.ppos)
    
    def step(self):
        """Método obrigatório que prepara as mudanças"""
        self.ppos = self.prox_pos()
             
    def advance(self):
        """Método obrigatório que aplica as mudanças"""
        self.prox_vel()
        self.model.continuous.move_agent(self, self.movimento())

#MODELO
class Modelo(Model):
    """Modelo geral"""
    def gerar_particulas(self,N,largura,altura):
      """Função para gerar animais iniciais"""    
      #N        - Número de animais
      #largura  - Largura da grade
      #altura   - Altura da grade
      for n in range(N):
          con=True
          while(con):
            con=False
            a = Agente(self,largura,altura)
            X= largura*random.random()
            Y= altura*random.random()
            if (len(self.schedule.agents)==0):
              self.schedule.add(a)
              self.continuous.place_agent(a, (X, Y))
            else:
              agentes = self.continuous.get_neighbors(pos=(X,Y), radius = 2*a.rai, include_center=True)
              for viz in agentes:
                con = (True) if (len(agentes)==0) else (con)  #Precisamos checar se nasceu no mesmo lugar de outro animal
              if(con==False):
                self.schedule.add(a)
                self.continuous.place_agent(a, (X, Y))
      return()

    def __init__(self, modelo,N,seed=None):
        """Função chamada quando o modelo é inicializazdo"""
        # Modelo   - Dicionário com especificações do modelo
        # N        - Número de partículas
        # seed     - Seed dos números aleatórios do modelo do mesa
        
        # Desempacotar o dicionário
        largura = modelo["Largura"];altura=modelo["Altura"]; random.seed(modelo["Seed"])                     
        self.continuous = ContinuousSpace(x_max= largura, y_max=altura, torus= True, x_min=0, y_min= 0) #Configura a grade
        self.schedule   = SimultaneousActivation(self)            #Configura o agendador
        self.running   = True                                     #Condiçao para seguir executando o modelo
        self.gerar_particulas(N,largura,altura)    #Distribuir as ovelhas
           
    def step(self):
        """Avançar um passo do modelo"""
        self.schedule.step()                              #Avançamos os agentes
        #Limpamos as colisões
        for a in self.schedule.agents:                    #Vamos percorrer os agentes
          a.colisao=False
#Parâmetros
MAX =1000000
N=100
modelo  =  {"Largura":100     ,"Altura":100      ,"Seed":1} 
M = Modelo(modelo,N)
for i in range(MAX):
    M.step()
    if ((i+1)%(MAX/100)==0):
      #clear_output() 
      print(str(100*(1+i)/MAX)+"%")

vel=[]
E=0
for a in M.schedule.agents:
    vel.append(np.sqrt(a.vel[0]**2+a.vel[1]**2))
    E+=a.vel[0]**2+a.vel[1]**2
print(E)
with open('velocidades0.2R100K1000.txt', 'w') as f:
    f.write(str(vel))

Para plotar o gráfico, podemos então usar simplesmente:

import numpy as np
import matplotlib.pyplot as plt

a,b,c=plt.hist(vel,8, density=True, facecolor='g', alpha=0.75)
m=1
K=0.7
x=np.arange(0,2.5, 0.00001)
y = (4/np.sqrt(np.pi))*((m/K)**(3/2))*(x**2)*np.exp(-m*(x**2)/K)
plt.plot(x,y)

Citações


Anterior: Por que usar e o que são modelos baseados em indivíduos | Índice: Ecologia | Próximo: MBA: Caminhante aleatório