Welcome, User!
Una simulación estilo videojuego 8-bit (pero con física real) donde cada partícula es un “mini-balón” que rebota elásticamente dentro de una caja. Midiendo los choques contra las paredes y la energía cinética media, verás aparecer la ecuación del gas ideal \(PV = N k_B T\) ante tus ojos — sin calorímetros ni álgebra oculta. (Bonus: chispazos históricos, cultura pop y un guiño a la música latina para que no todo sea sudor científico).
James Clerk Maxwell y Ludwig Boltzmann jugaban mentalmente con miles de moléculas rebotonas para descubrir la relación entre la presión \(P\), el volumen \(V\) y la temperatura \(T\). Nosotros haremos lo mismo… ¡pero con Python y emojis!
Dato random musical: Daddy Yankee popularizó la palabra gasolina en medio mundo; hoy haremos gas-o-física. 💃⛽️
💡 Spoiler: al promediar suficientes rebotes, la relación \(PV = N k_B T\) emerge — una sinfonía estadística.
pos += vel * dt
.Dato random automotriz: El Fiat 124 Spider (1966) introdujo motores DOHC compactos — pionero de alta “velocidad molecular” bajo el capó. 🏎️✨
import numpy as np
import matplotlib.pyplot as plt
import pandas as pd
# --- Parámetros del “juguete” ---
N = 2000 # número de partículas
L = 1.0 # lado de la caja (m)
m = 1.0 # masa de cada partícula (u.a.)
kB = 1.0 # constante de Boltzmann (u.a.)
T0 = 1.0 # temperatura inicial (u.a.)
steps = 8000 # pasos de simulación
dt = 0.002 # tamaño de paso (s)
# --- Estado inicial ---
pos = np.random.rand(N, 2) * L # posiciones
vel = np.random.normal(0, np.sqrt(kB*T0/m), (N,2)) # velocidades
# --- Registros ---
P_arr, T_arr = [], []
for _ in range(steps):
pos += vel * dt
# Rebotes contra muros y cálculo de impulso transferido
impulse = 0.0
for dim in (0, 1):
hit_low = pos[:, dim] < 0
hit_high = pos[:, dim] > L
vel[hit_low | hit_high, dim] *= -1
impulse += 2 * m * np.abs(vel[hit_low | hit_high, dim]).sum()
pos[hit_low, dim] = 0 # evita “escape” numérico
pos[hit_high, dim] = L
# Presión instantánea: impulso / (área total * dt)
P = impulse / (4 * L * dt) # 4 paredes en 2D
KE = 0.5 * m * (vel**2).sum()
T_inst = KE / (N * kB)
P_arr.append(P)
T_arr.append(T_inst)
# --- Gráficas ---
P_smooth = pd.Series(P_arr).rolling(window=300).mean()
fig, ax = plt.subplots(2, 1, figsize=(7, 6), sharex=True)
ax[0].plot(P_smooth, label='P (u.a.) suavizada')
ax[0].set_ylabel('Presión')
ax[0].legend()
ax[1].plot(T_arr, label='T (u.a.)', color='crimson')
ax[1].set_ylabel('Temperatura')
ax[1].set_xlabel('Paso de tiempo')
ax[1].legend()
plt.suptitle('Evolución de P y T en un gas 2D elástico')
plt.tight_layout()
plt.show()
# --- Verificación de la ley ideal ---
P_mean = np.mean(P_arr[int(steps*0.2):]) # descarta el arranque
T_mean = np.mean(T_arr[int(steps*0.2):])
print(f"P̄ ≈ {P_mean:.3f}, Nk_BT/L² ≈ {(N*kB*T_mean)/(L**2):.3f}")
Figura 1. Presión suavizada y temperatura media para N = 2000.
print
compara la presión promedio con \(Nk_B T / L^2\) (volumen 2-D ≡ área).P̄ ≈ 1961.506, Nk_BT/L² ≈ 1965.582
Se espera un error de ≈ 1 – 3 % por discretización y tamaño finito. En este caso,
\[ \text{Error relativo} \;= \frac{\left|\overline{P} - \frac{N k_B T}{L^{2}}\right|}{N k_B T / L^{2}} \rightarrow \overline{P} = 1961.506,\; N k_B T/L^{2}=1965.582 \rightarrow \frac{1961.506 - 1965.582}{1965.582} \approx -2.07 \times 10^{-3} \;\approx 0.21\%. \]
Dato cultura-latam: El concepto de “gas perfecto” apareció en textos españoles del siglo XIX casi al mismo tiempo que en Inglaterra — ¡la ciencia cruzó el Atlántico más rápido que un bolero! 🎺📚
Con unas cuantas líneas de código y estadística básica comprobamos que los rebotes aleatorios de moléculas producen orden macro: \(PV = N k_B T\). La próxima vez que infles un globo, recuerda que cada pum es una fiesta de impactos microscópicos bailando al ritmo de la termodinámica. 🥳🎈