Path: blob/main/latex-templates/templates/chemistry/molecular_dynamics.tex
75 views
unlisted
\documentclass[11pt,a4paper]{article}12% Essential packages3\usepackage[utf8]{inputenc}4\usepackage[T1]{fontenc}5\usepackage{amsmath,amssymb,amsthm}6\usepackage{graphicx}7\usepackage{float}8\usepackage{booktabs}9\usepackage{hyperref}10\usepackage{xcolor}11\usepackage{pythontex}12\usepackage[margin=1in]{geometry}1314% Custom commands15\newcommand{\dd}{\mathrm{d}}16\newcommand{\ee}{\mathrm{e}}17\newcommand{\vect}[1]{\mathbf{#1}}1819% Document metadata20\title{Molecular Dynamics Simulation:\\From Lennard-Jones Potentials to Thermodynamic Properties}21\author{Computational Chemistry}22\date{\today}2324\begin{document}2526\maketitle2728\begin{abstract}29Molecular dynamics (MD) simulations provide atomistic insight into the behavior of matter by solving Newton's equations of motion for systems of interacting particles. This document develops a complete MD simulation framework, starting from the Lennard-Jones potential for pairwise interactions, implementing the velocity Verlet integration algorithm, and extracting thermodynamic properties including temperature, pressure, and radial distribution functions. We simulate a simple Lennard-Jones fluid, demonstrating phase behavior and energy conservation.30\end{abstract}3132\tableofcontents33\newpage3435%------------------------------------------------------------------------------36\section{Introduction: Simulating Matter at the Atomic Scale}37%------------------------------------------------------------------------------3839Molecular dynamics simulations bridge the gap between quantum mechanical descriptions of molecular interactions and macroscopic thermodynamic properties. By numerically solving Newton's equations of motion for a system of $N$ particles, we can:4041\begin{itemize}42\item Observe molecular-level dynamics in real time43\item Calculate thermodynamic properties from first principles44\item Study phase transitions and transport phenomena45\item Investigate protein folding, chemical reactions, and material properties46\end{itemize}4748The fundamental algorithm is straightforward:49\begin{enumerate}50\item Define interatomic potentials that describe particle interactions51\item Initialize positions and velocities52\item Compute forces on each particle: $\vect{F}_i = -\nabla_i U$53\item Integrate equations of motion: $\vect{F}_i = m_i \ddot{\vect{r}}_i$54\item Repeat, collecting statistics along the trajectory55\end{enumerate}5657\begin{pycode}58import numpy as np59import matplotlib.pyplot as plt60from mpl_toolkits.mplot3d import Axes3D6162# Set random seed for reproducibility63np.random.seed(42)6465# Style configuration66plt.rcParams['figure.figsize'] = (10, 6)67plt.rcParams['font.size'] = 1168plt.rcParams['axes.labelsize'] = 1269plt.rcParams['axes.titlesize'] = 1470\end{pycode}7172%------------------------------------------------------------------------------73\section{The Lennard-Jones Potential}74%------------------------------------------------------------------------------7576The Lennard-Jones (LJ) potential is the most widely used model for van der Waals interactions between neutral atoms:7778\begin{equation}79U_{\text{LJ}}(r) = 4\varepsilon \left[\left(\frac{\sigma}{r}\right)^{12} - \left(\frac{\sigma}{r}\right)^{6}\right]80\label{eq:lennard-jones}81\end{equation}8283where:84\begin{itemize}85\item $\varepsilon$ is the depth of the potential well (energy scale)86\item $\sigma$ is the distance at which $U = 0$ (length scale)87\item $r$ is the interparticle distance88\end{itemize}8990The $r^{-12}$ term models Pauli repulsion at short range, while the $r^{-6}$ term models the attractive London dispersion force. The potential has a minimum at $r_{\text{min}} = 2^{1/6}\sigma \approx 1.122\sigma$ with depth $-\varepsilon$.9192\subsection{Force Derivation}9394The force between particles is the negative gradient of the potential:9596\begin{equation}97F_{\text{LJ}}(r) = -\frac{\dd U_{\text{LJ}}}{\dd r} = \frac{24\varepsilon}{r}\left[2\left(\frac{\sigma}{r}\right)^{12} - \left(\frac{\sigma}{r}\right)^{6}\right]98\label{eq:lj-force}99\end{equation}100101In vector form, the force on particle $i$ due to particle $j$ is:102103\begin{equation}104\vect{F}_{ij} = F_{\text{LJ}}(r_{ij}) \frac{\vect{r}_{ij}}{r_{ij}}105\end{equation}106107\begin{pycode}108# Lennard-Jones potential and force109def lennard_jones_potential(r, epsilon=1.0, sigma=1.0):110"""Lennard-Jones pair potential."""111sr6 = (sigma / r)**6112sr12 = sr6**2113return 4 * epsilon * (sr12 - sr6)114115def lennard_jones_force(r, epsilon=1.0, sigma=1.0):116"""Magnitude of Lennard-Jones force."""117sr6 = (sigma / r)**6118sr12 = sr6**2119return 24 * epsilon / r * (2 * sr12 - sr6)120121# Plot potential and force122fig, axes = plt.subplots(1, 2, figsize=(12, 5))123124r = np.linspace(0.9, 3.0, 500)125U = lennard_jones_potential(r)126F = lennard_jones_force(r)127128# Left: Potential129ax = axes[0]130ax.plot(r, U, 'b-', linewidth=2)131ax.axhline(0, color='gray', linestyle='--', alpha=0.5)132ax.axvline(2**(1/6), color='red', linestyle=':', alpha=0.7, label=r'$r_{min} = 2^{1/6}\sigma$')133134# Mark key points135r_min = 2**(1/6)136ax.plot(r_min, -1, 'ro', markersize=8)137ax.annotate(r'$(-\varepsilon)$', xy=(r_min, -1), xytext=(r_min + 0.2, -0.7),138fontsize=11, color='red')139ax.plot(1.0, 0, 'go', markersize=8)140ax.annotate(r'$\sigma$', xy=(1.0, 0), xytext=(0.85, 0.5), fontsize=11, color='green')141142ax.set_xlabel(r'Distance $r/\sigma$')143ax.set_ylabel(r'Potential $U/\varepsilon$')144ax.set_title('Lennard-Jones Potential')145ax.set_xlim(0.9, 3.0)146ax.set_ylim(-1.5, 2.0)147ax.legend()148ax.grid(True, alpha=0.3)149150# Right: Force151ax = axes[1]152ax.plot(r, F, 'r-', linewidth=2)153ax.axhline(0, color='gray', linestyle='--', alpha=0.5)154ax.axvline(2**(1/6), color='blue', linestyle=':', alpha=0.7)155156ax.fill_between(r[F > 0], 0, F[F > 0], alpha=0.3, color='red', label='Repulsive')157ax.fill_between(r[F < 0], 0, F[F < 0], alpha=0.3, color='blue', label='Attractive')158159ax.set_xlabel(r'Distance $r/\sigma$')160ax.set_ylabel(r'Force $F\sigma/\varepsilon$')161ax.set_title('Lennard-Jones Force')162ax.set_xlim(0.9, 3.0)163ax.set_ylim(-3, 5)164ax.legend()165ax.grid(True, alpha=0.3)166167plt.tight_layout()168plt.savefig('molecular_dynamics_potential.pdf', bbox_inches='tight')169plt.close()170\end{pycode}171172\begin{figure}[H]173\centering174\includegraphics[width=\textwidth]{molecular_dynamics_potential.pdf}175\caption{The Lennard-Jones potential and its derivative. Left: The potential energy curve shows strong repulsion at short range ($r < \sigma$), a minimum at $r_{\text{min}} = 2^{1/6}\sigma$ with depth $-\varepsilon$, and asymptotic approach to zero at large distances. Right: The force is repulsive (positive) for $r < r_{\text{min}}$ and attractive (negative) for $r > r_{\text{min}}$. At the equilibrium distance $r_{\text{min}}$, the force is zero.}176\label{fig:lj-potential}177\end{figure}178179%------------------------------------------------------------------------------180\section{Reduced Units}181%------------------------------------------------------------------------------182183MD simulations typically use \textit{reduced units} where $\varepsilon$, $\sigma$, and the particle mass $m$ are set to unity. All quantities are then expressed in terms of these fundamental scales:184185\begin{table}[H]186\centering187\begin{tabular}{lcc}188\toprule189Quantity & Reduced Unit & For Argon \\190\midrule191Length & $\sigma$ & 3.4 \AA \\192Energy & $\varepsilon$ & $1.65 \times 10^{-21}$ J \\193Mass & $m$ & $6.63 \times 10^{-26}$ kg \\194Time & $\sigma\sqrt{m/\varepsilon}$ & 2.15 ps \\195Temperature & $\varepsilon/k_B$ & 120 K \\196Pressure & $\varepsilon/\sigma^3$ & 42 MPa \\197\bottomrule198\end{tabular}199\caption{Reduced units and their values for liquid argon.}200\label{tab:units}201\end{table}202203%------------------------------------------------------------------------------204\section{The Velocity Verlet Algorithm}205%------------------------------------------------------------------------------206207The velocity Verlet algorithm is the most popular integrator for MD due to its time-reversibility, symplectic nature, and good energy conservation:208209\begin{align}210\vect{r}(t + \Delta t) &= \vect{r}(t) + \vect{v}(t)\Delta t + \frac{1}{2}\vect{a}(t)\Delta t^2 \label{eq:verlet-r}\\211\vect{v}(t + \Delta t) &= \vect{v}(t) + \frac{1}{2}[\vect{a}(t) + \vect{a}(t + \Delta t)]\Delta t \label{eq:verlet-v}212\end{align}213214where $\vect{a} = \vect{F}/m$. The algorithm proceeds as:215\begin{enumerate}216\item Calculate forces at time $t$217\item Update positions using Eq.~\eqref{eq:verlet-r}218\item Calculate forces at time $t + \Delta t$219\item Update velocities using Eq.~\eqref{eq:verlet-v}220\end{enumerate}221222\begin{pycode}223class LennardJonesSimulation:224"""225Molecular dynamics simulation of a Lennard-Jones fluid.226227Uses reduced units: epsilon = sigma = mass = 1228"""229230def __init__(self, n_particles, box_length, temperature, dt=0.002, cutoff=2.5):231"""232Initialize the simulation.233234Parameters:235-----------236n_particles : int237Number of particles238box_length : float239Side length of cubic simulation box240temperature : float241Initial temperature (reduced units)242dt : float243Time step (reduced units)244cutoff : float245Potential cutoff distance246"""247self.n = n_particles248self.L = box_length249self.T = temperature250self.dt = dt251self.rc = cutoff252self.rc2 = cutoff**2253254# Potential shift for continuity at cutoff255self.U_shift = 4 * ((1/cutoff)**12 - (1/cutoff)**6)256257# Initialize positions on a lattice258self.positions = self._init_lattice()259260# Initialize velocities from Maxwell-Boltzmann distribution261self.velocities = self._init_velocities()262263# Force array264self.forces = np.zeros_like(self.positions)265266# Observables267self.potential_energy = 0268self.kinetic_energy = 0269self.virial = 0270271def _init_lattice(self):272"""Place particles on a simple cubic lattice."""273n_side = int(np.ceil(self.n**(1/3)))274spacing = self.L / n_side275positions = []276277for i in range(n_side):278for j in range(n_side):279for k in range(n_side):280if len(positions) < self.n:281positions.append([282(i + 0.5) * spacing,283(j + 0.5) * spacing,284(k + 0.5) * spacing285])286287return np.array(positions)288289def _init_velocities(self):290"""Initialize velocities from Maxwell-Boltzmann distribution."""291velocities = np.random.randn(self.n, 3) * np.sqrt(self.T)292293# Remove center of mass motion294velocities -= np.mean(velocities, axis=0)295296# Scale to exact temperature297ke = 0.5 * np.sum(velocities**2)298current_T = 2 * ke / (3 * self.n)299velocities *= np.sqrt(self.T / current_T)300301return velocities302303def _minimum_image(self, dr):304"""Apply minimum image convention for periodic boundaries."""305return dr - self.L * np.round(dr / self.L)306307def compute_forces(self):308"""309Compute forces, potential energy, and virial.310311Uses the minimum image convention for periodic boundaries.312"""313self.forces.fill(0)314self.potential_energy = 0315self.virial = 0316317for i in range(self.n - 1):318for j in range(i + 1, self.n):319dr = self.positions[j] - self.positions[i]320dr = self._minimum_image(dr)321r2 = np.sum(dr**2)322323if r2 < self.rc2:324r2_inv = 1 / r2325r6_inv = r2_inv**3326r12_inv = r6_inv**2327328# Potential (shifted)329U = 4 * (r12_inv - r6_inv) - self.U_shift330self.potential_energy += U331332# Force magnitude / r333f_over_r = 24 * r2_inv * (2 * r12_inv - r6_inv)334335# Force vector336f = f_over_r * dr337self.forces[i] -= f338self.forces[j] += f339340# Virial for pressure calculation341self.virial += f_over_r * r2342343def velocity_verlet_step(self):344"""Perform one velocity Verlet integration step."""345# Half-step velocity update346self.velocities += 0.5 * self.forces * self.dt347348# Full position update349self.positions += self.velocities * self.dt350351# Apply periodic boundary conditions352self.positions = self.positions % self.L353354# Compute new forces355self.compute_forces()356357# Complete velocity update358self.velocities += 0.5 * self.forces * self.dt359360# Calculate kinetic energy361self.kinetic_energy = 0.5 * np.sum(self.velocities**2)362363def get_temperature(self):364"""Calculate instantaneous temperature."""365return 2 * self.kinetic_energy / (3 * self.n)366367def get_pressure(self):368"""Calculate pressure using virial theorem."""369density = self.n / self.L**3370T = self.get_temperature()371return density * T + self.virial / (3 * self.L**3)372373def get_total_energy(self):374"""Calculate total energy."""375return self.kinetic_energy + self.potential_energy376377def apply_thermostat(self, target_T):378"""Velocity rescaling thermostat."""379current_T = self.get_temperature()380if current_T > 0:381scale = np.sqrt(target_T / current_T)382self.velocities *= scale383384# Run a short simulation385np.random.seed(42)386n_particles = 108 # 4x4x4 FCC-like arrangement387density = 0.8 # Reduced density (liquid state)388box_length = (n_particles / density)**(1/3)389temperature = 1.0 # Reduced temperature390391sim = LennardJonesSimulation(n_particles, box_length, temperature)392sim.compute_forces()393394# Equilibration with thermostat395n_equil = 500396for _ in range(n_equil):397sim.velocity_verlet_step()398if _ % 50 == 0:399sim.apply_thermostat(temperature)400401# Production run (NVE - constant energy)402n_steps = 2000403energies = {'kinetic': [], 'potential': [], 'total': []}404temperatures = []405pressures = []406407for step in range(n_steps):408sim.velocity_verlet_step()409410energies['kinetic'].append(sim.kinetic_energy / n_particles)411energies['potential'].append(sim.potential_energy / n_particles)412energies['total'].append(sim.get_total_energy() / n_particles)413temperatures.append(sim.get_temperature())414pressures.append(sim.get_pressure())415416# Convert to arrays417for key in energies:418energies[key] = np.array(energies[key])419temperatures = np.array(temperatures)420pressures = np.array(pressures)421time_array = np.arange(n_steps) * sim.dt422\end{pycode}423424%------------------------------------------------------------------------------425\section{Simulation Results}426%------------------------------------------------------------------------------427428\subsection{Energy Conservation}429430A hallmark of a good MD integrator is energy conservation in the microcanonical (NVE) ensemble. The velocity Verlet algorithm achieves excellent long-term energy conservation.431432\begin{pycode}433# Energy conservation plot434fig, axes = plt.subplots(2, 2, figsize=(12, 10))435436# Top left: Energy components437ax = axes[0, 0]438ax.plot(time_array, energies['kinetic'], 'r-', alpha=0.7, label='Kinetic')439ax.plot(time_array, energies['potential'], 'b-', alpha=0.7, label='Potential')440ax.plot(time_array, energies['total'], 'k-', linewidth=2, label='Total')441442ax.set_xlabel('Time (reduced units)')443ax.set_ylabel('Energy per particle')444ax.set_title('Energy Components')445ax.legend()446ax.grid(True, alpha=0.3)447448# Top right: Energy conservation detail449ax = axes[0, 1]450E_total = energies['total']451E_mean = np.mean(E_total)452E_drift = (E_total - E_total[0]) * 100 / np.abs(E_mean) # Percentage drift453454ax.plot(time_array, E_drift, 'k-', linewidth=1)455ax.axhline(0, color='gray', linestyle='--', alpha=0.5)456457ax.set_xlabel('Time (reduced units)')458ax.set_ylabel('Energy drift (%)')459ax.set_title(f'Energy Conservation (drift: {np.std(E_drift):.4f}%)')460ax.grid(True, alpha=0.3)461462# Bottom left: Temperature fluctuations463ax = axes[1, 0]464ax.plot(time_array, temperatures, 'r-', alpha=0.7)465ax.axhline(np.mean(temperatures), color='k', linestyle='--',466label=f'Mean: {np.mean(temperatures):.3f}')467ax.fill_between(time_array,468np.mean(temperatures) - np.std(temperatures),469np.mean(temperatures) + np.std(temperatures),470alpha=0.2, color='red')471472ax.set_xlabel('Time (reduced units)')473ax.set_ylabel('Temperature')474ax.set_title(f'Temperature (std: {np.std(temperatures):.3f})')475ax.legend()476ax.grid(True, alpha=0.3)477478# Bottom right: Pressure fluctuations479ax = axes[1, 1]480ax.plot(time_array, pressures, 'b-', alpha=0.7)481ax.axhline(np.mean(pressures), color='k', linestyle='--',482label=f'Mean: {np.mean(pressures):.3f}')483484ax.set_xlabel('Time (reduced units)')485ax.set_ylabel('Pressure')486ax.set_title(f'Pressure (std: {np.std(pressures):.3f})')487ax.legend()488ax.grid(True, alpha=0.3)489490plt.tight_layout()491plt.savefig('molecular_dynamics_energy.pdf', bbox_inches='tight')492plt.close()493\end{pycode}494495\begin{figure}[H]496\centering497\includegraphics[width=\textwidth]{molecular_dynamics_energy.pdf}498\caption{Thermodynamic properties during MD simulation. Top left: Energy components showing the exchange between kinetic and potential energy while total energy remains constant. Top right: Energy drift as a percentage of mean energy, demonstrating excellent conservation by the velocity Verlet integrator. Bottom left: Temperature fluctuations around the mean---these are expected in the NVE ensemble and decrease as $1/\sqrt{N}$. Bottom right: Pressure fluctuations; the mean pressure includes both ideal gas and virial contributions.}499\label{fig:energy}500\end{figure}501502\subsection{Radial Distribution Function}503504The radial distribution function (RDF) $g(r)$ measures the probability of finding a particle at distance $r$ from another particle, relative to an ideal gas. It provides structural information about the fluid:505506\begin{equation}507g(r) = \frac{V}{N^2} \left\langle \sum_i \sum_{j \neq i} \delta(r - r_{ij}) \right\rangle508\end{equation}509510\begin{pycode}511def compute_rdf(positions, box_length, n_bins=100, r_max=None):512"""513Compute the radial distribution function.514515Parameters:516-----------517positions : ndarray518Particle positions (N x 3)519box_length : float520Simulation box size521n_bins : int522Number of histogram bins523r_max : float524Maximum distance (default: half box length)525"""526if r_max is None:527r_max = box_length / 2528529n = len(positions)530dr = r_max / n_bins531hist = np.zeros(n_bins)532533for i in range(n - 1):534for j in range(i + 1, n):535dpos = positions[j] - positions[i]536dpos = dpos - box_length * np.round(dpos / box_length)537r = np.sqrt(np.sum(dpos**2))538539if r < r_max:540bin_idx = int(r / dr)541hist[bin_idx] += 2 # Count both i-j and j-i542543# Normalize544r_edges = np.linspace(0, r_max, n_bins + 1)545r_centers = 0.5 * (r_edges[:-1] + r_edges[1:])546shell_volumes = 4 * np.pi * r_centers**2 * dr547548density = n / box_length**3549g_r = hist / (n * density * shell_volumes)550551return r_centers, g_r552553# Collect RDF samples554rdf_samples = []555for _ in range(200):556sim.velocity_verlet_step()557if _ % 10 == 0:558r_vals, g_vals = compute_rdf(sim.positions, sim.L)559rdf_samples.append(g_vals)560561g_mean = np.mean(rdf_samples, axis=0)562g_std = np.std(rdf_samples, axis=0)563564# Plot RDF565fig, ax = plt.subplots(figsize=(10, 6))566567ax.plot(r_vals, g_mean, 'b-', linewidth=2, label='Simulation')568ax.fill_between(r_vals, g_mean - g_std, g_mean + g_std, alpha=0.3, color='blue')569ax.axhline(1, color='gray', linestyle='--', alpha=0.7, label='Ideal gas')570571# Mark key features572peaks = [1.0, 1.95] # Approximate peak positions for LJ liquid573for i, p in enumerate(peaks):574idx = np.argmin(np.abs(r_vals - p))575if g_mean[idx] > 1:576ax.annotate(f'{i+1}st shell', xy=(r_vals[idx], g_mean[idx]),577xytext=(r_vals[idx] + 0.2, g_mean[idx] + 0.5),578arrowprops=dict(arrowstyle='->', color='red'),579fontsize=10, color='red')580581ax.set_xlabel(r'Distance $r/\sigma$')582ax.set_ylabel(r'$g(r)$')583ax.set_title(f'Radial Distribution Function ($\\rho^* = {density:.1f}$, $T^* = {temperature:.1f}$)')584ax.legend()585ax.grid(True, alpha=0.3)586ax.set_xlim(0, sim.L / 2)587ax.set_ylim(0, 3.5)588589plt.tight_layout()590plt.savefig('molecular_dynamics_rdf.pdf', bbox_inches='tight')591plt.close()592\end{pycode}593594\begin{figure}[H]595\centering596\includegraphics[width=0.9\textwidth]{molecular_dynamics_rdf.pdf}597\caption{Radial distribution function $g(r)$ for the Lennard-Jones fluid at liquid density ($\rho^* = 0.8$) and temperature ($T^* = 1.0$). The first peak near $r = \sigma$ corresponds to the first coordination shell of nearest neighbors. The second peak indicates the second shell. At large distances, $g(r) \to 1$ (ideal gas behavior). The oscillatory structure is characteristic of liquids, contrasting with the delta-function peaks of solids and the uniform $g(r) = 1$ of gases.}598\label{fig:rdf}599\end{figure}600601\subsection{Phase Diagram Exploration}602603The Lennard-Jones system exhibits gas, liquid, and solid phases. The phase depends on reduced density $\rho^* = \rho\sigma^3$ and reduced temperature $T^* = k_BT/\varepsilon$.604605\begin{pycode}606# Scan different state points607state_points = [608(0.1, 2.0, 'Gas'),609(0.8, 1.0, 'Liquid'),610(1.0, 0.5, 'Solid'),611]612613fig, axes = plt.subplots(1, 3, figsize=(15, 5))614615for ax, (density, temp, phase) in zip(axes, state_points):616# Quick simulation at this state point617np.random.seed(42)618n_test = 64619L_test = (n_test / density)**(1/3)620621sim_test = LennardJonesSimulation(n_test, L_test, temp)622sim_test.compute_forces()623624# Short equilibration625for _ in range(300):626sim_test.velocity_verlet_step()627if _ % 20 == 0:628sim_test.apply_thermostat(temp)629630# Plot 3D snapshot631pos = sim_test.positions632ax.scatter(pos[:, 0], pos[:, 1], c=pos[:, 2], cmap='viridis',633s=500, alpha=0.7, edgecolors='white')634635ax.set_xlim(0, L_test)636ax.set_ylim(0, L_test)637ax.set_xlabel('x')638ax.set_ylabel('y')639ax.set_title(f'{phase}\n($\\rho^* = {density}$, $T^* = {temp}$)')640ax.set_aspect('equal')641ax.grid(True, alpha=0.3)642643plt.tight_layout()644plt.savefig('molecular_dynamics_phases.pdf', bbox_inches='tight')645plt.close()646\end{pycode}647648\begin{figure}[H]649\centering650\includegraphics[width=\textwidth]{molecular_dynamics_phases.pdf}651\caption{Snapshots of the Lennard-Jones system in different phases (2D projection, colored by z-coordinate). Left: Gas phase at low density---particles are well-separated with no persistent structure. Center: Liquid phase at intermediate density---particles form a disordered but cohesive structure with short-range order. Right: Solid phase at high density and low temperature---particles arrange in an ordered crystalline lattice. These phases emerge naturally from the interplay between kinetic energy (temperature) and potential energy (density).}652\label{fig:phases}653\end{figure}654655%------------------------------------------------------------------------------656\section{Conclusions}657%------------------------------------------------------------------------------658659This document has presented a complete molecular dynamics simulation framework:660661\begin{enumerate}662\item \textbf{Lennard-Jones potential}: The $12$-$6$ form captures essential physics of neutral atom interactions, with repulsion at short range and attraction at longer distances.663664\item \textbf{Velocity Verlet integration}: This symplectic integrator provides excellent energy conservation, enabling accurate NVE simulations over long timescales.665666\item \textbf{Thermodynamic properties}: Temperature, pressure, and energy are directly calculable from particle positions and velocities. The virial theorem connects microscopic forces to macroscopic pressure.667668\item \textbf{Structural analysis}: The radial distribution function reveals the characteristic short-range order of liquids, distinguishing them from gases and solids.669670\item \textbf{Phase behavior}: By varying density and temperature, the simulation captures gas, liquid, and solid phases---all emerging from the same simple interatomic potential.671\end{enumerate}672673The simulated system of \pyc{print(n_particles)} particles showed:674\begin{itemize}675\item Energy conservation to \pyc{print(f'{np.std(E_drift):.4f}')}{\%}676\item Mean temperature: \pyc{print(f'{np.mean(temperatures):.3f}')} (target: \pyc{print(f'{temperature:.1f}')})677\item Mean pressure: \pyc{print(f'{np.mean(pressures):.3f}')} (reduced units)678\item First RDF peak at $r \approx \sigma$, characteristic of liquid structure679\end{itemize}680681Extensions of this framework include more realistic potentials (EAM for metals, force fields for biomolecules), constant temperature and pressure ensembles, long-range electrostatics (Ewald summation), and reactive MD for chemical transformations.682683%------------------------------------------------------------------------------684\section{References}685%------------------------------------------------------------------------------686687\begin{enumerate}688\item Allen, M.P. \& Tildesley, D.J. (2017). \textit{Computer Simulation of Liquids}, 2nd ed. Oxford University Press.689690\item Frenkel, D. \& Smit, B. (2002). \textit{Understanding Molecular Simulation}, 2nd ed. Academic Press.691692\item Rapaport, D.C. (2004). \textit{The Art of Molecular Dynamics Simulation}, 2nd ed. Cambridge University Press.693694\item Lennard-Jones, J.E. (1924). On the Determination of Molecular Fields. \textit{Proc. R. Soc. Lond. A}, 106(738), 463-477.695696\item Verlet, L. (1967). Computer ``Experiments'' on Classical Fluids. \textit{Physical Review}, 159(1), 98-103.697698\item Hansen, J.P. \& Verlet, L. (1969). Phase Transitions of the Lennard-Jones System. \textit{Physical Review}, 184(1), 151-161.699\end{enumerate}700701\end{document}702703704