{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Mixture simulation with Sage: Ethanol and cyclohexane\n", "\n", "This example shows how to use the Open Force Field Toolkit to create a parametrized `System` object that can be used to run a molecular dynamic simulation with OpenMM. If you want to run MD with a different engine, see the example in `examples/conversion_amber_gromacs/`." ] }, { "cell_type": "markdown", "metadata": { "tags": [] }, "source": [ "## Create an Interchange\n", "\n", "We start by loading a PDB file containing one copy of ethanol and cyclohexane. Our goal is to create an OFF `Topology` object describing this system that we can parametrize with the SMIRNOFF-format \"Sage\" force field. The parametrized system is stored in an `Interchange` object, which can then produce input files for a number of simulation engines.\n", "\n", "The two `Molecule` objects created from the SMILES strings can contain information such as partial charges and stereochemistry that is not included in an OpenMM topology. In this example, partial charges are not explicitly given, and `ForceField` will assign AM1/BCC charges as specified by the \"Sage\" force field." ] }, { "cell_type": "code", "execution_count": 1, "metadata": {}, "outputs": [], "source": [ "from openff.toolkit.utils import get_data_file_path" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We use the `get_data_file_path` utility function to easily access the data installed with the toolkit. Here you have the option to load example systems of increasing complexity. For speed, we recommend that you begin by loading a system with a single ethanol and a single cyclohexane." ] }, { "cell_type": "code", "execution_count": 2, "metadata": {}, "outputs": [], "source": [ "# 1 molecule of ethanol and 1 of cyclohexane.\n", "pdb_file_path = get_data_file_path(\"systems/test_systems/1_cyclohexane_1_ethanol.pdb\")\n", "\n", "# 40%-60% cyclohexane-ethanol mixture.\n", "# pdb_file_path = get_data_file_path('systems/packmol_boxes/cyclohexane_ethanol_0.4_0.6.pdb')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "PDB files are not a reliable source of bond orders, so the toolkit requires users to supply a more detailed description of the molecule and its connectivity. SMILES records, SDF, and MOL2 files are among the [many ways to create a `Molecule`.](https://docs.openforcefield.org/projects/toolkit/en/stable/users/molecule_cookbook.html)" ] }, { "cell_type": "code", "execution_count": 3, "metadata": {}, "outputs": [], "source": [ "from openff.toolkit import Molecule\n", "\n", "ethanol = Molecule.from_smiles(\"CCO\")\n", "cyclohexane = Molecule.from_smiles(\"C1CCCCC1\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Alternatively, if you have `sdf` files of the molecules, or if you have OpenEye installed and `mol2` files available, you can get the same results as above by loading the detailed molecule information from the files." ] }, { "cell_type": "code", "execution_count": 4, "metadata": {}, "outputs": [], "source": [ "from openff.toolkit.utils.toolkits import OpenEyeToolkitWrapper\n", "\n", "if OpenEyeToolkitWrapper.is_available():\n", " ethanol = Molecule.from_file(get_data_file_path(\"molecules/ethanol.mol2\"))\n", " cyclohexane = Molecule.from_file(get_data_file_path(\"molecules/cyclohexane.mol2\"))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We now create the Open Force Field Toolkit `Topology` describing the system from an OpenMM `Topology` object. The OFF `Topology` include more information (supplied by the two `Molecule` objects) than the OpenMM `Topology` such as (optionally) partial charges and stereochemistry. In this example, partial charges are not explicitly given, and `ForceField` will assign AM1/BCC charges as specified by the \"Sage\" force field.\n", "\n", "Note that the Open Force Field Toolkit produces deterministic charges that do not depend on the input conformation of parameterized molecules. See the [FAQ](https://docs.openforcefield.org/projects/toolkit/en/stable/faq.html#the-partial-charges-generated-by-the-toolkit-dont-seem-to-depend-on-the-molecules-conformation-is-this-a-bug) for more information.\n", "\n", "
\n", " Note on partial charges: The full 1.0.0 release will implement support for the definition of semiempirical partial charge treatment directly into the SMIRNOFF force field file (for details, see https://openforcefield.github.io/standards/standards/smirnoff/#partial-charge-and-electrostatics-models). Moreover, it will be possible to import charges directly from sdf and mol2 files.\n", "
" ] }, { "cell_type": "code", "execution_count": 5, "metadata": {}, "outputs": [ { "data": { "application/vnd.jupyter.widget-view+json": { "model_id": "b21376e03b3c481c9d943d2f427c082f", "version_major": 2, "version_minor": 0 }, "text/plain": [] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "from openff.toolkit import ForceField, Topology\n", "\n", "# Create the OpenFF Topology from an PDB file\n", "topology = Topology.from_pdb(\n", " pdb_file_path,\n", " unique_molecules=[ethanol, cyclohexane],\n", ")\n", "\n", "# Load the OpenFF \"Sage\" force field.\n", "forcefield = ForceField(\"openff-2.2.0.offxml\")\n", "\n", "# Parametrize the topology and create an Interchange object.\n", "interchange = forcefield.create_interchange(topology)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Run a simulation\n", "\n", "We can now use the `Interchange` object to prepare and run molecular dynamics simulations with OpenMM." ] }, { "cell_type": "code", "execution_count": 6, "metadata": {}, "outputs": [], "source": [ "import openmm\n", "from openmm import unit\n", "\n", "# Propagate the System with Langevin dynamics.\n", "time_step = 2 * unit.femtoseconds # simulation timestep\n", "temperature = 300 * unit.kelvin # simulation temperature\n", "friction = 1 / unit.picosecond # collision rate\n", "integrator = openmm.LangevinIntegrator(temperature, friction, time_step)\n", "\n", "# Length of the simulation.\n", "num_steps = 1000 # number of integration steps to run\n", "\n", "# Logging options.\n", "trj_freq = 100 # number of steps per written trajectory frame\n", "data_freq = 100 # number of steps per written simulation statistics\n", "\n", "# Set up an OpenMM simulation.\n", "simulation = interchange.to_openmm_simulation(integrator)\n", "\n", "# Randomize the velocities from a Boltzmann distribution at a given temperature.\n", "simulation.context.setVelocitiesToTemperature(temperature)\n", "\n", "# Configure the information in the output files.\n", "pdb_reporter = openmm.app.PDBReporter(\"trajectory.pdb\", trj_freq)\n", "state_data_reporter = openmm.app.StateDataReporter(\n", " \"data.csv\",\n", " data_freq,\n", " step=True,\n", " potentialEnergy=True,\n", " temperature=True,\n", " density=True,\n", ")\n", "simulation.reporters.append(pdb_reporter)\n", "simulation.reporters.append(state_data_reporter)" ] }, { "cell_type": "code", "execution_count": 7, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Starting simulation\n", "Elapsed time 1.19 seconds\n", "Done!\n" ] } ], "source": [ "import time\n", "\n", "print(\"Starting simulation\")\n", "start = time.process_time()\n", "\n", "# Run the simulation\n", "simulation.step(num_steps)\n", "\n", "end = time.process_time()\n", "print(\"Elapsed time %.2f seconds\" % (end - start))\n", "print(\"Done!\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "If successful, the directory where your jupyter notebook is running should contain a `trajectory.pdb` file that you can visualize and a `data.csv` file including potential energy, density, and temperature of each frame." ] }, { "cell_type": "code", "execution_count": 8, "metadata": { "scrolled": true }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "#\"Step\",\"Potential Energy (kJ/mole)\",\"Temperature (K)\",\"Density (g/mL)\"\n", "100,74.374459717961,153.8639730810106,0.006671086812409914\n", "200,67.18465564081257,225.04871999306042,0.006671086812409914\n", "300,73.153268311711,216.02183682738016,0.006671086812409914\n", "400,80.31836840936725,207.7937447329963,0.006671086812409914\n", "500,80.12940356561725,188.36565243783872,0.006671086812409914\n", "600,67.47083422967975,170.97481857480136,0.006671086812409914\n", "700,84.53037402460163,191.9952731085917,0.006671086812409914\n", "800,83.9705290538985,213.94618798329284,0.006671086812409914\n", "900,101.1868376476485,248.17766215167603,0.006671086812409914\n", "1000,82.79697558710163,252.8156596432481,0.006671086812409914\n" ] } ], "source": [ "!cat data.csv" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [] } ], "metadata": { "category": "parametrization_evaluation", "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.10.10" }, "widgets": { "application/vnd.jupyter.widget-state+json": { "state": { "1f9d9a5a15b045c1ab18661385a15a0d": { "model_module": "nglview-js-widgets", "model_module_version": "3.0.6", "model_name": "ColormakerRegistryModel", "state": { "_dom_classes": [], "_model_module": "nglview-js-widgets", "_model_module_version": "3.0.6", "_model_name": "ColormakerRegistryModel", "_msg_ar": [], "_msg_q": [], "_ready": false, "_view_count": null, "_view_module": "nglview-js-widgets", "_view_module_version": "3.0.6", "_view_name": "ColormakerRegistryView", "layout": "IPY_MODEL_c1dd3367d9294a209f6fea73feed04aa", "tabbable": null, "tooltip": null } }, "c1dd3367d9294a209f6fea73feed04aa": { "model_module": "@jupyter-widgets/base", "model_module_version": "2.0.0", "model_name": "LayoutModel", "state": { "_model_module": "@jupyter-widgets/base", "_model_module_version": "2.0.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "2.0.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border_bottom": null, "border_left": null, "border_right": null, "border_top": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null } } }, "version_major": 2, "version_minor": 0 } } }, "nbformat": 4, "nbformat_minor": 4 }