If you want to go back to the menu press

# NMM Pset1. Maxwell's Demon¶

First of all, we will describe in a simple way this mental experiment. Imagine we have in two different boxes an air, composed by hot and cold particles. In the boundary that separates both of the volumes, there is a tiny tiny devil able to open and close the door ONLY when a hot particle is in the cold chamber and bounce towards the door direction to the right (hot) chamber and viceversa. After some time, the chambers will be so organized that the general entropy of the system has decreased! Lets simulate this

I have decided to use pygame library in order animate the scene. First of all, lets go through the code to see what makes each part. Also, in the final code it can be found all the coments that helps to understand what line or block is doing.

In [ ]:



Implementing all the code explained from above, the final code looks like this :

In [ ]:
import pygame
import random
import math

import matplotlib.pyplot as plt

from drawnow import *

pygame.init()

background_colour = (0,0,0)
(width, height) = (1000, 600)
mass_of_air = 0.2
elasticity = 1
gravity = (math.pi, 0.002)
cont_right_energy= []
font_name = pygame.font.match_font('calibri light')
plt.ion()
font = pygame.font.Font(pygame.font.get_default_font(), 30)
blue = (0,0,255)
red = (255,0,0)
light_red = (250, 139, 139)
light_blue = (63,166,255)
light_yellow = (255,255,153)
yellow = (255,255,0)

def draw_text(surf, text, x, y, color):
text_surface = font.render(str(text),True, color)
text_rect = text_surface.get_rect()
text_rect.topleft = (x,y)
surf.blit(text_surface, text_rect)

def measure_temp_right(particles):
right_tempt = 0
part_in_right = []
for i, particle in enumerate(my_particles):
if particle.x < 500:
right_tempt += particle.temp
part_in_right.append(1)

return round(right_tempt / (len(part_in_right)),2)

def measure_temp_left(particles):
left_tempt = 0
part_in_left = []
for i, particle in enumerate(my_particles):
if particle.x > 500:
left_tempt += particle.temp
part_in_left.append(1)

return round(left_tempt / (len(part_in_left)),2)

def total_ke_left(particles):
left_ke = 0

for i, particle in enumerate(my_particles):
if particle.x < 500:
left_ke += particle.speed**2

return round(left_ke ,2)

def total_ke_right(particles):
right_ke = 0

for i, particle in enumerate(my_particles):
if particle.x > 500:
right_ke += particle.speed**2

return round(right_ke ,2)

x = math.sin(angle1) * length1 + math.sin(angle2) * length2
y = math.cos(angle1) * length1 + math.cos(angle2) * length2

angle = 0.5 * math.pi - math.atan2(y, x)
length = math.hypot(x, y)

return (angle, length)

def findParticle(particles, x, y):
for p in particles:
if math.hypot(p.x - x, p.y - y) <= p.size:
return p
return None

def Energy_calculation(particles):
left_energy = 0
right_energy = 0

for i, particle in enumerate(my_particles):
if particle.x > 500:
right_energy += particle.speed*2
else:
left_energy += particle.speed*2

cont_right_energy.append(right_energy)

def collide(p1, p2):
dx = p1.x - p2.x
dy = p1.y - p2.y

dist = math.hypot(dx, dy)
if dist < p1.size + p2.size:
angle = math.atan2(dy, dx) + 0.5 * math.pi
total_mass = p1.mass + p2.mass

(p1.angle, p1.speed) = addVectors(p1.angle, p1.speed * (p1.mass - p2.mass) / total_mass,
angle, 2 * p2.speed * p2.mass / total_mass)
(p2.angle, p2.speed) = addVectors(p2.angle, p2.speed * (p2.mass - p1.mass) / total_mass,
angle + math.pi, 2 * p1.speed * p1.mass / total_mass)
p1.speed *= elasticity
p2.speed *= elasticity

overlap = 0.5 * (p1.size + p2.size - dist + 1)
p1.x += math.sin(angle) * overlap
p1.y -= math.cos(angle) * overlap
p2.x -= math.sin(angle) * overlap
p2.y += math.cos(angle) * overlap

class Particle_blue():
def __init__(self, x, y, size, mass = 1):
self.x = x
self.y = y
self.size = size
self.colour = blue
self.thickness = 100
self.speed = 40
self.angle = 0
self.mass = mass
self.temp = 1

def display(self):
pygame.draw.circle(screen, self.colour, (int(self.x), int(self.y)), self.size, self.thickness)

def move(self):
# (self.angle, self.speed) = addVectors(self.angle, self.speed)

self.x +=  math.sin(self.angle) * self.speed
self.y -=  math.cos(self.angle) * self.speed
self.vel_y = self.speed * math.cos(self.angle)
self.vel_x = self.speed * math.sin(self.angle)

def bounce(self):
if self.x > width - self.size -10:
self.x = 2 * (width - self.size-10) - self.x
self.angle = - self.angle

elif self.x < self.size +10 :
self.x = 2 * (self.size+10) - self.x
self.angle = - self.angle

if self.y > height - self.size -10:
self.y = 2 * (height - self.size-10) - self.y
self.angle = math.pi - self.angle

elif self.y < self.size +10:
self.y = 2 * (self.size+10)  - self.y
self.angle = math.pi - self.angle

if 490 <= self.x< 1000:
if 0< self.y < 225 and self.vel_x < 0 and self.x <= 500 + self.size :
self.x = 2 * (self.size + self.x-self.size) - self.x
self.angle = - self.angle
elif 405< self.y <600  and self.vel_x < 0 and self.x <= 500 + self.size :
self.x = 2 * (self.size + self.x-self.size) - self.x
self.angle = - self.angle

if self.vel_x > 0 and self.x < width/2  :
if self.x > width/2 -self.size:
self.x = 2 * (width/2 - self.size) - self.x
self.angle = - self.angle

class Particle_red():
def __init__(self, x, y, size, mass=1):
self.x = x
self.y = y
self.size = size
self.colour = red
self.thickness = 100
self.speed = 80
self.angle = 0
self.mass = mass
self.temp = 40

def display(self):
pygame.draw.circle(screen, self.colour, (int(self.x), int(self.y)), self.size, self.thickness)

def move(self):
self.x += 5* math.sin(self.angle) * self.speed
self.y -=  5*math.cos(self.angle) * self.speed
self.vel_y = self.speed * math.cos(self.angle)
self.vel_x = self.speed * math.sin(self.angle)

def bounce(self):
if self.x > width - self.size -10:
self.x = 2 * (width - self.size-10) - self.x
self.angle = - self.angle

elif self.x < self.size +10 :
self.x = 2 * (self.size+10) - self.x
self.angle = - self.angle

if self.y > height - self.size -10:
self.y = 2 * (height - self.size-10) - self.y
self.angle = math.pi - self.angle

elif self.y < self.size +10:
self.y = 2 * (self.size+10)  - self.y
self.angle = math.pi - self.angle

if 0 <= self.x < 510:
if 0 < self.y < 225 and self.vel_x > 0 and self.x >= 500 - self.size:
self.x = 2 * (self.size + self.x - self.size) - self.x
self.angle = - self.angle
elif 405 < self.y < 600 and self.vel_x > 0 and self.x >= 500 - self.size:
self.x = 2 * (self.size + self.x - self.size) - self.x
self.angle = - self.angle

if self.vel_x < 0 and self.x > width/2  :
if self.x < width/2 +self.size:
self.x = 2 * (self.size +width/2) - self.x
self.angle = - self.angle

screen = pygame.display.set_mode((width, height+200))
pygame.display.set_caption('NMM PSET1. Maxwell Demon!')
number_of_particles = 60
my_particles = []

for n in range(number_of_particles):
size = 10
x = random.randint(size, width-size)
y = random.randint(size, height-size)
x1 = random.randint(size, width - size)
y2 = random.randint(size, height - size)

particleb = Particle_blue(x, y, size)
particleb.speed = 0.7
particleb.angle = random.uniform(0, math.pi*2)
my_particles.append(particleb)

particler = Particle_red(x1,y2,size)
particler.speed = 1
particler.angle = random.uniform(0, math.pi*2)
my_particles.append(particler)

running = True
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False

screen.fill(background_colour)
#background image
screen.blit(background,(0,0))

for i, particle in enumerate(my_particles):
particle.move()
particle.bounce()
Energy_calculation(particle)
#
for particle2 in my_particles[i+1:]:
collide(particle, particle2)
particle.display()
draw_text(screen, measure_temp_right(particle), 300, 700, light_blue)
draw_text(screen, 'T = ', 230, 700, blue)
draw_text(screen, measure_temp_left(particle), 760, 700, light_red)
draw_text(screen, 'T = ', 690, 700, red)

draw_text(screen, total_ke_right(particle), 760, 740, light_yellow)
draw_text(screen, 'KE  = ', 660, 740, yellow)

draw_text(screen, total_ke_left(particle), 300, 740, light_yellow)
draw_text(screen, 'KE  = ', 210, 740, yellow)

pygame.display.flip()


If you want to go back to the menu press