From Microstates to Macrostates
Classical thermodynamics describes heat engines and phase diagrams without asking what molecules are doing. Statistical mechanics does the opposite: it derives every thermodynamic quantity — temperature, entropy, pressure — from microscopic probabilities. The bridge is simple: a system in thermal equilibrium occupies each microstate with probability proportional to e^(−E/kT), the Boltzmann factor.
Entropy S = k·ln(Ω) counts microstates. The second law — entropy never decreases in an isolated system — says nothing more than that the system wanders toward macrostates with more microstates. At 10²³ particles, the probability of going backwards is effectively zero.
Probability of microstate i: P_i = e^(−E_i/kT) / Z
Partition function: Z = Σ_i e^(−E_i/kT) (sum over all
microstates)
Helmholtz free energy: F = −kT · ln Z
Mean energy: ⟨E⟩ = −∂(ln Z)/∂β where β = 1/kT
Entropy: S = −∂F/∂T = k·ln Z + ⟨E⟩/T
Ising Hamiltonian: H = −J·Σ_{⟨ij⟩} s_i·s_j − h·Σ_i s_i
Metropolis accept probability: min(1, e^(−ΔE/kT))
Phase Transitions & Critical Phenomena
The Ising model undergoes a second-order phase transition at the Curie temperature T_c ≈ 2.269 J/k (Onsager's exact 2D solution, 1944). Below T_c, the system breaks symmetry: spins align ferromagnetically. At T_c itself, the correlation length ξ → ∞, fluctuations occur at every length scale, and the system is described by a scale-invariant (conformal) field theory. This is why critical opalescence looks the same under a microscope or at arm's length.
Why does the Metropolis algorithm work? The Metropolis rule — always accept moves that lower energy, sometimes accept moves that raise it — satisfies detailed balance: the probability flux from state A to state B equals the flux from B to A at equilibrium. This guarantees the Markov chain converges to the Boltzmann distribution regardless of starting configuration.
Implementing Metropolis Monte Carlo
// Ising model Metropolis step (2D, periodic boundary)
function metropolisStep(spins, J, T) {
const N = spins.length;
const i = (Math.random() * N) | 0;
const j = (Math.random() * N) | 0;
// Energy change from flipping spin[i][j]
const s = spins[i][j];
const neighbours =
spins[(i + 1) % N][j] + spins[(i - 1 + N) % N][j] +
spins[i][(j + 1) % N] + spins[i][(j - 1 + N) % N];
const dE = 2 * J * s * neighbours;
// Accept / reject (Metropolis criterion)
if (dE <= 0 || Math.random() < Math.exp(-dE / T)) {
spins[i][j] = -s; // flip spin
}
}