{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# 1.6 signac-flow HOOMD-blue Example\n", "\n", "## About\n", "\n", "This notebook contains a minimal example for running a *signac-flow* project from scratch.\n", "The example demonstrates how to compare an ideal gas with a Lennard-Jones (LJ) fluid by calculating a p-V phase diagram.\n", "\n", "This examples uses the general-purpose simulation toolkit [HOOMD-blue](http://glotzerlab.engin.umich.edu/hoomd-blue/) for the execution of the molecular-dynamics (MD) simulations.\n", "\n", "## Author\n", "\n", "Carl Simon Adorf, Bradley Dice\n", "\n", "## Before you start\n", "\n", "This example requires signac, signac-flow, HOOMD-blue, gsd, and numpy.\n", "You can install these package for example via conda:\n", "```\n", "conda install -c conda-forge gsd hoomd numpy signac signac-flow\n", "```" ] }, { "cell_type": "code", "execution_count": 1, "metadata": { "tags": [] }, "outputs": [], "source": [ "import itertools\n", "import os\n", "import warnings\n", "\n", "import flow\n", "import gsd.hoomd\n", "import hoomd\n", "import numpy as np\n", "import signac\n", "\n", "project_path = \"projects/tutorial-signac-flow-hoomd\"\n", "\n", "\n", "class MyProject(flow.FlowProject):\n", " pass" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We want to generate a pressure-volume phase diagram for a Lennard-Jones fluid with molecular dynamics (MD) using the general-purpose simulation toolkit HOOMD-blue (http://glotzerlab.engin.umich.edu/hoomd-blue/).\n", "\n", "We start by defining two functions, one for the initialization of our simulation and one for the actual execution." ] }, { "cell_type": "code", "execution_count": 2, "metadata": { "tags": [] }, "outputs": [], "source": [ "from contextlib import contextmanager\n", "from math import ceil\n", "\n", "\n", "def init(N):\n", " frame = gsd.hoomd.Frame()\n", " frame.particles.N = N\n", " frame.particles.types = [\"A\"]\n", " frame.particles.typeid = np.zeros(N)\n", "\n", " n = ceil(pow(N, 1 / 3))\n", " assert n**3 == N\n", " spacing = 1.2\n", " L = n * spacing\n", " frame.configuration.box = [L, L, L, 0.0, 0.0, 0.0]\n", " x = np.linspace(-L / 2, L / 2, n, endpoint=False)\n", " position = list(itertools.product(x, repeat=3))\n", " frame.particles.position = position\n", " with gsd.hoomd.open(\"init.gsd\", \"w\") as traj:\n", " traj.append(frame)\n", "\n", "\n", "@contextmanager\n", "def get_sim(N, sigma, seed, kT, tau, p, tauP, r_cut):\n", " sim = hoomd.Simulation(hoomd.device.CPU())\n", " state_fn = \"init.gsd\"\n", " if os.path.exists(\"restart.gsd\"):\n", " state_fn = \"restart.gsd\"\n", " sim.create_state_from_gsd(state_fn)\n", " sim.operations += hoomd.write.GSD(\n", " filename=\"restart.gsd\", truncate=True, trigger=100, filter=hoomd.filter.All()\n", " )\n", " lj = hoomd.md.pair.LJ(default_r_cut=r_cut, nlist=hoomd.md.nlist.Cell(0.4))\n", " lj.params[(\"A\", \"A\")] = {\"epsilon\": 1.0, \"sigma\": sigma}\n", " integrator = hoomd.md.Integrator(\n", " dt=0.005,\n", " methods=[\n", " hoomd.md.methods.ConstantPressure(\n", " filter=hoomd.filter.All(),\n", " S=p,\n", " tauS=tauP,\n", " couple=\"xyz\",\n", " thermostat=hoomd.md.methods.thermostats.MTTK(kT=kT, tau=tau),\n", " )\n", " ],\n", " forces=[lj],\n", " )\n", " sim.operations.integrator = integrator\n", " logger = hoomd.logging.Logger(categories=(\"scalar\", \"string\"))\n", " logger[\"Volume\"] = (lambda: sim.state.box.volume, \"scalar\")\n", "\n", " # This context manager keeps the log file open while the simulation runs.\n", " with open(\"dump.log\", \"w+\") as outfile:\n", " table = hoomd.write.Table(\n", " trigger=100, logger=logger, output=outfile, pretty=False\n", " )\n", " sim.operations += table\n", " yield sim" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We want to use **signac** to manage our simulation data and **signac-flow** to define a workflow acting on the data space.\n", "\n", "Now that we have defined the core simulation logic above, it's time to embed those functions into a general *workflow*.\n", "For this purpose we add labels and operations with preconditions/postconditions to `MyProject`, a subclass of `flow.FlowProject`.\n", "\n", "The `estimate` operation stores an ideal gas estimation of the volume for the given system.\n", "The `sample` operation actually executes the MD simulation as defined in the previous cell." ] }, { "cell_type": "code", "execution_count": 3, "metadata": { "tags": [] }, "outputs": [], "source": [ "@MyProject.label\n", "def estimated(job):\n", " return \"V\" in job.document\n", "\n", "\n", "@MyProject.label\n", "def sampled(job):\n", " return job.document.get(\"sample_step\", 0) >= 5000\n", "\n", "\n", "@MyProject.post(estimated)\n", "@MyProject.operation\n", "def estimate(job):\n", " sp = job.statepoint()\n", " job.document[\"V\"] = sp[\"N\"] * sp[\"kT\"] / sp[\"p\"]\n", "\n", "\n", "@MyProject.post(sampled)\n", "@MyProject.operation\n", "def sample(job):\n", " with job:\n", " with get_sim(**job.statepoint()) as sim:\n", " try:\n", " sim.run(5_000)\n", " finally:\n", " job.document[\"sample_step\"] = sim.timestep" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Now it's time to actually generate some data! Let's initialize the data space!\n" ] }, { "cell_type": "code", "execution_count": 4, "metadata": { "tags": [] }, "outputs": [], "source": [ "project = MyProject.init_project(project_path)\n", "\n", "# Uncomment the following two lines if you want to start over!\n", "# for job in project:\n", "# job.remove()\n", "\n", "for p in np.linspace(0.5, 5.0, 10):\n", " sp = dict(\n", " N=512, sigma=1.0, seed=42, kT=1.0, p=float(p), tau=1.0, tauP=1.0, r_cut=2.5\n", " )\n", " job = project.open_job(sp).init()\n", " with job:\n", " init(N=sp[\"N\"])" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The `print_status()` function allows to get a quick overview of our project's *status*:" ] }, { "cell_type": "code", "execution_count": 5, "metadata": { "tags": [] }, "outputs": [ { "data": { "application/vnd.jupyter.widget-view+json": { "model_id": "a3b1ced62d5e47b2a84fe1a77c14a2ad", "version_major": 2, "version_minor": 0 }, "text/plain": [ "Fetching status: 0%| | 0/20 [00:00" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "%matplotlib inline\n", "# Display plots within the notebook\n", "\n", "from matplotlib import pyplot as plt\n", "\n", "V = dict()\n", "V_idg = dict()\n", "\n", "for job in project:\n", " V[job.statepoint()[\"p\"]] = get_volume(job)\n", " V_idg[job.statepoint()[\"p\"]] = job.document[\"V\"]\n", "\n", "p = sorted(V.keys())\n", "V = [V[p_] for p_ in p]\n", "V_idg = [V_idg[p_] for p_ in p]\n", "\n", "plt.plot(p, V, label=\"LJ\")\n", "plt.plot(p, V_idg, label=\"idG\")\n", "plt.xlabel(r\"pressure [$\\epsilon / \\sigma^3$]\")\n", "plt.ylabel(r\"volume [$\\sigma^3$]\")\n", "plt.legend()\n", "plt.show()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Uncomment and execute the following line to remove all data and start over." ] }, { "cell_type": "code", "execution_count": 10, "metadata": { "tags": [] }, "outputs": [], "source": [ "# %rm -r projects/tutorial-signac-flow-hoomd-blue/workspace" ] } ], "metadata": { "kernelspec": { "display_name": "Python 3 (ipykernel)", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.12.0" } }, "nbformat": 4, "nbformat_minor": 4 }