Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 14 additions & 1 deletion NOTEBOOK_TUTORIALS.md
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,13 @@ examples/notebook/content/
│ ├── 04-grain-boundary.ipynb
│ ├── 05-fracture.ipynb
│ └── 06-nanoindentation.ipynb
├── md-basics/ # molecular dynamics concepts (LJ systems)
├── md-basics/ # SHIPPED: statistical mechanics you can watch
│ ├── 01-maxwell-boltzmann.ipynb
│ ├── 02-condensation.ipynb
│ ├── 03-real-vs-ideal-gas.ipynb
│ ├── 04-reversibility-and-chaos.ipynb
│ ├── 05-thermal-conductivity.ipynb
│ └── 06-polymer-chains.ipynb
├── materials/ # matsci-tutorials.pdf ports (needs MANYBODY)
├── soft-matter/ # LiveCoMS soft-matter ports (needs KSPACE/MOLECULE)
├── showcase/ # atomify example ports
Expand All @@ -112,6 +118,13 @@ examples/notebook/content/

### Series 1 — MD fundamentals with LJ systems (default wasm ✓)

Partially SHIPPED as `md-basics/`: Maxwell–Boltzmann relaxation from a
delta-function start, vapor→droplet condensation (with an optional KOKKOS
"go big" cell), real-vs-ideal gas with a numpy $B_2$ virial overlay,
Loschmidt velocity-reversal + chaos horizon, Müller-Plathe thermal
conductivity, and FENE bead-spring polymers with the Flory exponent
(data files generated by numpy). Remaining ideas below.

1. **Melting an fcc crystal** — energy/temperature vs time, spot the phase transition.
2. **Radial distribution function** — `compute rdf`, plot g(r) for solid vs liquid.
3. **Diffusion & mean-squared displacement** — Einstein relation, extract D from MSD slope (atomify has a `diffusion` example to port).
Expand Down
14 changes: 14 additions & 0 deletions examples/notebook/content/index.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,20 @@
"| [basics/04-multithreading-kokkos](basics/04-multithreading-kokkos.ipynb) | The multithreaded KOKKOS build |\n",
"| [basics/05-thermo-output-to-file](basics/05-thermo-output-to-file.ipynb) | The classic workflow: `fix ave/time` writes thermo to a file, a separate cell plots it |\n",
"\n",
"## Molecular dynamics fundamentals\n",
"\n",
"Statistical mechanics you can watch: each notebook takes one famous piece\n",
"of physics and makes it happen live, in a system small enough to see.\n",
"\n",
"| Notebook | The physics |\n",
"|---|---|\n",
"| [md-basics/01-maxwell-boltzmann](md-basics/01-maxwell-boltzmann.ipynb) | Start every atom at the same speed, watch the Maxwell–Boltzmann distribution assemble itself |\n",
"| [md-basics/02-condensation](md-basics/02-condensation.ipynb) | Quench a vapor below its condensation point: droplets nucleate and coarsen |\n",
"| [md-basics/03-real-vs-ideal-gas](md-basics/03-real-vs-ideal-gas.ipynb) | Measure Z = P/ρT, then predict it from the pair potential with a numpy virial integral |\n",
"| [md-basics/04-reversibility-and-chaos](md-basics/04-reversibility-and-chaos.ipynb) | Flip every velocity and un-melt a crystal — then find the chaos horizon where it fails |\n",
"| [md-basics/05-thermal-conductivity](md-basics/05-thermal-conductivity.ipynb) | A real transport measurement: Müller-Plathe flux, temperature gradient, κ |\n",
"| [md-basics/06-polymer-chains](md-basics/06-polymer-chains.ipynb) | Build bead-spring polymers with numpy, measure the Flory exponent |\n",
"\n",
"## Materials science: from a perfect crystal to failure\n",
"\n",
"A six-part course following the LiveCoMS\n",
Expand Down
193 changes: 193 additions & 0 deletions examples/notebook/content/md-basics/01-maxwell-boltzmann.ipynb
Original file line number Diff line number Diff line change
@@ -0,0 +1,193 @@
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# 01 — The Maxwell–Boltzmann distribution, live\n",
"\n",
"Give every atom in a gas *exactly the same speed*, pointing in random\n",
"directions — about as far from thermal equilibrium as a velocity\n",
"distribution gets. Then let them collide. Within a few hundred collisions\n",
"the most famous distribution in statistical mechanics assembles itself in\n",
"front of you: velocity components become Gaussian, speeds become\n",
"Maxwell–Boltzmann. No thermostat, no randomness added — just Newton's laws\n",
"(this is Boltzmann's H-theorem happening in real time).\n",
"\n",
"We use a 2D gas with purely repulsive interactions (the WCA potential — a\n",
"Lennard-Jones potential cut at its minimum), so collisions redistribute\n",
"energy without any of it hiding as potential energy."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"%pip install lammps-js matplotlib"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## A gas where every atom has the same speed\n",
"\n",
"`velocity create` draws random directions; two atom-style variables then\n",
"rescale every velocity to the same magnitude $v_0 = 1.5$. In LJ units\n",
"($m = k_B = 1$) the temperature in 2D is $T = \\langle v^2 \rangle / 2$,\n",
"so it starts at $v_0^2/2 = 1.125$ and stays within a few percent of it\n",
"(during a collision a little energy briefly sits in the repulsive\n",
"potential) while the distribution's shape changes completely."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"import numpy as np\n",
"import matplotlib.pyplot as plt\n",
"from lammps import lammps, LMP_VAR_ATOM\n",
"\n",
"lmp = await lammps(output=None)\n",
"lmp.commands_string(\"\"\"\n",
"units lj\n",
"dimension 2\n",
"lattice sq 0.3\n",
"region box block 0 32 0 32 -0.1 0.1\n",
"create_box 1 box\n",
"create_atoms 1 box\n",
"mass 1 1.0\n",
"pair_style lj/cut 1.122462\n",
"pair_coeff 1 1 1.0 1.0\n",
"pair_modify shift yes\n",
"\n",
"velocity all create 1.0 777 dist gaussian\n",
"variable v0 equal 1.5\n",
"variable s atom v_v0/sqrt(vx*vx+vy*vy)\n",
"variable ux atom vx*v_s\n",
"variable uy atom vy*v_s\n",
"velocity all set v_ux v_uy NULL\n",
"\n",
"variable vxa atom vx\n",
"variable spd atom sqrt(vx*vx+vy*vy)\n",
"\n",
"fix 1 all nve\n",
"fix 2d all enforce2d\n",
"run 0\n",
"\"\"\")\n",
"print(lmp.get_natoms(), \"atoms, all at speed 1.5, T =\", round(lmp.get_thermo(\"temp\"), 4))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Watch equilibrium assemble\n",
"\n",
"Sample the velocity components at a few moments. At $t=0$ every speed is\n",
"$v_0$, so the distribution of $v_x = v_0\\cos\\theta$ has spikes at\n",
"$\\pm v_0$ (an arcsine distribution — most of a circle is near its\n",
"edges). Then collisions take over:"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"snapshots = {}\n",
"for label, steps in [(\"t = 0\", 0), (\"after 100 steps\", 100),\n",
" (\"after 400 steps\", 300), (\"after 4000 steps\", 3600)]:\n",
" if steps:\n",
" lmp.command(f\"run {steps}\")\n",
" snapshots[label] = (lmp.extract_variable(\"vxa\", vartype=LMP_VAR_ATOM).copy(),\n",
" lmp.extract_variable(\"spd\", vartype=LMP_VAR_ATOM).copy())\n",
"T = lmp.get_thermo(\"temp\")\n",
"print(\"temperature after equilibration:\", round(T, 4), \"(started at v0²/2 = 1.125)\")"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"fig, axes = plt.subplots(2, 2, figsize=(9, 6), sharex=True)\n",
"v = np.linspace(-3.5, 3.5, 300)\n",
"gauss = np.exp(-v**2 / (2 * T)) / np.sqrt(2 * np.pi * T)\n",
"for ax, (label, (vx, _)) in zip(axes.flat, snapshots.items()):\n",
" ax.hist(vx, bins=45, range=(-3.5, 3.5), density=True, alpha=0.7)\n",
" ax.plot(v, gauss, \"k--\", lw=1.2, label=\"Gaussian, measured T\")\n",
" ax.set_title(label)\n",
" ax.legend(fontsize=8)\n",
"for ax in axes[1]:\n",
" ax.set_xlabel(\"$v_x$\")\n",
"for ax in axes[:, 0]:\n",
" ax.set_ylabel(\"probability density\")\n",
"fig.suptitle(\"A Gaussian velocity distribution assembles itself\")\n",
"fig.tight_layout()\n",
"plt.show()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Speeds: the 2D Maxwell–Boltzmann distribution\n",
"\n",
"Same data, but now the speed $|v|$. It starts as a delta function at\n",
"$v_0$ and relaxes to the 2D Maxwell–Boltzmann form\n",
"$f(v) = (v/T)\\,e^{-v^2/2T}$:"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"v = np.linspace(0, 4, 300)\n",
"mb2d = (v / T) * np.exp(-v**2 / (2 * T))\n",
"\n",
"plt.figure(figsize=(6, 3.6))\n",
"plt.hist(snapshots[\"t = 0\"][1], bins=45, range=(0, 4), density=True,\n",
" alpha=0.55, label=\"t = 0 (all speeds = 1.5)\")\n",
"plt.hist(snapshots[\"after 4000 steps\"][1], bins=45, range=(0, 4), density=True,\n",
" alpha=0.55, label=\"after 4000 steps\")\n",
"plt.plot(v, mb2d, \"k--\", lw=1.2, label=\"2D Maxwell–Boltzmann\")\n",
"plt.xlabel(\"speed $|v|$\"); plt.ylabel(\"probability density\")\n",
"plt.legend(); plt.tight_layout(); plt.show()\n",
"\n",
"lmp.close()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Every collision conserved energy and momentum exactly, yet the initial\n",
"condition was forgotten completely — only its total energy survives, as the\n",
"temperature setting the width of the Gaussian.\n",
"\n",
"Next: [02 — Condensation](02-condensation.ipynb), where the same gas is\n",
"quenched below its condensation point and droplets rain out."
]
}
],
"metadata": {
"kernelspec": {
"name": "python",
"display_name": "Python (Pyodide)"
},
"language_info": {
"name": "python"
}
},
"nbformat": 4,
"nbformat_minor": 5
}
Loading