From 19e70e66f83c819d5ed8e58565c9c986e4229fd9 Mon Sep 17 00:00:00 2001 From: Pablo Zubieta <8410335+pabloferz@users.noreply.github.com> Date: Thu, 31 Oct 2024 01:29:52 -0500 Subject: [PATCH] Update OpenMM Colab notebooks --- examples/openmm/Harmonic_Bias.ipynb | 459 ------------------ examples/openmm/Harmonic_Bias.md | 224 --------- examples/openmm/metad/Metadynamics-ADP.ipynb | 127 ++--- examples/openmm/metad/Metadynamics-ADP.md | 100 ++-- .../openmm/spectral_abf/ADP_SpectralABF.ipynb | 114 ++--- .../openmm/spectral_abf/ADP_SpectralABF.md | 88 +--- 6 files changed, 119 insertions(+), 993 deletions(-) delete mode 100644 examples/openmm/Harmonic_Bias.ipynb delete mode 100644 examples/openmm/Harmonic_Bias.md diff --git a/examples/openmm/Harmonic_Bias.ipynb b/examples/openmm/Harmonic_Bias.ipynb deleted file mode 100644 index 678a321f..00000000 --- a/examples/openmm/Harmonic_Bias.ipynb +++ /dev/null @@ -1,459 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": { - "id": "_UgEohXC8n0g" - }, - "source": [ - "\n", - "# Setting up the environment\n", - "\n", - "First, we are setting up our environment. We use an already compiled and packaged installation of OpenMM and the DLExt plugin. We copy it from Google Drive and install PySAGES for it. We also have a Google Colab that performs this installation for reference, but that requires permissions that we do not want on our Google Drive.\n" - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "metadata": { - "id": "nMThqa-DjVcb" - }, - "outputs": [], - "source": [ - "%%bash\n", - "\n", - "BASE_URL=\"https://drive.usercontent.google.com/download?id=1hsKkKtdxZTVfHKgqVF6qV2e-4SShmhr7\"\n", - "wget -q --load-cookies /tmp/cookies.txt \"$BASE_URL&confirm=$(wget -q --save-cookies /tmp/cookies.txt --keep-session-cookies --no-check-certificate $BASE_URL -O- | sed -rn 's/.*confirm=(\\w+).*/\\1\\n/p')\" -O pysages-env.zip\n", - "rm -rf /tmp/cookies.txt" - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "metadata": { - "colab": { - "base_uri": "https://localhost:8080/" - }, - "id": "25H3kl03wzJe", - "outputId": "528d12be-8cc4-42d9-d460-692d46a0e662" - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "env: PYSAGES_ENV=/env/pysages\n" - ] - } - ], - "source": [ - "%env PYSAGES_ENV=/env/pysages" - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "metadata": { - "id": "CPkgxfj6w4te" - }, - "outputs": [], - "source": [ - "%%bash\n", - "\n", - "mkdir -p $PYSAGES_ENV .\n", - "unzip -qquo pysages-env.zip -d $PYSAGES_ENV" - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "metadata": { - "id": "JMO5fiRTxAWB" - }, - "outputs": [], - "source": [ - "import os\n", - "import sys\n", - "\n", - "ver = sys.version_info\n", - "\n", - "sys.path.append(os.environ[\"PYSAGES_ENV\"] + \"/lib/python\" + str(ver.major) + \".\" + str(ver.minor) + \"/site-packages/\")" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "lf2KeHt5_eFv" - }, - "source": [ - "\n", - "## PySAGES\n", - "\n", - "The next step is to install PySAGES.\n", - "First, we install the jaxlib version that matches the CUDA installation of this Colab setup. See the JAX documentation [here](https://github.com/google/jax) for more details.\n" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "mx0IRythaTyG" - }, - "source": [ - "\n", - "We test the jax installation and check the versions.\n" - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "metadata": { - "colab": { - "base_uri": "https://localhost:8080/" - }, - "id": "Z4E914qBHbZS", - "outputId": "56c47936-19c1-4de8-fbc7-1cace7282498" - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "0.2.27\n", - "0.1.75\n" - ] - } - ], - "source": [ - "import jax\n", - "import jaxlib\n", - "print(jax.__version__)\n", - "print(jaxlib.__version__)" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "vtAmA51IAYxn" - }, - "source": [ - "\n", - "Now we can finally install PySAGES. We clone the newest version from [here](https://github.com/SSAGESLabs/PySAGES) and build the remaining pure python dependencies and PySAGES itself.\n" - ] - }, - { - "cell_type": "code", - "execution_count": 7, - "metadata": { - "id": "xYRGOcFJjEE6" - }, - "outputs": [], - "source": [ - "%%bash\n", - "\n", - "rm -rf PySAGES\n", - "git clone https://github.com/SSAGESLabs/PySAGES.git &> /dev/null\n", - "cd PySAGES\n", - "pip install -q . &> /dev/null" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "h5xD1zfj-J2z" - }, - "source": [ - "\n", - "# Harmonic Bias simulations\n" - ] - }, - { - "cell_type": "code", - "execution_count": 8, - "metadata": { - "id": "OIyRfOU9_cEJ" - }, - "outputs": [], - "source": [ - "%%bash\n", - "\n", - "mkdir /content/harmonic-bias\n", - "cd /content/harmonic-bias" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "Uh2y2RXDDZub" - }, - "source": [ - "\n", - "A harmonic bias simulation constraints a collective variable with a harmonic potential. This is useful for a variety of advanced sampling methods, in particular, umbrella sampling.\n", - "\n", - "For this Colab, we are using alanine dipeptide as the example molecule, a system widely-used for benchmarking enhanced sampling methods. So first, we fetch the molecule from the examples of PySAGES.\n" - ] - }, - { - "cell_type": "code", - "execution_count": 9, - "metadata": { - "id": "5fxJMNyE-RdA" - }, - "outputs": [], - "source": [ - "%%bash\n", - "\n", - "cp /content/PySAGES/examples/inputs/alanine-dipeptide/adp-explicit.pdb ./" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "SqaG8YdK__FU" - }, - "source": [ - "\n", - "Next we load the PySAGES/OpenMM environment.\n" - ] - }, - { - "cell_type": "code", - "execution_count": 10, - "metadata": { - "colab": { - "base_uri": "https://localhost:8080/" - }, - "id": "P6kPLtGI_-66", - "outputId": "98e496cb-b78d-47bf-8b96-f2af942b10fc" - }, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "WARNING:absl:No GPU/TPU found, falling back to CPU. (Set TF_CPP_MIN_LOG_LEVEL=0 and rerun for more info.)\n" - ] - } - ], - "source": [ - "from pysages.colvars import DihedralAngle\n", - "from pysages.methods import HarmonicBias, HistogramLogger\n", - "import numpy as np\n", - "from pysages.utils import try_import\n", - "\n", - "import pysages\n", - "\n", - "openmm = try_import(\"openmm\", \"simtk.openmm\")\n", - "unit = try_import(\"openmm.unit\", \"simtk.unit\")\n", - "app = try_import(\"openmm.app\", \"simtk.openmm.app\")" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "3TV4h_WEAdSm" - }, - "source": [ - "\n", - "Next, we write a function that can generate an execution context for OpenMM. This is everything you would normally write in an OpenMM script, just wrapped as a function that returns the context of the simulation.\n" - ] - }, - { - "cell_type": "code", - "execution_count": 11, - "metadata": { - "id": "GAGw0s_cAcgP" - }, - "outputs": [], - "source": [ - "\"\"\"\n", - "Generates a simulation context, we pass this function to the attribute `run` of our sampling method.\n", - "\"\"\"\n", - "def generate_simulation(**kwargs):\n", - " pdb_filename = \"adp-explicit.pdb\"\n", - " T = 298.15 * unit.kelvin\n", - " dt = 2.0 * unit.femtoseconds\n", - " pdb = app.PDBFile(pdb_filename)\n", - "\n", - " ff = app.ForceField(\"amber99sb.xml\", \"tip3p.xml\")\n", - " cutoff_distance = 1.0 * unit.nanometer\n", - " topology = pdb.topology\n", - " system = ff.createSystem(\n", - " topology, constraints = app.HBonds, nonbondedMethod = app.NoCutoff,\n", - " nonbondedCutoff = cutoff_distance\n", - " )\n", - "\n", - " positions = pdb.getPositions(asNumpy = True)\n", - "\n", - " integrator = openmm.LangevinIntegrator(T, 1 / unit.picosecond, dt)\n", - "\n", - " simulation = app.Simulation(topology, system, integrator)\n", - " simulation.context.setPositions(positions)\n", - " simulation.minimizeEnergy()\n", - "\n", - " return simulation" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "YtUoUMEdKtH8" - }, - "source": [ - "\n", - "The next step is to define the collective variable (CV). In this case, we choose the two dihedral angles on the molecule as defined by the atom positions. We also choose an equilibrium value to constrain the dihedrals and the corresponding spring constant.\n", - "The `HarmonicBias` class is responsible for introducing the bias into the simulation run.\n" - ] - }, - { - "cell_type": "code", - "execution_count": 12, - "metadata": { - "id": "zEH5jrRoKszT" - }, - "outputs": [], - "source": [ - "cvs = (DihedralAngle((4, 6, 8, 14)), DihedralAngle((6, 8, 14, 16)))\n", - "center =[ -0.33*np.pi, -0.4*np.pi]\n", - "k = 100\n", - "method = HarmonicBias(cvs, k, center)" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "sqKuZo92K9n9" - }, - "source": [ - "\n", - "We now define a Histogram callback to log the measured values of the CVs and run the simulation for $10^4$ time steps. The `HistogramLogger` collects the state of the collective variable during the run.\n", - "Make sure to run with GPU support. Using the CPU platform with OpenMM is possible and supported, but can take a very long time.\n" - ] - }, - { - "cell_type": "code", - "execution_count": 13, - "metadata": { - "id": "-XKSe3os_-Rg" - }, - "outputs": [], - "source": [ - "callback = HistogramLogger(50)\n", - "pysages.run(method, generate_simulation, int(1e4), callback)" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "z8V0iX70RF1m" - }, - "source": [ - "\n", - "Next, we want to plot the histogram as recorded from the simulations.\n" - ] - }, - { - "cell_type": "code", - "execution_count": 14, - "metadata": { - "id": "Mvq9CWdg_qxl" - }, - "outputs": [], - "source": [ - "bins = 25\n", - "lim = (-np.pi/2, -np.pi/4)\n", - "lims = [lim for i in range(2)]\n", - "hist, edges = callback.get_histograms(bins=bins, range=lims)\n", - "hist_list = [np.sum(hist, axis=(0)), np.sum(hist, axis=(1))]" - ] - }, - { - "cell_type": "code", - "execution_count": 15, - "metadata": { - "colab": { - "base_uri": "https://localhost:8080/", - "height": 301 - }, - "id": "mxZVBr2FR5FJ", - "outputId": "2d0d189b-a1b8-400d-92cd-0fbbeaa783e8" - }, - "outputs": [ - { - "data": { - "text/plain": [ - "" - ] - }, - "execution_count": 15, - "metadata": {}, - "output_type": "execute_result" - }, - { - "data": { - "image/png": "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\n", - "text/plain": [ - "
" - ] - }, - "metadata": { - "needs_background": "light" - }, - "output_type": "display_data" - } - ], - "source": [ - "import matplotlib.pyplot as plt\n", - "fig, ax = plt.subplots()\n", - "\n", - "ax.set_xlabel(r\"CV $\\xi_i$\")\n", - "ax.set_ylabel(r\"$p(\\xi_i)$\")\n", - "\n", - "x = np.linspace(lim[0], lim[1], hist_list[0].shape[0])\n", - "\n", - "for i in range(len(hist_list)):\n", - " (line,) = ax.plot(x, hist_list[i], label=\"i= {0}\".format(i))\n", - "\n", - "ax.legend(loc=\"best\")" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "m9JjGXq_ha-6" - }, - "source": [ - "\n", - "We see how the dihedral angles are distributed. The histograms are not perfect in this example because we ran the simulation only for a few time steps and hence sampling is quite limited.\n" - ] - } - ], - "metadata": { - "accelerator": "GPU", - "colab": { - "name": "Harmonic-Bias-PySAGES-OpenMM.ipynb", - "provenance": [] - }, - "jupytext": { - "formats": "ipynb,md" - }, - "kernelspec": { - "display_name": "Python 3", - "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.9.2" - } - }, - "nbformat": 4, - "nbformat_minor": 1 -} diff --git a/examples/openmm/Harmonic_Bias.md b/examples/openmm/Harmonic_Bias.md deleted file mode 100644 index 160717c7..00000000 --- a/examples/openmm/Harmonic_Bias.md +++ /dev/null @@ -1,224 +0,0 @@ ---- -jupyter: - jupytext: - formats: ipynb,md - text_representation: - extension: .md - format_name: markdown - format_version: '1.3' - jupytext_version: 1.16.4 - kernelspec: - display_name: Python 3 - language: python - name: python3 ---- - - - -# Setting up the environment - -First, we are setting up our environment. We use an already compiled and packaged installation of OpenMM and the DLExt plugin. We copy it from Google Drive and install PySAGES for it. We also have a Google Colab that performs this installation for reference, but that requires permissions that we do not want on our Google Drive. - - - -```bash id="nMThqa-DjVcb" - -BASE_URL="https://drive.usercontent.google.com/download?id=1hsKkKtdxZTVfHKgqVF6qV2e-4SShmhr7" -wget -q --load-cookies /tmp/cookies.txt "$BASE_URL&confirm=$(wget -q --save-cookies /tmp/cookies.txt --keep-session-cookies --no-check-certificate $BASE_URL -O- | sed -rn 's/.*confirm=(\w+).*/\1\n/p')" -O pysages-env.zip -rm -rf /tmp/cookies.txt -``` - -```python colab={"base_uri": "https://localhost:8080/"} id="25H3kl03wzJe" outputId="528d12be-8cc4-42d9-d460-692d46a0e662" -%env PYSAGES_ENV=/env/pysages -``` - -```bash id="CPkgxfj6w4te" - -mkdir -p $PYSAGES_ENV . -unzip -qquo pysages-env.zip -d $PYSAGES_ENV -``` - -```python id="JMO5fiRTxAWB" -import os -import sys - -ver = sys.version_info - -sys.path.append(os.environ["PYSAGES_ENV"] + "/lib/python" + str(ver.major) + "." + str(ver.minor) + "/site-packages/") -``` - - - -## PySAGES - -The next step is to install PySAGES. -First, we install the jaxlib version that matches the CUDA installation of this Colab setup. See the JAX documentation [here](https://github.com/google/jax) for more details. - - - - - -We test the jax installation and check the versions. - - - -```python colab={"base_uri": "https://localhost:8080/"} id="Z4E914qBHbZS" outputId="56c47936-19c1-4de8-fbc7-1cace7282498" -import jax -import jaxlib -print(jax.__version__) -print(jaxlib.__version__) -``` - - - -Now we can finally install PySAGES. We clone the newest version from [here](https://github.com/SSAGESLabs/PySAGES) and build the remaining pure python dependencies and PySAGES itself. - - - -```bash id="xYRGOcFJjEE6" - -rm -rf PySAGES -git clone https://github.com/SSAGESLabs/PySAGES.git &> /dev/null -cd PySAGES -pip install -q . &> /dev/null -``` - - - -# Harmonic Bias simulations - - - -```bash id="OIyRfOU9_cEJ" - -mkdir /content/harmonic-bias -cd /content/harmonic-bias -``` - - - -A harmonic bias simulation constraints a collective variable with a harmonic potential. This is useful for a variety of advanced sampling methods, in particular, umbrella sampling. - -For this Colab, we are using alanine dipeptide as the example molecule, a system widely-used for benchmarking enhanced sampling methods. So first, we fetch the molecule from the examples of PySAGES. - - - -```bash id="5fxJMNyE-RdA" - -cp /content/PySAGES/examples/inputs/alanine-dipeptide/adp-explicit.pdb ./ -``` - - - -Next we load the PySAGES/OpenMM environment. - - - -```python colab={"base_uri": "https://localhost:8080/"} id="P6kPLtGI_-66" outputId="98e496cb-b78d-47bf-8b96-f2af942b10fc" -from pysages.colvars import DihedralAngle -from pysages.methods import HarmonicBias, HistogramLogger -import numpy as np -from pysages.utils import try_import - -import pysages - -openmm = try_import("openmm", "simtk.openmm") -unit = try_import("openmm.unit", "simtk.unit") -app = try_import("openmm.app", "simtk.openmm.app") -``` - - - -Next, we write a function that can generate an execution context for OpenMM. This is everything you would normally write in an OpenMM script, just wrapped as a function that returns the context of the simulation. - - - -```python id="GAGw0s_cAcgP" -""" -Generates a simulation context, we pass this function to the attribute `run` of our sampling method. -""" -def generate_simulation(**kwargs): - pdb_filename = "adp-explicit.pdb" - T = 298.15 * unit.kelvin - dt = 2.0 * unit.femtoseconds - pdb = app.PDBFile(pdb_filename) - - ff = app.ForceField("amber99sb.xml", "tip3p.xml") - cutoff_distance = 1.0 * unit.nanometer - topology = pdb.topology - system = ff.createSystem( - topology, constraints = app.HBonds, nonbondedMethod = app.NoCutoff, - nonbondedCutoff = cutoff_distance - ) - - positions = pdb.getPositions(asNumpy = True) - - integrator = openmm.LangevinIntegrator(T, 1 / unit.picosecond, dt) - - simulation = app.Simulation(topology, system, integrator) - simulation.context.setPositions(positions) - simulation.minimizeEnergy() - - return simulation -``` - - - -The next step is to define the collective variable (CV). In this case, we choose the two dihedral angles on the molecule as defined by the atom positions. We also choose an equilibrium value to constrain the dihedrals and the corresponding spring constant. -The `HarmonicBias` class is responsible for introducing the bias into the simulation run. - - - -```python id="zEH5jrRoKszT" -cvs = (DihedralAngle((4, 6, 8, 14)), DihedralAngle((6, 8, 14, 16))) -center =[ -0.33*np.pi, -0.4*np.pi] -k = 100 -method = HarmonicBias(cvs, k, center) -``` - - - -We now define a Histogram callback to log the measured values of the CVs and run the simulation for $10^4$ time steps. The `HistogramLogger` collects the state of the collective variable during the run. -Make sure to run with GPU support. Using the CPU platform with OpenMM is possible and supported, but can take a very long time. - - - -```python id="-XKSe3os_-Rg" -callback = HistogramLogger(50) -pysages.run(method, generate_simulation, int(1e4), callback) -``` - - - -Next, we want to plot the histogram as recorded from the simulations. - - - -```python id="Mvq9CWdg_qxl" -bins = 25 -lim = (-np.pi/2, -np.pi/4) -lims = [lim for i in range(2)] -hist, edges = callback.get_histograms(bins=bins, range=lims) -hist_list = [np.sum(hist, axis=(0)), np.sum(hist, axis=(1))] -``` - -```python colab={"base_uri": "https://localhost:8080/", "height": 301} id="mxZVBr2FR5FJ" outputId="2d0d189b-a1b8-400d-92cd-0fbbeaa783e8" -import matplotlib.pyplot as plt -fig, ax = plt.subplots() - -ax.set_xlabel(r"CV $\xi_i$") -ax.set_ylabel(r"$p(\xi_i)$") - -x = np.linspace(lim[0], lim[1], hist_list[0].shape[0]) - -for i in range(len(hist_list)): - (line,) = ax.plot(x, hist_list[i], label="i= {0}".format(i)) - -ax.legend(loc="best") -``` - - - -We see how the dihedral angles are distributed. The histograms are not perfect in this example because we ran the simulation only for a few time steps and hence sampling is quite limited. - - diff --git a/examples/openmm/metad/Metadynamics-ADP.ipynb b/examples/openmm/metad/Metadynamics-ADP.ipynb index 671c4b89..9779b12f 100644 --- a/examples/openmm/metad/Metadynamics-ADP.ipynb +++ b/examples/openmm/metad/Metadynamics-ADP.ipynb @@ -3,14 +3,13 @@ { "cell_type": "markdown", "metadata": { - "id": "T-Qkg9C9n7Cc" + "id": "_UgEohXC8n0g" }, "source": [ - "\n", "# Setting up the environment\n", "\n", - "First, we set up our environment. We use an already compiled and packaged installation of OpenMM and the DLExt plugin.\n", - "We copy it from Google Drive and install PySAGES for it.\n" + "First, we set up our environment. We use an already compiled and packaged installation of OpenMM and the openmm-dlext plugin.\n", + "We download it from Google Drive and make it visible to the running python process in this Colab instance." ] }, { @@ -24,8 +23,11 @@ "%%bash\n", "\n", "BASE_URL=\"https://drive.usercontent.google.com/download?id=1hsKkKtdxZTVfHKgqVF6qV2e-4SShmhr7\"\n", - "wget -q --load-cookies /tmp/cookies.txt \"$BASE_URL&confirm=$(wget -q --save-cookies /tmp/cookies.txt --keep-session-cookies --no-check-certificate $BASE_URL -O- | sed -rn 's/.*confirm=(\\w+).*/\\1\\n/p')\" -O pysages-env.zip\n", - "rm -rf /tmp/cookies.txt" + "COOKIES=\"/tmp/cookies.txt\"\n", + "CONFIRMATION=\"$(wget -q --save-cookies $COOKIES --keep-session-cookies --no-check-certificate $BASE_URL -O- | sed -rn 's/.*confirm=(\\w+).*/\\1\\n/p')\"\n", + "\n", + "wget -q --load-cookies $COOKIES \"$BASE_URL&confirm=$CONFIRMATION\" -O pysages-env.zip\n", + "rm -rf $COOKIES" ] }, { @@ -35,8 +37,8 @@ "colab": { "base_uri": "https://localhost:8080/" }, - "id": "KRPmkpd9n_NG", - "outputId": "5e474d51-1c66-4d16-bab9-29747fd9d466" + "id": "25H3kl03wzJe", + "outputId": "528d12be-8cc4-42d9-d460-692d46a0e662" }, "outputs": [ { @@ -55,7 +57,7 @@ "cell_type": "code", "execution_count": null, "metadata": { - "id": "J7OY5K9VoBBh" + "id": "CPkgxfj6w4te" }, "outputs": [], "source": [ @@ -69,7 +71,7 @@ "cell_type": "code", "execution_count": null, "metadata": { - "id": "EMAWp8VloIk4" + "id": "JMO5fiRTxAWB" }, "outputs": [], "source": [ @@ -79,46 +81,18 @@ "ver = sys.version_info\n", "sys.path.append(os.environ[\"PYSAGES_ENV\"] + \"/lib/python\" + str(ver.major) + \".\" + str(ver.minor) + \"/site-packages/\")\n", "\n", - "os.environ[\"XLA_FLAGS\"] = \"--xla_gpu_strict_conv_algorithm_picker=false\"\n", "os.environ[\"LD_LIBRARY_PATH\"] = \"/usr/lib/x86_64-linux-gnu:\" + os.environ[\"LD_LIBRARY_PATH\"]" ] }, { "cell_type": "markdown", "metadata": { - "id": "we_mTkFioS6R" + "id": "lf2KeHt5_eFv" }, "source": [ - "\n", "## PySAGES\n", "\n", - "The next step is to install PySAGES.\n", - "First, we install the jaxlib version that matches the CUDA installation of this Colab setup. See the JAX documentation [here](https://github.com/google/jax) for more details.\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "id": "vK0RZtbroQWe" - }, - "outputs": [], - "source": [ - "%%bash\n", - "\n", - "pip install -q --upgrade pip\n", - "# Installs the wheel compatible with CUDA.\n", - "pip install -q --upgrade \"jax[cuda]\" -f https://storage.googleapis.com/jax-releases/jax_cuda_releases.html &> /dev/null" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "wAtjM-IroYX8" - }, - "source": [ - "\n", - "Now we can finally install PySAGES. We clone the newest version from [here](https://github.com/SSAGESLabs/PySAGES) and build the remaining pure python dependencies and PySAGES itself.\n" + "The next step is to install PySAGES. We retrieve the latest version from GitHub and add its dependecies via `pip`." ] }, { @@ -129,12 +103,7 @@ }, "outputs": [], "source": [ - "%%bash\n", - "\n", - "rm -rf PySAGES\n", - "git clone https://github.com/SSAGESLabs/PySAGES.git &> /dev/null\n", - "cd PySAGES\n", - "pip install -q . &> /dev/null" + "!pip install -qq git+https://github.com/SSAGESLabs/PySAGES.git > /dev/null" ] }, { @@ -143,8 +112,7 @@ "id": "KBFVcG1FoeMq" }, "source": [ - "\n", - "# Metadynamics-biased simulations\n" + "# Metadynamics-biased simulations" ] }, { @@ -171,8 +139,7 @@ "%%bash\n", "\n", "# Download pdb file with the initial configuration of our system\n", - "PDB_URL=\"https://raw.githubusercontent.com/SSAGESLabs/PySAGES/main/examples/inputs/alanine-dipeptide/adp-vacuum.pdb\"\n", - "wget -q $PDB_URL" + "wget -q https://raw.githubusercontent.com/SSAGESLabs/PySAGES/main/examples/inputs/alanine-dipeptide/adp-vacuum.pdb" ] }, { @@ -183,23 +150,19 @@ }, "outputs": [], "source": [ - "import numpy\n", - "\n", - "from pysages.utils import try_import\n", + "import numpy as np\n", + "import openmm\n", + "import openmm.app as app\n", + "import openmm.unit as unit\n", "\n", - "openmm = try_import(\"openmm\", \"simtk.openmm\")\n", - "unit = try_import(\"openmm.unit\", \"simtk.unit\")\n", - "app = try_import(\"openmm.app\", \"simtk.openmm.app\")\n", - "\n", - "\n", - "pi = numpy.pi\n", "\n", + "pi = np.pi\n", "T = 298.15 * unit.kelvin\n", "dt = 2.0 * unit.femtoseconds\n", "adp_pdb = \"adp-vacuum.pdb\"\n", "\n", "\n", - "def generate_simulation(pdb_filename=adp_pdb, T=T, dt=dt):\n", + "def generate_simulation(pdb_filename=adp_pdb, T=T, dt=dt, **kwargs):\n", " pdb = app.PDBFile(pdb_filename)\n", "\n", " ff = app.ForceField(\"amber99sb.xml\")\n", @@ -249,11 +212,11 @@ }, "outputs": [], "source": [ + "import pysages\n", + "\n", "from pysages.grids import Grid\n", "from pysages.colvars import DihedralAngle\n", - "from pysages.methods import Metadynamics\n", - "\n", - "import pysages" + "from pysages.methods import Metadynamics" ] }, { @@ -262,7 +225,6 @@ "id": "LknkRvo1o4av" }, "source": [ - "\n", "The next step is to define the collective variable (CV). In this case, we choose the so called $\\phi$ and $\\psi$ dihedral angles of alanine dipeptide.\n", "\n", "For this example we will use the well-tempered version without grids. But these options can be configured.\n", @@ -271,7 +233,7 @@ "\n", "We also define a grid, which can be used as optional parameter to accelerate Metadynamics by approximating the biasing potential and its gradient by the closest value at the centers of the grid cells.\n", "\n", - "_Note:_ when setting $\\Delta T$ we need to also provide a value for $k_B$ that matches the internal units used by the backend.\n" + "_Note:_ when setting $\\Delta T$ we need to also provide a value for $k_B$ that matches the internal units used by the backend." ] }, { @@ -310,10 +272,9 @@ "id": "Fz8BfU34pA_N" }, "source": [ - "\n", "We now simulate the number of time steps set above.\n", "Make sure to run with GPU support, otherwise, it can take a very long time.\n", - "On the GPU this should run in around half an hour.\n" + "On the GPU this should run in around half an hour." ] }, { @@ -333,10 +294,9 @@ "id": "PXBKUfK0p9T2" }, "source": [ - "\n", "## Analysis\n", "\n", - "Let's plot the negative of the sum of gaussians accumulated. This will get close to the free energy surface for long enough simulations (larger than what is practical to run on Colab, but we should get close enough for illustration purposes here).\n" + "Let's plot the negative of the sum of gaussians accumulated. This will get close to the free energy surface for long enough simulations (larger than what is practical to run on Colab, but we should get close enough for illustration purposes here)." ] }, { @@ -357,8 +317,7 @@ "id": "6mrlIOfoszBJ" }, "source": [ - "\n", - "We are now going to gather the information for the heights, standard deviations and centers of the accumulated gaussians and build a function to evaluate their sum at any point of the collective variable space.\n" + "We are now going to gather the information for the heights, standard deviations and centers of the accumulated gaussians and build a function to evaluate their sum at any point of the collective variable space." ] }, { @@ -370,7 +329,7 @@ "outputs": [], "source": [ "fe_result = pysages.analyze(run_result)\n", - "metapotential = fe_result['metapotential']" + "metapotential = fe_result[\"metapotential\"]" ] }, { @@ -408,8 +367,7 @@ "id": "Kf_CMdih90Cd" }, "source": [ - "\n", - "And plot it.\n" + "And plot it." ] }, { @@ -461,8 +419,7 @@ "id": "a-LGmeZ_3_m-" }, "source": [ - "\n", - "Lastly, we plot the height of the gaussians as a function of time and observe that their height decreases at an exponential rate as expected for well-tempered metadynamics.\n" + "Lastly, we plot the height of the gaussians as a function of time and observe that their height decreases at an exponential rate as expected for well-tempered metadynamics." ] }, { @@ -491,24 +448,16 @@ } ], "source": [ - "_dt = dt #method.context[0].sampler.snapshot.dt\n", - "ts = _dt * 1e-3 * numpy.arange(0, fe_result['heights'].size) * run_result.method.stride\n", + "ts = dt * 1e-3 * np.arange(0, fe_result[\"heights\"].size) * run_result.method.stride\n", "\n", "fig, ax = plt.subplots(dpi=120)\n", - "ax.plot(ts, fe_result['heights'], \"o\", mfc=\"none\", ms=4)\n", + "\n", "ax.set_xlabel(\"time [ns]\")\n", "ax.set_ylabel(\"height [kJ/mol]\")\n", - "plt.show()" + "ax.plot(ts, fe_result[\"heights\"], \"o\", mfc=\"none\", ms=4)\n", + "\n", + "fig.show()" ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "id": "R6rEuwWAZ8Qp" - }, - "outputs": [], - "source": [] } ], "metadata": { diff --git a/examples/openmm/metad/Metadynamics-ADP.md b/examples/openmm/metad/Metadynamics-ADP.md index fb430387..b30b119a 100644 --- a/examples/openmm/metad/Metadynamics-ADP.md +++ b/examples/openmm/metad/Metadynamics-ADP.md @@ -13,77 +13,55 @@ jupyter: name: python3 --- - - + # Setting up the environment -First, we set up our environment. We use an already compiled and packaged installation of OpenMM and the DLExt plugin. -We copy it from Google Drive and install PySAGES for it. - +First, we set up our environment. We use an already compiled and packaged installation of OpenMM and the openmm-dlext plugin. +We download it from Google Drive and make it visible to the running python process in this Colab instance. ```bash id="3eTbKklCnyd_" BASE_URL="https://drive.usercontent.google.com/download?id=1hsKkKtdxZTVfHKgqVF6qV2e-4SShmhr7" -wget -q --load-cookies /tmp/cookies.txt "$BASE_URL&confirm=$(wget -q --save-cookies /tmp/cookies.txt --keep-session-cookies --no-check-certificate $BASE_URL -O- | sed -rn 's/.*confirm=(\w+).*/\1\n/p')" -O pysages-env.zip -rm -rf /tmp/cookies.txt +COOKIES="/tmp/cookies.txt" +CONFIRMATION="$(wget -q --save-cookies $COOKIES --keep-session-cookies --no-check-certificate $BASE_URL -O- | sed -rn 's/.*confirm=(\w+).*/\1\n/p')" + +wget -q --load-cookies $COOKIES "$BASE_URL&confirm=$CONFIRMATION" -O pysages-env.zip +rm -rf $COOKIES ``` -```python colab={"base_uri": "https://localhost:8080/"} id="KRPmkpd9n_NG" outputId="5e474d51-1c66-4d16-bab9-29747fd9d466" +```python colab={"base_uri": "https://localhost:8080/"} id="25H3kl03wzJe" outputId="528d12be-8cc4-42d9-d460-692d46a0e662" %env PYSAGES_ENV=/env/pysages ``` -```bash id="J7OY5K9VoBBh" +```bash id="CPkgxfj6w4te" mkdir -p $PYSAGES_ENV . unzip -qquo pysages-env.zip -d $PYSAGES_ENV ``` -```python id="EMAWp8VloIk4" +```python id="JMO5fiRTxAWB" import os import sys ver = sys.version_info sys.path.append(os.environ["PYSAGES_ENV"] + "/lib/python" + str(ver.major) + "." + str(ver.minor) + "/site-packages/") -os.environ["XLA_FLAGS"] = "--xla_gpu_strict_conv_algorithm_picker=false" os.environ["LD_LIBRARY_PATH"] = "/usr/lib/x86_64-linux-gnu:" + os.environ["LD_LIBRARY_PATH"] ``` - - + ## PySAGES -The next step is to install PySAGES. -First, we install the jaxlib version that matches the CUDA installation of this Colab setup. See the JAX documentation [here](https://github.com/google/jax) for more details. - +The next step is to install PySAGES. We retrieve the latest version from GitHub and add its dependecies via `pip`. -```bash id="vK0RZtbroQWe" - -pip install -q --upgrade pip -# Installs the wheel compatible with CUDA. -pip install -q --upgrade "jax[cuda]" -f https://storage.googleapis.com/jax-releases/jax_cuda_releases.html &> /dev/null -``` - - - -Now we can finally install PySAGES. We clone the newest version from [here](https://github.com/SSAGESLabs/PySAGES) and build the remaining pure python dependencies and PySAGES itself. - - - -```bash id="B-HB9CzioV5j" - -rm -rf PySAGES -git clone https://github.com/SSAGESLabs/PySAGES.git &> /dev/null -cd PySAGES -pip install -q . &> /dev/null +```python id="B-HB9CzioV5j" +!pip install -qq git+https://github.com/SSAGESLabs/PySAGES.git > /dev/null ``` - # Metadynamics-biased simulations - @@ -98,28 +76,23 @@ For this Colab, we are using alanine peptide in vacuum as example system. ```bash id="fre3-LYso1hh" # Download pdb file with the initial configuration of our system -PDB_URL="https://raw.githubusercontent.com/SSAGESLabs/PySAGES/main/examples/inputs/alanine-dipeptide/adp-vacuum.pdb" -wget -q $PDB_URL +wget -q https://raw.githubusercontent.com/SSAGESLabs/PySAGES/main/examples/inputs/alanine-dipeptide/adp-vacuum.pdb ``` ```python id="BBvC7Spoog82" -import numpy +import numpy as np +import openmm +import openmm.app as app +import openmm.unit as unit -from pysages.utils import try_import - -openmm = try_import("openmm", "simtk.openmm") -unit = try_import("openmm.unit", "simtk.unit") -app = try_import("openmm.app", "simtk.openmm.app") - - -pi = numpy.pi +pi = np.pi T = 298.15 * unit.kelvin dt = 2.0 * unit.femtoseconds adp_pdb = "adp-vacuum.pdb" -def generate_simulation(pdb_filename=adp_pdb, T=T, dt=dt): +def generate_simulation(pdb_filename=adp_pdb, T=T, dt=dt, **kwargs): pdb = app.PDBFile(pdb_filename) ff = app.ForceField("amber99sb.xml") @@ -158,15 +131,14 @@ Next, we load PySAGES and the relevant classes and methods for our problem ```python id="fpMg-o8WomAA" +import pysages + from pysages.grids import Grid from pysages.colvars import DihedralAngle from pysages.methods import Metadynamics - -import pysages ``` - The next step is to define the collective variable (CV). In this case, we choose the so called $\phi$ and $\psi$ dihedral angles of alanine dipeptide. For this example we will use the well-tempered version without grids. But these options can be configured. @@ -176,7 +148,6 @@ We set the initial height, standard deviation and deposition frequency `stride` We also define a grid, which can be used as optional parameter to accelerate Metadynamics by approximating the biasing potential and its gradient by the closest value at the centers of the grid cells. _Note:_ when setting $\Delta T$ we need to also provide a value for $k_B$ that matches the internal units used by the backend. - ```python id="B1Z8FWz0o7u_" @@ -203,11 +174,9 @@ method = Metadynamics(cvs, height, sigma, stride, ngauss, deltaT=deltaT, kB=kB, ``` - We now simulate the number of time steps set above. Make sure to run with GPU support, otherwise, it can take a very long time. On the GPU this should run in around half an hour. - ```python id="K951m4BbpUar" @@ -215,11 +184,9 @@ run_result = pysages.run(method, generate_simulation, timesteps) ``` - ## Analysis Let's plot the negative of the sum of gaussians accumulated. This will get close to the free energy surface for long enough simulations (larger than what is practical to run on Colab, but we should get close enough for illustration purposes here). - ```python id="X69d1R7OpW4P" @@ -228,14 +195,12 @@ from pysages.approxfun import compute_mesh ``` - We are now going to gather the information for the heights, standard deviations and centers of the accumulated gaussians and build a function to evaluate their sum at any point of the collective variable space. - ```python id="zJqvpbw8szxR" fe_result = pysages.analyze(run_result) -metapotential = fe_result['metapotential'] +metapotential = fe_result["metapotential"] ``` @@ -257,9 +222,7 @@ A = A.reshape(plot_grid.shape) ``` - And plot it. - ```python colab={"base_uri": "https://localhost:8080/", "height": 461} id="3s9LL9apBMVb" outputId="55abf4e5-fef0-4faa-bf01-9719cbe8aa2b" @@ -281,22 +244,17 @@ plt.show() ``` - Lastly, we plot the height of the gaussians as a function of time and observe that their height decreases at an exponential rate as expected for well-tempered metadynamics. - ```python colab={"base_uri": "https://localhost:8080/", "height": 457} id="SI_fhUW9CGlP" outputId="5d32f99d-4911-44bb-9d89-69c3e6212cb7" -_dt = dt #method.context[0].sampler.snapshot.dt -ts = _dt * 1e-3 * numpy.arange(0, fe_result['heights'].size) * run_result.method.stride +ts = dt * 1e-3 * np.arange(0, fe_result["heights"].size) * run_result.method.stride fig, ax = plt.subplots(dpi=120) -ax.plot(ts, fe_result['heights'], "o", mfc="none", ms=4) + ax.set_xlabel("time [ns]") ax.set_ylabel("height [kJ/mol]") -plt.show() -``` - -```python id="R6rEuwWAZ8Qp" +ax.plot(ts, fe_result["heights"], "o", mfc="none", ms=4) +fig.show() ``` diff --git a/examples/openmm/spectral_abf/ADP_SpectralABF.ipynb b/examples/openmm/spectral_abf/ADP_SpectralABF.ipynb index b0b67f25..c1c3e304 100644 --- a/examples/openmm/spectral_abf/ADP_SpectralABF.ipynb +++ b/examples/openmm/spectral_abf/ADP_SpectralABF.ipynb @@ -3,14 +3,13 @@ { "cell_type": "markdown", "metadata": { - "id": "T-Qkg9C9n7Cc" + "id": "_UgEohXC8n0g" }, "source": [ - "\n", "# Setting up the environment\n", "\n", - "First, we set up our environment. We use an already compiled and packaged installation of OpenMM and the DLExt plugin.\n", - "We copy it from Google Drive and install PySAGES for it.\n" + "First, we set up our environment. We use an already compiled and packaged installation of OpenMM and the openmm-dlext plugin.\n", + "We download it from Google Drive and make it visible to the running python process in this Colab instance." ] }, { @@ -24,19 +23,18 @@ "%%bash\n", "\n", "BASE_URL=\"https://drive.usercontent.google.com/download?id=1hsKkKtdxZTVfHKgqVF6qV2e-4SShmhr7\"\n", - "wget -q --load-cookies /tmp/cookies.txt \"$BASE_URL&confirm=$(wget -q --save-cookies /tmp/cookies.txt --keep-session-cookies --no-check-certificate $BASE_URL -O- | sed -rn 's/.*confirm=(\\w+).*/\\1\\n/p')\" -O pysages-env.zip\n", - "rm -rf /tmp/cookies.txt" + "COOKIES=\"/tmp/cookies.txt\"\n", + "CONFIRMATION=\"$(wget -q --save-cookies $COOKIES --keep-session-cookies --no-check-certificate $BASE_URL -O- | sed -rn 's/.*confirm=(\\w+).*/\\1\\n/p')\"\n", + "\n", + "wget -q --load-cookies $COOKIES \"$BASE_URL&confirm=$CONFIRMATION\" -O pysages-env.zip\n", + "rm -rf $COOKIES" ] }, { "cell_type": "code", "execution_count": 2, "metadata": { - "colab": { - "base_uri": "https://localhost:8080/" - }, - "id": "KRPmkpd9n_NG", - "outputId": "e577ce0d-0a62-4f48-fb05-fde3a39c2ccc" + "id": "25H3kl03wzJe" }, "outputs": [ { @@ -55,7 +53,7 @@ "cell_type": "code", "execution_count": 3, "metadata": { - "id": "J7OY5K9VoBBh" + "id": "CPkgxfj6w4te" }, "outputs": [], "source": [ @@ -69,7 +67,7 @@ "cell_type": "code", "execution_count": 5, "metadata": { - "id": "EMAWp8VloIk4" + "id": "JMO5fiRTxAWB" }, "outputs": [], "source": [ @@ -79,62 +77,29 @@ "ver = sys.version_info\n", "sys.path.append(os.environ[\"PYSAGES_ENV\"] + \"/lib/python\" + str(ver.major) + \".\" + str(ver.minor) + \"/site-packages/\")\n", "\n", - "os.environ[\"XLA_FLAGS\"] = \"--xla_gpu_strict_conv_algorithm_picker=false\"\n", "os.environ[\"LD_LIBRARY_PATH\"] = \"/usr/lib/x86_64-linux-gnu:\" + os.environ[\"LD_LIBRARY_PATH\"]" ] }, { "cell_type": "markdown", "metadata": { - "id": "we_mTkFioS6R" + "id": "lf2KeHt5_eFv" }, "source": [ - "\n", "## PySAGES\n", "\n", - "The next step is to install PySAGES.\n", - "First, we install the jaxlib version that matches the CUDA installation of this Colab setup. See the JAX documentation [here](https://github.com/google/jax) for more details.\n" + "The next step is to install PySAGES. We retrieve the latest version from GitHub and add its dependecies via `pip`." ] }, { "cell_type": "code", "execution_count": 6, - "metadata": { - "id": "vK0RZtbroQWe" - }, - "outputs": [], - "source": [ - "%%bash\n", - "\n", - "pip install -q --upgrade pip\n", - "# Installs the wheel compatible with CUDA.\n", - "pip install -q --upgrade \"jax[cuda]\" -f https://storage.googleapis.com/jax-releases/jax_cuda_releases.html &> /dev/null" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "wAtjM-IroYX8" - }, - "source": [ - "\n", - "Now we can finally install PySAGES. We clone the newest version from [here](https://github.com/SSAGESLabs/PySAGES) and build the remaining pure python dependencies and PySAGES itself.\n" - ] - }, - { - "cell_type": "code", - "execution_count": 7, "metadata": { "id": "B-HB9CzioV5j" }, "outputs": [], "source": [ - "%%bash\n", - "\n", - "rm -rf PySAGES\n", - "git clone https://github.com/SSAGESLabs/PySAGES.git &> /dev/null\n", - "cd PySAGES\n", - "pip install -q . &> /dev/null" + "!pip install -qq git+https://github.com/SSAGESLabs/PySAGES.git > /dev/null" ] }, { @@ -143,8 +108,7 @@ "id": "KBFVcG1FoeMq" }, "source": [ - "\n", - "# SpectralABF-biased simulations\n" + "# SpectralABF-biased simulations" ] }, { @@ -153,10 +117,9 @@ "id": "0W2ukJuuojAl" }, "source": [ - "\n", "SpectralABF gradually learns a better approximation to the coefficients of a basis functions expansion of the free energy of a system, from the generalized mean forces in a similar fashion to the ABF sampling method.\n", "\n", - "For this Colab, we are using alanine peptide in vacuum as example system.\n" + "For this Colab, we are using alanine peptide in vacuum as example system." ] }, { @@ -170,8 +133,7 @@ "%%bash\n", "\n", "# Download pdb file with the initial configuration of our system\n", - "PDB_URL=\"https://raw.githubusercontent.com/SSAGESLabs/PySAGES/main/examples/inputs/alanine-dipeptide/adp-vacuum.pdb\"\n", - "wget -q $PDB_URL" + "wget -q https://raw.githubusercontent.com/SSAGESLabs/PySAGES/main/examples/inputs/alanine-dipeptide/adp-vacuum.pdb" ] }, { @@ -182,23 +144,19 @@ }, "outputs": [], "source": [ - "import numpy\n", - "\n", - "from pysages.utils import try_import\n", - "\n", - "openmm = try_import(\"openmm\", \"simtk.openmm\")\n", - "unit = try_import(\"openmm.unit\", \"simtk.unit\")\n", - "app = try_import(\"openmm.app\", \"simtk.openmm.app\")\n", - "\n", + "import numpy as np\n", + "import openmm\n", + "import openmm.app as app\n", + "import openmm.unit as unit\n", "\n", - "pi = numpy.pi\n", "\n", + "pi = np.pi\n", "T = 298.15 * unit.kelvin\n", "dt = 2.0 * unit.femtoseconds\n", "adp_pdb = \"adp-vacuum.pdb\"\n", "\n", "\n", - "def generate_simulation(pdb_filename=adp_pdb, T=T, dt=dt):\n", + "def generate_simulation(pdb_filename=adp_pdb, T=T, dt=dt, **kwargs):\n", " pdb = app.PDBFile(pdb_filename)\n", "\n", " ff = app.ForceField(\"amber99sb.xml\")\n", @@ -236,8 +194,7 @@ "id": "3UrzENm_oo6U" }, "source": [ - "\n", - "Next, we load PySAGES and the relevant classes and methods for our problem\n" + "Next, we load PySAGES and the relevant classes and methods for our problem" ] }, { @@ -261,10 +218,9 @@ "id": "LknkRvo1o4av" }, "source": [ - "\n", "The next step is to define the collective variable (CV). In this case, we choose the so called $\\phi$ and $\\psi$ dihedral angles of alanine dipeptide.\n", "\n", - "We define a grid, which will be used to indicate how we want to bin the forces that will be used to approximate the biasing potential and its gradient.\n" + "We define a grid, which will be used to indicate how we want to bin the forces that will be used to approximate the biasing potential and its gradient." ] }, { @@ -288,9 +244,8 @@ "id": "Fz8BfU34pA_N" }, "source": [ - "\n", "We now simulate the number of time steps set above.\n", - "Make sure to run with GPU support, otherwise, it can take a very long time.\n" + "Make sure to run with GPU support, otherwise, it can take a very long time." ] }, { @@ -310,10 +265,9 @@ "id": "PXBKUfK0p9T2" }, "source": [ - "\n", "## Analysis\n", "\n", - "Let's plot the free energy!\n" + "Let's plot the free energy!" ] }, { @@ -324,8 +278,7 @@ }, "outputs": [], "source": [ - "import matplotlib.pyplot as plt\n", - "from pysages.approxfun import compute_mesh" + "import matplotlib.pyplot as plt" ] }, { @@ -385,17 +338,8 @@ "cbar = plt.colorbar(im)\n", "cbar.ax.set_ylabel(r\"$A~[kJ/mol]$\", rotation=270, labelpad=20)\n", "\n", - "plt.show()" + "fig.show()" ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "id": "R6rEuwWAZ8Qp" - }, - "outputs": [], - "source": [] } ], "metadata": { diff --git a/examples/openmm/spectral_abf/ADP_SpectralABF.md b/examples/openmm/spectral_abf/ADP_SpectralABF.md index 714e33e4..adeb2fd8 100644 --- a/examples/openmm/spectral_abf/ADP_SpectralABF.md +++ b/examples/openmm/spectral_abf/ADP_SpectralABF.md @@ -13,112 +13,83 @@ jupyter: name: python3 --- - - + # Setting up the environment -First, we set up our environment. We use an already compiled and packaged installation of OpenMM and the DLExt plugin. -We copy it from Google Drive and install PySAGES for it. - +First, we set up our environment. We use an already compiled and packaged installation of OpenMM and the openmm-dlext plugin. +We download it from Google Drive and make it visible to the running python process in this Colab instance. ```bash id="3eTbKklCnyd_" BASE_URL="https://drive.usercontent.google.com/download?id=1hsKkKtdxZTVfHKgqVF6qV2e-4SShmhr7" -wget -q --load-cookies /tmp/cookies.txt "$BASE_URL&confirm=$(wget -q --save-cookies /tmp/cookies.txt --keep-session-cookies --no-check-certificate $BASE_URL -O- | sed -rn 's/.*confirm=(\w+).*/\1\n/p')" -O pysages-env.zip -rm -rf /tmp/cookies.txt +COOKIES="/tmp/cookies.txt" +CONFIRMATION="$(wget -q --save-cookies $COOKIES --keep-session-cookies --no-check-certificate $BASE_URL -O- | sed -rn 's/.*confirm=(\w+).*/\1\n/p')" + +wget -q --load-cookies $COOKIES "$BASE_URL&confirm=$CONFIRMATION" -O pysages-env.zip +rm -rf $COOKIES ``` -```python colab={"base_uri": "https://localhost:8080/"} id="KRPmkpd9n_NG" outputId="e577ce0d-0a62-4f48-fb05-fde3a39c2ccc" +```python id="25H3kl03wzJe" %env PYSAGES_ENV=/env/pysages ``` -```bash id="J7OY5K9VoBBh" +```bash id="CPkgxfj6w4te" mkdir -p $PYSAGES_ENV . unzip -qquo pysages-env.zip -d $PYSAGES_ENV ``` -```python id="EMAWp8VloIk4" +```python id="JMO5fiRTxAWB" import os import sys ver = sys.version_info sys.path.append(os.environ["PYSAGES_ENV"] + "/lib/python" + str(ver.major) + "." + str(ver.minor) + "/site-packages/") -os.environ["XLA_FLAGS"] = "--xla_gpu_strict_conv_algorithm_picker=false" os.environ["LD_LIBRARY_PATH"] = "/usr/lib/x86_64-linux-gnu:" + os.environ["LD_LIBRARY_PATH"] ``` - - + ## PySAGES -The next step is to install PySAGES. -First, we install the jaxlib version that matches the CUDA installation of this Colab setup. See the JAX documentation [here](https://github.com/google/jax) for more details. - - - -```bash id="vK0RZtbroQWe" - -pip install -q --upgrade pip -# Installs the wheel compatible with CUDA. -pip install -q --upgrade "jax[cuda]" -f https://storage.googleapis.com/jax-releases/jax_cuda_releases.html &> /dev/null -``` - - - -Now we can finally install PySAGES. We clone the newest version from [here](https://github.com/SSAGESLabs/PySAGES) and build the remaining pure python dependencies and PySAGES itself. - +The next step is to install PySAGES. We retrieve the latest version from GitHub and add its dependecies via `pip`. -```bash id="B-HB9CzioV5j" - -rm -rf PySAGES -git clone https://github.com/SSAGESLabs/PySAGES.git &> /dev/null -cd PySAGES -pip install -q . &> /dev/null +```python id="B-HB9CzioV5j" +!pip install -qq git+https://github.com/SSAGESLabs/PySAGES.git > /dev/null ``` - # SpectralABF-biased simulations - - SpectralABF gradually learns a better approximation to the coefficients of a basis functions expansion of the free energy of a system, from the generalized mean forces in a similar fashion to the ABF sampling method. For this Colab, we are using alanine peptide in vacuum as example system. - ```bash id="fre3-LYso1hh" # Download pdb file with the initial configuration of our system -PDB_URL="https://raw.githubusercontent.com/SSAGESLabs/PySAGES/main/examples/inputs/alanine-dipeptide/adp-vacuum.pdb" -wget -q $PDB_URL +wget -q https://raw.githubusercontent.com/SSAGESLabs/PySAGES/main/examples/inputs/alanine-dipeptide/adp-vacuum.pdb ``` ```python id="BBvC7Spoog82" -import numpy - -from pysages.utils import try_import - -openmm = try_import("openmm", "simtk.openmm") -unit = try_import("openmm.unit", "simtk.unit") -app = try_import("openmm.app", "simtk.openmm.app") - +import numpy as np +import openmm +import openmm.app as app +import openmm.unit as unit -pi = numpy.pi +pi = np.pi T = 298.15 * unit.kelvin dt = 2.0 * unit.femtoseconds adp_pdb = "adp-vacuum.pdb" -def generate_simulation(pdb_filename=adp_pdb, T=T, dt=dt): +def generate_simulation(pdb_filename=adp_pdb, T=T, dt=dt, **kwargs): pdb = app.PDBFile(pdb_filename) ff = app.ForceField("amber99sb.xml") @@ -151,9 +122,7 @@ def generate_simulation(pdb_filename=adp_pdb, T=T, dt=dt): ``` - Next, we load PySAGES and the relevant classes and methods for our problem - ```python id="fpMg-o8WomAA" @@ -165,11 +134,9 @@ import pysages ``` - The next step is to define the collective variable (CV). In this case, we choose the so called $\phi$ and $\psi$ dihedral angles of alanine dipeptide. We define a grid, which will be used to indicate how we want to bin the forces that will be used to approximate the biasing potential and its gradient. - ```python id="B1Z8FWz0o7u_" @@ -181,10 +148,8 @@ method = SpectralABF(cvs, grid) ``` - We now simulate the number of time steps set above. Make sure to run with GPU support, otherwise, it can take a very long time. - ```python id="K951m4BbpUar" @@ -192,16 +157,13 @@ run_result = pysages.run(method, generate_simulation, timesteps) ``` - ## Analysis Let's plot the free energy! - ```python id="X69d1R7OpW4P" import matplotlib.pyplot as plt -from pysages.approxfun import compute_mesh ``` ```python id="zJqvpbw8szxR" @@ -229,9 +191,5 @@ ax.set_ylabel(r"$\psi$") cbar = plt.colorbar(im) cbar.ax.set_ylabel(r"$A~[kJ/mol]$", rotation=270, labelpad=20) -plt.show() -``` - -```python id="R6rEuwWAZ8Qp" - +fig.show() ```