├── .gitignore ├── 01_introduction ├── 01_introduction.ipynb ├── 01_introduction.md ├── 02_molecular_setup.ipynb ├── 02_molecular_setup.md ├── 03_molecular_dynamics.ipynb ├── 03_molecular_dynamics.md ├── 04_writing_nodes.ipynb ├── 04_writing_nodes.md ├── 05_running_nodes.ipynb ├── 05_running_nodes.md ├── README.md ├── assets │ ├── 02_solvated.png │ ├── 03_rmsd.png │ ├── 03_time_series.png │ ├── 03_view_molecule.png │ ├── 03_view_molecules.png │ ├── 03_view_system.png │ ├── 04_gui.png │ └── output.zip ├── get_tutorial.py ├── myconfig │ ├── Process.py │ └── Protocol.py ├── nodes │ ├── equilibrate.ipynb │ ├── equilibrate.py │ ├── minimise.ipynb │ ├── minimise.py │ ├── parameterise.ipynb │ ├── parameterise.py │ ├── solvate.ipynb │ └── solvate.py └── scripts │ └── workflow.sh ├── 02_funnel_metad ├── 01_bss-fun-metad-setup.ipynb ├── 01_bss-fun-metad-setup.md ├── 02_bss-fun-metad-analysis.ipynb ├── 02_bss-fun-metad-analysis.md ├── README.md ├── example_nodes │ ├── equilibrate_no_trj.py │ ├── fun_metad.py │ ├── minimise.py │ ├── parameterise_solvate.py │ └── submit.sub ├── figures │ ├── figure1.jpeg │ ├── figure2.png │ └── fun-hsp90.png └── get_tutorial.py ├── 03_steered_md ├── 01_setup_sMD.ipynb ├── 01_setup_sMD.md ├── 02_trajectory_analysis.ipynb ├── 02_trajectory_analysis.md ├── README.md ├── figures │ ├── COLVAR_all.png │ ├── COLVAR_failed.png │ ├── COLVAR_snapshots.png │ ├── ensemble-md-protocol.png │ ├── open-close.png │ ├── phe196.png │ └── tyr152.png ├── get_tutorial.py └── scripts │ ├── sMD_LSF.sh │ ├── sMD_multiCV.py │ ├── sMD_simple.py │ ├── sMD_slurm.sh │ ├── seededMD.py │ └── seededMD_LSF.sh ├── 04_fep ├── 01_intro_to_alchemy │ ├── alchemical_introduction.ipynb │ ├── alchemical_introduction.md │ ├── example_output │ │ ├── mbar_bound_output.txt │ │ └── mbar_free_output.txt │ ├── get_tutorial.py │ ├── images │ │ ├── MCS.png │ │ ├── Therm_cycle.png │ │ ├── overlap_MBAR_example.png │ │ ├── overlap_MBAR_example_circles.png │ │ ├── overlap_MBAR_example_less.png │ │ ├── overlap_MBAR_example_less_circles.png │ │ └── thermo_cycle_rel_eq.png │ └── slides │ │ └── CCPBioSimTraining2022_slides.pdf ├── 02_RBFE │ ├── 01_setup_rbfe.ipynb │ ├── 01_setup_rbfe.md │ ├── 02_analysis_rbfe.ipynb │ ├── 02_analysis_rbfe.md │ ├── get_tutorial.py │ ├── images │ │ ├── fep_pipeline.png │ │ └── tyk2_protlig.png │ └── scripts │ │ ├── .svn │ │ ├── entries │ │ ├── format │ │ ├── pristine │ │ │ ├── 01 │ │ │ │ └── 015f3293a2b13c2bb141480cf0a46d2710d7e505.svn-base │ │ │ ├── 09 │ │ │ │ └── 09b149f7700b981e3a51dbb930632c234a099ab8.svn-base │ │ │ ├── 4c │ │ │ │ └── 4cdea129ef2fd591a19b69c88e8b8182b9c293ce.svn-base │ │ │ ├── e4 │ │ │ │ └── e489348848a63603fd82f847f97faf1e5598002c.svn-base │ │ │ ├── ec │ │ │ │ └── ec0594e6d6479b8b830af7315ad23adaed62f79e.svn-base │ │ │ ├── f3 │ │ │ │ └── f3791f681e50dd384968ce79e03b599e355721aa.svn-base │ │ │ └── f9 │ │ │ │ └── f9f96d03b0f2df3f8a8ec74555fbfc2374e949d6.svn-base │ │ ├── wc.db │ │ └── wc.db-journal │ │ ├── analysis.py │ │ ├── extract_execution_model_bash.sh │ │ ├── fepprep.py │ │ ├── ligprep.py │ │ ├── run_all_slurm.sh │ │ ├── run_analysis_slurm.sh │ │ ├── run_fepprep_slurm.sh │ │ ├── run_ligprep_slurm.sh │ │ └── run_production_slurm.sh ├── 03_ABFE │ ├── 01_setup_abfe.ipynb │ ├── 01_setup_abfe.md │ ├── 02_analysis_abfe.ipynb │ ├── 02_analysis_abfe.md │ ├── get_tutorial.py │ └── images │ │ ├── MCS.png │ │ ├── abfe_cycle_details.png │ │ ├── alt_poses.png │ │ ├── boresch_dof.png │ │ ├── boresch_restr_2.png │ │ ├── convergence_equil.png │ │ ├── convergence_no_equil.png │ │ ├── mif_mif180.png │ │ ├── ngl_1.png │ │ ├── ngl_2.png │ │ ├── ngl_new_1.png │ │ ├── ngl_new_2.png │ │ ├── ngl_new_3.png │ │ ├── overlap.png │ │ └── pmf_evolution.png ├── 04_PFEP │ ├── 01_setup_pfep.ipynb │ ├── 01_setup_pfep.md │ └── images │ │ └── pfep_tutorial_tcycle.png ├── 05_ATM │ ├── 01_ATM.ipynb │ └── get_tutorial.py └── README.md ├── LICENSE ├── LIVECOMS ├── 01_introduction │ └── livecoms.tex ├── 02_funnel_metad │ ├── fun-hsp90-analyses.png │ ├── funmetad-hsp90.png │ ├── funmetadfig1.jpeg │ └── livecoms.tex ├── 03_steered_md │ ├── COLVAR_all.png │ ├── ensemble-md-protocol.png │ ├── livecoms.tex │ ├── msm-final.png │ └── open-close.png ├── 04_fep │ ├── abfe-tutorial.png │ ├── alignment_visualisation.png │ ├── introfep.png │ ├── introfep_updated.png │ ├── livecoms.tex │ ├── pfep-tutorial_tcycle.png │ ├── rbfe-analysis.png │ └── rbfe-setup.png ├── livecoms.cls ├── main.tex ├── references.bib └── vancouver-livecoms.bst ├── README.md ├── Style_guide.ipynb └── releases ├── LiveCoMS_Article_v1.pdf └── header_v1.jpg /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | .ipynb_checkpoints 3 | -------------------------------------------------------------------------------- /01_introduction/04_writing_nodes.md: -------------------------------------------------------------------------------- 1 | Author: Lester Hedges
2 | Email:   lester.hedges@bristol.ac.uk 3 | 4 | # Nodes: _Interoperable workflow components_ 5 | 6 | The companion notebook for this section can be found [here](https://github.com/michellab/BioSimSpaceTutorials/blob/4844562e7d2cd0b269cead56562ec16a3dfaef7c/01_introduction/04_writing_nodes.ipynb) 7 | 8 | So far we have been working with BioSimSpace in a rather ad hoc fashion. While this intereactive exploration is a great way of learning and prototyping ideas, it is not a good way of producing a reproducible and interoperable script that can be shared with others. For example, we created processes that used specific packages such as AMBER and GROMACS. If a user didn't have these available on their system, then the script simply wouldn't work. We also used hard-coded paths to input files. This means the user would have to edit the paths each time they ran the script with different input, which would quickly become tedious. 9 | 10 | In order to solve this problem, a core concept of BioSimSpace is the interoperable workflow component, or _node_. These are robust and portable Python scripts that typically do a small, well-defined piece of work. All inputs and outputs from the node are validated and the node is written in a such a way that it is _independent_ of the underlying software packages, i.e. the same script can work with a range of different packages. In addition, nodes are aware of the environment in which they are run, so can be used interactively, from the command-line, or within a workflow engine. 11 | 12 | While it is possible to write a node directly as a Python script, we suggest that the best way of writing one is inside of a [Jupyter](http://jupyter.org) notebook. As you've already seen, the interactive notebook environment provides a fantastic way of prototyping and documenting your node and will allow a user to interact with it directly on a remote cloud server, such as [notebook.biosimspace.org](https://notebook.biosimspace.org). The notebook can provide a complete record of your work, inlcuding documentation, visualisation, and graphs. When you are happy with the node, you can download it as a regular Python script (by clicking on `File/Download As/Python` in JupyterHub or `File/Export Notebook As/Export Notebook to Executable Script` in JupyterLab) and run it directly from the command-line on your workstation, laptop, or on a high-performance computing cluster. Any interactive BioSimSpace elements, such as molecular visualisations, will simply be ignored when run this way. 13 | 14 | 15 | ## An example: Minimisation 16 | 17 | In the rest of the notebook you'll learn how to use BioSimSpace to write a robust and interoperable workflow node to perform energy minimisation on a molecular system. 18 | 19 | As always, we'll first need to import BioSimSpace: 20 | 21 | 22 | ```python 23 | import BioSimSpace as BSS 24 | ``` 25 | 26 | We begin by creating a `Node` object. This is the core of our molecular workflow component. It defines what it does, what input is needed, and the output that is produced. 27 | 28 | 29 | ```python 30 | node = BSS.Gateway.Node("A node to perform energy minimisation and save the minimised molecular configuration to file.") 31 | ``` 32 | 33 | We'll now set the author and license of the node. When nodes are run the the authorship can be queried so that people can get credit for their work. Eventually, BioSimSpace nodes also will also contain built in tracking information to determine how many times they are run. 34 | 35 | 36 | ```python 37 | node.addAuthor(name="Lester Hedges", email="lester.hedges@bristol.ac.uk", affiliation="University of Bristol") 38 | node.setLicense("GPLv3") 39 | ``` 40 | 41 | Nodes require inputs. To specify inputs we use the `BSS.Gateway` package, which is used as a bridge between BioSimSpace and the outside world. This will allow us to document the inputs, define their type, and specify any constraints on their allowed values. Here we will need a set of files that define the molecular system, and an integer that indicates the number of minimisation steps to perform. 42 | 43 | 44 | ```python 45 | node.addInput("files", BSS.Gateway.FileSet( 46 | help="A set of molecular input files.") 47 | ) 48 | 49 | node.addInput("steps", BSS.Gateway.Integer( 50 | help="The number of minimisation steps.", 51 | minimum=0, 52 | maximum=1000000, 53 | default=10000) 54 | ) 55 | 56 | node.addInput("engine", BSS.Gateway.String( 57 | help="The molecular dynamics engine", 58 | allowed=BSS.MD.engines(), 59 | default="auto") 60 | ) 61 | ``` 62 | 63 | Note that the input requirements `steps` and `engine` have default values, so are optional. 64 | 65 | We now need to define the output of the node. In this case we will return a set of files representing the minimised molecular system. 66 | 67 | 68 | ```python 69 | node.addOutput("minimised", BSS.Gateway.FileSet(help="The minimised molecular system.")) 70 | ``` 71 | 72 | When working interactively within a Jupyter notebook we need a way to allow users to set the input requirements. The `node.showControls` method will display a graphical user interface (GUI), from which inputs can be set. All of the elements for this GUI are automatically generated by the `addInput` and `addOutput` functions above. As you'll see in the next section, if we were to run the same node from the command-line, we would instead get an automatically generated [argparse](https://docs.python.org/3/library/argparse.html) parser. 73 | 74 | Note that the GUI requires active user input. All input requirements that don't have a default value _must_ be set before the node can proceed. If you try to query the node for one of the user values then an error will be raised. For bounded integer inputs you can use a slider to set the value, or type in the input box and press enter. 75 | 76 | When working interactively you will typically be running on a remote server where you won't have access to the local filesystem. In this case you'll need to upload files for any of the `File` or `FileSet` input requirements. The GUI below will provide buttons that allow you to browse your own filesystem and select files. Since Jupyter has a limit of 5MB for file transfers, we provide support for compressed formats, such as `.zip` or `.tar.gz`. (A single archive can contain a set of files, allowing you to set a single value for a `FileSet` requirement.) We've provided some example input files that can be used in the training notebooks, which are available to download from the links below. These can then be re-uploaded using the GUI. 77 | 78 | AMBER: [ala.crd](https://raw.githubusercontent.com/michellab/BioSimSpace/devel/demo/amber/ala/ala.crd), [ala.top](https://raw.githubusercontent.com/michellab/BioSimSpace/devel/demo/amber/ala/ala.top) 79 | 80 | GROMACS: [kigaki.gro](https://raw.githubusercontent.com/michellab/BioSimSpace/devel/demo/gromacs/kigaki/kigaki.gro), [kigaki.top](https://raw.githubusercontent.com/michellab/BioSimSpace/devel/demo/gromacs/kigaki/kigaki.top) 81 | 82 | 83 | When uploading files the name of the current file(s) will replace the `Upload` button. If you need to change the file, simply click on the button again and choose a new file. Note that both input files should be loaded at the same time. 84 | 85 | 86 | ```python 87 | node.showControls() 88 | ``` 89 | 90 | ![Notebook GUI](https://github.com/michellab/BioSimSpaceTutorials/blob/c06201e9464732df5fd64fa560779ef333f59651/01_introduction/assets/04_gui.png) 91 | 92 | 93 | Once all requirements are set then we can acces the values using the `node.getInput` method. The first time this is called the `node` will automatically validate all of the input and report the user if any errors were found. 94 | 95 | We'll now create a molecular system using the input files uploaded by the user. As in the previous section, we don't need to specify the format of the files, since this is automatically determined by BioSimSpace. (BioSimSpace has support for a wide range of formats and can convert between many formats too.) 96 | 97 | 98 | ```python 99 | system = BSS.IO.readMolecules(node.getInput("files")) 100 | ``` 101 | 102 | As learned in the previus notebook, in order to run a minimisation we need to define a protocol. This can be done using the `BSS.Protocol` package. Here we will create a "best practice" minimisation protocol, overriding the number of steps with the input from the user. 103 | 104 | 105 | ```python 106 | protocol = BSS.Protocol.Minimisation(steps=node.getInput("steps")) 107 | ``` 108 | 109 | We now have everything that is required to run a minimisation. To do so, we use the `BSS.MD` package to find an appropriate molecular dynamics package on our current environment. What package is found will depend upon both the system and protocol, as well as the hardware that is available to the user. (For example, the user can choose to find packages with GPU support.) 110 | 111 | Note that this is different to the previous section, where we specifically launched AMBER and GROMACS processes ourselves. This is what makes the node interoperable, i.e. it will work regardles of what MD packages are installed. (As long as we find a package that supports minimisation and supports a molecular file format to which we can convert the input system.) By adding the optional `engine` requirement we have also allowed the user to override the `auto` setting if they prefer to use a specific engine. 112 | 113 | (By default, the `run` function automatically starts the process so it will be running as once you execute the cell below.) 114 | 115 | 116 | ```python 117 | process = BSS.MD.run(system, protocol, engine=node.getInput("engine")) 118 | ``` 119 | 120 | We now wait for the process to finish, then check whether there were any errors before continuing. If errors were raised, then we raise an exception and print the last 10 lines of stdout and stderr to the user. 121 | 122 | 123 | ```python 124 | process.wait() 125 | 126 | if process.isError(): 127 | print(process.stdout(10)) 128 | print(process.stdout(10)) 129 | raise RuntimeError("The process exited with an error!") 130 | ``` 131 | 132 | When the process has finished running we can get the minimised molecular configuration. We will save this to file using the same format as the original system, and set the `minimised` output requirement to the list of file names that were written. 133 | 134 | 135 | ```python 136 | node.setOutput("minimised", 137 | BSS.IO.saveMolecules("minimised", process.getSystem(), system.fileFormat())) 138 | ``` 139 | 140 | Finally, we validate that the node completed succesfully. This will check that all output requirements are satisfied and that no errors were raised by the user. Any file outputs will be available for the user to download as a compressed archive. 141 | 142 | Note that the validation will fail until the cell above finishes running. 143 | 144 | 145 | ```python 146 | node.validate() 147 | ``` 148 | 149 | 150 | 151 | 152 | output.zip
153 | 154 | 155 | 156 | Once we are satisfied with our node we can choosed to download it as a regular Python script that can be run from the command-line. 157 | 158 | In JupyterHub, click on: `File/Download As/Python`\ 159 | In JupyterLab, click on: `File/Export Notebook As/Export Notebook to Executable Script` 160 | 161 | That's it, you've now succesfully executed your first BioSimSpace node! 162 | -------------------------------------------------------------------------------- /01_introduction/README.md: -------------------------------------------------------------------------------- 1 | # An introduction to BioSimSpace 2 | 3 | This tutorial provides an introduction to [BioSimSpace](https://biosimspace.org/index.html), an interoperable Python framework for biomolecular simulation. 4 | 5 | The tutorial is divided into five sections, each linked below: 6 | 7 | * 01 - [Introduction](https://github.com/OpenBioSim/BioSimSpaceTutorials/blob/main/01_introduction/01_introduction.md) 8 | * 02 - [Molecular setup](https://github.com/OpenBioSim/BioSimSpaceTutorials/blob/main/01_introduction/02_molecular_setup.md) 9 | * 03 - [Molecular dynamics](https://github.com/OpenBioSim/BioSimSpaceTutorials/blob/main/01_introduction/03_molecular_dynamics.md) 10 | * 04 - [Writing nodes](https://github.com/OpenBioSim/BioSimSpaceTutorials/blob/main/01_introduction/04_writing_nodes.md) 11 | * 05 - [Running nodes](https://github.com/OpenBioSim/BioSimSpaceTutorials/blob/main/01_introduction/05_running_nodes.md) 12 | -------------------------------------------------------------------------------- /01_introduction/assets/02_solvated.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenBioSim/biosimspace_tutorials/ac200df769ba3373c9e0e724020aaaa96e340889/01_introduction/assets/02_solvated.png -------------------------------------------------------------------------------- /01_introduction/assets/03_rmsd.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenBioSim/biosimspace_tutorials/ac200df769ba3373c9e0e724020aaaa96e340889/01_introduction/assets/03_rmsd.png -------------------------------------------------------------------------------- /01_introduction/assets/03_time_series.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenBioSim/biosimspace_tutorials/ac200df769ba3373c9e0e724020aaaa96e340889/01_introduction/assets/03_time_series.png -------------------------------------------------------------------------------- /01_introduction/assets/03_view_molecule.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenBioSim/biosimspace_tutorials/ac200df769ba3373c9e0e724020aaaa96e340889/01_introduction/assets/03_view_molecule.png -------------------------------------------------------------------------------- /01_introduction/assets/03_view_molecules.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenBioSim/biosimspace_tutorials/ac200df769ba3373c9e0e724020aaaa96e340889/01_introduction/assets/03_view_molecules.png -------------------------------------------------------------------------------- /01_introduction/assets/03_view_system.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenBioSim/biosimspace_tutorials/ac200df769ba3373c9e0e724020aaaa96e340889/01_introduction/assets/03_view_system.png -------------------------------------------------------------------------------- /01_introduction/assets/04_gui.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenBioSim/biosimspace_tutorials/ac200df769ba3373c9e0e724020aaaa96e340889/01_introduction/assets/04_gui.png -------------------------------------------------------------------------------- /01_introduction/assets/output.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenBioSim/biosimspace_tutorials/ac200df769ba3373c9e0e724020aaaa96e340889/01_introduction/assets/output.zip -------------------------------------------------------------------------------- /01_introduction/get_tutorial.py: -------------------------------------------------------------------------------- 1 | import requests, os 2 | 3 | links = { 4 | "01": ( 5 | "inputs.tar.bz2", 6 | "https://openbiosim-my.sharepoint.com/:u:/g/personal/director_openbiosim_org/EU3zvc0UmE1CqX0UKKR5hGIBtdF8Jj1-u8hol1XPwAEgKQ?download=1", 7 | ), 8 | } 9 | 10 | 11 | def download(): 12 | localfile, url = links["01"] 13 | # Do not download if tarball already found 14 | if os.path.isfile(localfile): 15 | return 16 | print("Downloading %s from openbiosim.org ..." % localfile) 17 | req = requests.get(url, stream=True) 18 | with open(localfile, "wb") as f: 19 | for chunk in req.iter_content(chunk_size=1024): 20 | if chunk: 21 | f.write(chunk) 22 | f.flush() 23 | print("Extracting compressed tarball ...") 24 | os.system("tar -xf %s" % localfile) 25 | # os.system("rm %s" % localfile) 26 | -------------------------------------------------------------------------------- /01_introduction/myconfig/Process.py: -------------------------------------------------------------------------------- 1 | import BioSimSpace as BSS 2 | 3 | 4 | # Wrap the instantiation of BSS.Process.Amber objects to configure them 5 | # such that coordinates are always wrapped to the minimum image. 6 | def Amber( 7 | system, protocol, exe=None, name="amber", work_dir=None, seed=None, property_map={} 8 | ): 9 | # Create process using the passed parameters. 10 | process = BSS.Process.Amber( 11 | system, 12 | protocol, 13 | exe=exe, 14 | name=name, 15 | work_dir=work_dir, 16 | seed=seed, 17 | property_map=property_map, 18 | ) 19 | 20 | # Get the config. 21 | config = process.getConfig() 22 | 23 | # Add coordinate wrapping as the second last line. 24 | config[-1] = " iwrap=1," 25 | config.append(" /") 26 | 27 | # Set the new config. 28 | process.setConfig(config) 29 | 30 | # Return the process. 31 | return process 32 | -------------------------------------------------------------------------------- /01_introduction/myconfig/Protocol.py: -------------------------------------------------------------------------------- 1 | import BioSimSpace as BSS 2 | 3 | # Override the equilibration protocol with some custom defaults. 4 | 5 | 6 | def EquilibrationNVT( 7 | runtime=5 * BSS.Units.Time.nanosecond, 8 | report_interval=2500, 9 | restart_interval=250000, 10 | restraint="backbone", 11 | ): 12 | return BSS.Protocol.Equilibration( 13 | runtime=runtime, 14 | report_interval=report_interval, 15 | restart_interval=restart_interval, 16 | restraint=restraint, 17 | ) 18 | 19 | 20 | def EquilibrationNPT( 21 | runtime=5 * BSS.Units.Time.nanosecond, 22 | pressure=BSS.Units.Pressure.atm, 23 | report_interval=2500, 24 | restart_interval=250000, 25 | restraint="backbone", 26 | ): 27 | return BSS.Protocol.Equilibration( 28 | runtime=runtime, 29 | pressure=pressure, 30 | report_interval=report_interval, 31 | restart_interval=restart_interval, 32 | restraint=restraint, 33 | ) 34 | -------------------------------------------------------------------------------- /01_introduction/nodes/equilibrate.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "Author: Lester Hedges
\n", 8 | "Email:   lester.hedges@bristol.ac.uk\n", 9 | "\n", 10 | "# Equilibration\n", 11 | "\n", 12 | "A node to perform equilibration of a molecular system. Saves the equilibration system to AMBER format files along with a DCD trajectory." 13 | ] 14 | }, 15 | { 16 | "cell_type": "code", 17 | "execution_count": null, 18 | "metadata": {}, 19 | "outputs": [], 20 | "source": [ 21 | "import BioSimSpace as BSS" 22 | ] 23 | }, 24 | { 25 | "cell_type": "code", 26 | "execution_count": null, 27 | "metadata": {}, 28 | "outputs": [], 29 | "source": [ 30 | "node = BSS.Gateway.Node(\n", 31 | " \"A node to perform equilibration. Saves the equlibrated molecular configuration and trajectory to file.\"\n", 32 | ")\n", 33 | "node.addAuthor(\n", 34 | " name=\"Lester Hedges\",\n", 35 | " email=\"lester.hedges@bristol.ac.uk\",\n", 36 | " affiliation=\"University of Bristol\",\n", 37 | ")\n", 38 | "node.setLicense(\"GPLv3\")" 39 | ] 40 | }, 41 | { 42 | "cell_type": "markdown", 43 | "metadata": {}, 44 | "source": [ 45 | "Set the input requirements:" 46 | ] 47 | }, 48 | { 49 | "cell_type": "code", 50 | "execution_count": null, 51 | "metadata": {}, 52 | "outputs": [], 53 | "source": [ 54 | "node.addInput(\"files\", BSS.Gateway.FileSet(help=\"A set of molecular input files.\"))\n", 55 | "\n", 56 | "node.addInput(\n", 57 | " \"runtime\",\n", 58 | " BSS.Gateway.Time(\n", 59 | " help=\"The run time.\",\n", 60 | " unit=\"nanoseconds\",\n", 61 | " minimum=0.02 * BSS.Units.Time.nanosecond,\n", 62 | " maximum=10 * BSS.Units.Time.nanosecond,\n", 63 | " default=0.02 * BSS.Units.Time.nanosecond,\n", 64 | " ),\n", 65 | ")\n", 66 | "\n", 67 | "node.addInput(\n", 68 | " \"temperature_start\",\n", 69 | " BSS.Gateway.Temperature(\n", 70 | " help=\"The initial temperature.\",\n", 71 | " unit=\"kelvin\",\n", 72 | " minimum=0 * BSS.Units.Temperature.kelvin,\n", 73 | " maximum=1000 * BSS.Units.Temperature.kelvin,\n", 74 | " default=0 * BSS.Units.Temperature.kelvin,\n", 75 | " ),\n", 76 | ")\n", 77 | "\n", 78 | "node.addInput(\n", 79 | " \"temperature_end\",\n", 80 | " BSS.Gateway.Temperature(\n", 81 | " help=\"The final temperature.\",\n", 82 | " unit=\"kelvin\",\n", 83 | " minimum=0 * BSS.Units.Temperature.kelvin,\n", 84 | " maximum=1000 * BSS.Units.Temperature.kelvin,\n", 85 | " default=300 * BSS.Units.Temperature.kelvin,\n", 86 | " ),\n", 87 | ")\n", 88 | "\n", 89 | "node.addInput(\n", 90 | " \"restraint\",\n", 91 | " BSS.Gateway.String(\n", 92 | " help=\"The type of restraint.\",\n", 93 | " allowed=BSS.Protocol.Equilibration.restraints(),\n", 94 | " default=\"none\",\n", 95 | " ),\n", 96 | ")\n", 97 | "\n", 98 | "node.addInput(\n", 99 | " \"report_interval\",\n", 100 | " BSS.Gateway.Integer(\n", 101 | " help=\"The number of integration steps between reporting output.\",\n", 102 | " minimum=100,\n", 103 | " maximum=10000,\n", 104 | " default=100,\n", 105 | " ),\n", 106 | ")\n", 107 | "\n", 108 | "node.addInput(\n", 109 | " \"restart_interval\",\n", 110 | " BSS.Gateway.Integer(\n", 111 | " help=\"The number of integration steps between saving trajectory frames.\",\n", 112 | " minimum=100,\n", 113 | " maximum=10000,\n", 114 | " default=500,\n", 115 | " ),\n", 116 | ")\n", 117 | "\n", 118 | "node.addInput(\n", 119 | " \"engine\",\n", 120 | " BSS.Gateway.String(\n", 121 | " help=\"The molecular dynamics engine.\", allowed=BSS.MD.engines(), default=\"auto\"\n", 122 | " ),\n", 123 | ")" 124 | ] 125 | }, 126 | { 127 | "cell_type": "markdown", 128 | "metadata": {}, 129 | "source": [ 130 | "We now need to define the output of the node. In this case we will return a set of files representing the equilibrated molecular system in AMBER format and a single file containing the trajectory frames." 131 | ] 132 | }, 133 | { 134 | "cell_type": "code", 135 | "execution_count": null, 136 | "metadata": {}, 137 | "outputs": [], 138 | "source": [ 139 | "node.addOutput(\n", 140 | " \"equilibrated\", BSS.Gateway.FileSet(help=\"The equilibrated molecular system.\")\n", 141 | ")\n", 142 | "node.addOutput(\"trajectory\", BSS.Gateway.File(help=\"The trajectory file.\"))" 143 | ] 144 | }, 145 | { 146 | "cell_type": "code", 147 | "execution_count": null, 148 | "metadata": {}, 149 | "outputs": [], 150 | "source": [ 151 | "node.showControls()" 152 | ] 153 | }, 154 | { 155 | "cell_type": "markdown", 156 | "metadata": {}, 157 | "source": [ 158 | "Generate the molecular system." 159 | ] 160 | }, 161 | { 162 | "cell_type": "code", 163 | "execution_count": null, 164 | "metadata": {}, 165 | "outputs": [], 166 | "source": [ 167 | "system = BSS.IO.readMolecules(node.getInput(\"files\"))" 168 | ] 169 | }, 170 | { 171 | "cell_type": "markdown", 172 | "metadata": {}, 173 | "source": [ 174 | "Set up the equilibration protocol.\n", 175 | "\n", 176 | "(Note that the keyword arguments happen to have the same name as the input requirements. This need not be the case.)" 177 | ] 178 | }, 179 | { 180 | "cell_type": "code", 181 | "execution_count": null, 182 | "metadata": {}, 183 | "outputs": [], 184 | "source": [ 185 | "protocol = BSS.Protocol.Equilibration(\n", 186 | " runtime=node.getInput(\"runtime\"),\n", 187 | " temperature_start=node.getInput(\"temperature_start\"),\n", 188 | " temperature_end=node.getInput(\"temperature_end\"),\n", 189 | " restraint=node.getInput(\"restraint\"),\n", 190 | ")" 191 | ] 192 | }, 193 | { 194 | "cell_type": "markdown", 195 | "metadata": {}, 196 | "source": [ 197 | "Start the molecular dynamics process." 198 | ] 199 | }, 200 | { 201 | "cell_type": "code", 202 | "execution_count": null, 203 | "metadata": {}, 204 | "outputs": [], 205 | "source": [ 206 | "process = BSS.MD.run(system, protocol, engine=node.getInput(\"engine\"))" 207 | ] 208 | }, 209 | { 210 | "cell_type": "markdown", 211 | "metadata": {}, 212 | "source": [ 213 | "We now wait for the process to finish, then check whether there were any errors before continuing. If errors were raised, then we raise an exception and print the last 10 lines of `stdout` and `stderr` to the user." 214 | ] 215 | }, 216 | { 217 | "cell_type": "code", 218 | "execution_count": null, 219 | "metadata": {}, 220 | "outputs": [], 221 | "source": [ 222 | "process.wait()\n", 223 | "\n", 224 | "if process.isError():\n", 225 | " print(process.stdout(10))\n", 226 | " print(process.stdout(10))\n", 227 | " raise RuntimeError(\"The process exited with an error!\")" 228 | ] 229 | }, 230 | { 231 | "cell_type": "markdown", 232 | "metadata": {}, 233 | "source": [ 234 | "Get the equilibrated molecular system and write to file in the same format as the input, along with a PDB file to use as a topology file for the trajectory in [VMD](https://www.ks.uiuc.edu/Research/vmd/)." 235 | ] 236 | }, 237 | { 238 | "cell_type": "code", 239 | "execution_count": null, 240 | "metadata": {}, 241 | "outputs": [], 242 | "source": [ 243 | "# Get the original file formats associated with the system.\n", 244 | "# This is returned as a comma-separated string.\n", 245 | "formats = system.fileFormat()\n", 246 | "\n", 247 | "# Append PDB format, so we can visualise the trajectory with VMD.\n", 248 | "formats += \",pdb\"\n", 249 | "\n", 250 | "# Write to file and bind to the output.\n", 251 | "node.setOutput(\n", 252 | " \"equilibrated\", BSS.IO.saveMolecules(\"equilibrated\", process.getSystem(), formats)\n", 253 | ")" 254 | ] 255 | }, 256 | { 257 | "cell_type": "markdown", 258 | "metadata": {}, 259 | "source": [ 260 | "Get the trajectory, convert to MDTraj format and save to a DCD file." 261 | ] 262 | }, 263 | { 264 | "cell_type": "code", 265 | "execution_count": null, 266 | "metadata": {}, 267 | "outputs": [], 268 | "source": [ 269 | "# Get the BioSimSpace trajectory wrapper.\n", 270 | "trajectory = process.getTrajectory()\n", 271 | "\n", 272 | "# Extract the MDTraj object.\n", 273 | "traj = trajectory.getTrajectory(format=\"mdtraj\")\n", 274 | "\n", 275 | "# Write to file.\n", 276 | "traj.save(\"equilibrated.dcd\")\n", 277 | "\n", 278 | "# Bind to the output requirement.\n", 279 | "node.setOutput(\"trajectory\", \"equilibrated.dcd\")" 280 | ] 281 | }, 282 | { 283 | "cell_type": "markdown", 284 | "metadata": {}, 285 | "source": [ 286 | "Validate the node." 287 | ] 288 | }, 289 | { 290 | "cell_type": "code", 291 | "execution_count": null, 292 | "metadata": {}, 293 | "outputs": [], 294 | "source": [ 295 | "node.validate()" 296 | ] 297 | } 298 | ], 299 | "metadata": { 300 | "kernelspec": { 301 | "display_name": "Python 3", 302 | "language": "python", 303 | "name": "python3" 304 | }, 305 | "language_info": { 306 | "codemirror_mode": { 307 | "name": "ipython", 308 | "version": 3 309 | }, 310 | "file_extension": ".py", 311 | "mimetype": "text/x-python", 312 | "name": "python", 313 | "nbconvert_exporter": "python", 314 | "pygments_lexer": "ipython3" 315 | } 316 | }, 317 | "nbformat": 4, 318 | "nbformat_minor": 2 319 | } 320 | -------------------------------------------------------------------------------- /01_introduction/nodes/equilibrate.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # coding: utf-8 3 | 4 | # Author: Lester Hedges
5 | # Email:   lester.hedges@bristol.ac.uk 6 | # 7 | # # Equilibration 8 | # 9 | # A node to perform equilibration of a molecular system. Saves the equilibration system to AMBER format files along with a DCD trajectory. 10 | 11 | # In[ ]: 12 | 13 | 14 | import BioSimSpace as BSS 15 | 16 | 17 | # In[ ]: 18 | 19 | 20 | node = BSS.Gateway.Node( 21 | "A node to perform equilibration. Saves the equlibrated molecular configuration and trajectory to file." 22 | ) 23 | node.addAuthor( 24 | name="Lester Hedges", 25 | email="lester.hedges@bristol.ac.uk", 26 | affiliation="University of Bristol", 27 | ) 28 | node.setLicense("GPLv3") 29 | 30 | 31 | # Set the input requirements: 32 | 33 | # In[ ]: 34 | 35 | 36 | node.addInput("files", BSS.Gateway.FileSet(help="A set of molecular input files.")) 37 | 38 | node.addInput( 39 | "runtime", 40 | BSS.Gateway.Time( 41 | help="The run time.", 42 | unit="nanoseconds", 43 | minimum=0.02 * BSS.Units.Time.nanosecond, 44 | maximum=10 * BSS.Units.Time.nanosecond, 45 | default=0.02 * BSS.Units.Time.nanosecond, 46 | ), 47 | ) 48 | 49 | node.addInput( 50 | "temperature_start", 51 | BSS.Gateway.Temperature( 52 | help="The initial temperature.", 53 | unit="kelvin", 54 | minimum=0 * BSS.Units.Temperature.kelvin, 55 | maximum=1000 * BSS.Units.Temperature.kelvin, 56 | default=0 * BSS.Units.Temperature.kelvin, 57 | ), 58 | ) 59 | 60 | node.addInput( 61 | "temperature_end", 62 | BSS.Gateway.Temperature( 63 | help="The final temperature.", 64 | unit="kelvin", 65 | minimum=0 * BSS.Units.Temperature.kelvin, 66 | maximum=1000 * BSS.Units.Temperature.kelvin, 67 | default=300 * BSS.Units.Temperature.kelvin, 68 | ), 69 | ) 70 | 71 | node.addInput( 72 | "restraint", 73 | BSS.Gateway.String( 74 | help="The type of restraint.", 75 | allowed=BSS.Protocol.Equilibration.restraints(), 76 | default="none", 77 | ), 78 | ) 79 | 80 | node.addInput( 81 | "report_interval", 82 | BSS.Gateway.Integer( 83 | help="The number of integration steps between reporting output.", 84 | minimum=100, 85 | maximum=10000, 86 | default=100, 87 | ), 88 | ) 89 | 90 | node.addInput( 91 | "restart_interval", 92 | BSS.Gateway.Integer( 93 | help="The number of integration steps between saving trajectory frames.", 94 | minimum=100, 95 | maximum=10000, 96 | default=500, 97 | ), 98 | ) 99 | 100 | node.addInput( 101 | "engine", 102 | BSS.Gateway.String( 103 | help="The molecular dynamics engine.", allowed=BSS.MD.engines(), default="auto" 104 | ), 105 | ) 106 | 107 | 108 | # We now need to define the output of the node. In this case we will return a set of files representing the equilibrated molecular system in AMBER format and a single file containing the trajectory frames. 109 | 110 | # In[ ]: 111 | 112 | 113 | node.addOutput( 114 | "equilibrated", BSS.Gateway.FileSet(help="The equilibrated molecular system.") 115 | ) 116 | node.addOutput("trajectory", BSS.Gateway.File(help="The trajectory file.")) 117 | 118 | 119 | # In[ ]: 120 | 121 | 122 | node.showControls() 123 | 124 | 125 | # Generate the molecular system. 126 | 127 | # In[ ]: 128 | 129 | 130 | system = BSS.IO.readMolecules(node.getInput("files")) 131 | 132 | 133 | # Set up the equilibration protocol. 134 | # 135 | # (Note that the keyword arguments happen to have the same name as the input requirements. This need not be the case.) 136 | 137 | # In[ ]: 138 | 139 | 140 | protocol = BSS.Protocol.Equilibration( 141 | runtime=node.getInput("runtime"), 142 | temperature_start=node.getInput("temperature_start"), 143 | temperature_end=node.getInput("temperature_end"), 144 | restraint=node.getInput("restraint"), 145 | ) 146 | 147 | 148 | # Start the molecular dynamics process. 149 | 150 | # In[ ]: 151 | 152 | 153 | process = BSS.MD.run(system, protocol, engine=node.getInput("engine")) 154 | 155 | 156 | # We now wait for the process to finish, then check whether there were any errors before continuing. If errors were raised, then we raise an exception and print the last 10 lines of `stdout` and `stderr` to the user. 157 | 158 | # In[ ]: 159 | 160 | 161 | process.wait() 162 | 163 | if process.isError(): 164 | print(process.stdout(10)) 165 | print(process.stdout(10)) 166 | raise RuntimeError("The process exited with an error!") 167 | 168 | 169 | # Get the equilibrated molecular system and write to file in the same format as the input, along with a PDB file to use as a topology file for the trajectory in [VMD](https://www.ks.uiuc.edu/Research/vmd/). 170 | 171 | # In[ ]: 172 | 173 | 174 | # Get the original file formats associated with the system. 175 | # This is returned as a comma-separated string. 176 | formats = system.fileFormat() 177 | 178 | # Append PDB format, so we can visualise the trajectory with VMD. 179 | formats += ",pdb" 180 | 181 | # Write to file and bind to the output. 182 | node.setOutput( 183 | "equilibrated", BSS.IO.saveMolecules("equilibrated", process.getSystem(), formats) 184 | ) 185 | 186 | 187 | # Get the trajectory, convert to MDTraj format and save to a DCD file. 188 | 189 | # In[ ]: 190 | 191 | 192 | # Get the BioSimSpace trajectory wrapper. 193 | trajectory = process.getTrajectory() 194 | 195 | # Extract the MDTraj object. 196 | traj = trajectory.getTrajectory(format="mdtraj") 197 | 198 | # Write to file. 199 | traj.save("equilibrated.dcd") 200 | 201 | # Bind to the output requirement. 202 | node.setOutput("trajectory", "equilibrated.dcd") 203 | 204 | 205 | # Validate the node. 206 | 207 | # In[ ]: 208 | 209 | 210 | node.validate() 211 | -------------------------------------------------------------------------------- /01_introduction/nodes/minimise.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # coding: utf-8 3 | 4 | # Author: Lester Hedges
5 | # Email:   lester.hedges@bristol.ac.uk 6 | # 7 | # # Minimisation 8 | # 9 | # In this notebook you'll learn how to use BioSimSpace to write a robust and interoperable workflow node to perform energy minimisation on a molecular system. 10 | # 11 | # First we'll need to import BioSimSpace: 12 | 13 | # In[ ]: 14 | 15 | 16 | import BioSimSpace as BSS 17 | 18 | 19 | # We begin by creating a `Node` object. This is the core of our molecular workflow component. It defines what it does, what input is needed, and the output that is produced. 20 | 21 | # In[ ]: 22 | 23 | 24 | node = BSS.Gateway.Node( 25 | "A node to perform energy minimisation and save the minimised molecular configuration to file." 26 | ) 27 | 28 | 29 | # We'll now set the author and license: 30 | 31 | # In[ ]: 32 | 33 | 34 | node.addAuthor( 35 | name="Lester Hedges", 36 | email="lester.hedges@bristol.ac.uk", 37 | affiliation="University of Bristol", 38 | ) 39 | node.setLicense("GPLv3") 40 | 41 | 42 | # Nodes require inputs. To specify inputs we use the `Gateway` module, which is used as a bridge between BioSimSpace and the outside world. This will allow us to document the inputs, define their type, and specify any constraints on their allowed values. Here we will need a set of files that define the molecular system, and an integer that indicates the number of minimisation steps to perform. 43 | 44 | # In[ ]: 45 | 46 | 47 | node.addInput("files", BSS.Gateway.FileSet(help="A set of molecular input files.")) 48 | 49 | node.addInput( 50 | "steps", 51 | BSS.Gateway.Integer( 52 | help="The number of minimisation steps.", 53 | minimum=0, 54 | maximum=1000000, 55 | default=10000, 56 | ), 57 | ) 58 | 59 | node.addInput( 60 | "engine", 61 | BSS.Gateway.String( 62 | help="The molecular dynamics engine.", allowed=BSS.MD.engines(), default="auto" 63 | ), 64 | ) 65 | 66 | 67 | # Note that the input requirement `steps` has a default value, so it is optional. 68 | # 69 | # We now need to define the output of the node. In this case we will return a set of files representing the minimised molecular system. 70 | 71 | # In[ ]: 72 | 73 | 74 | node.addOutput("minimised", BSS.Gateway.FileSet(help="The minimised molecular system.")) 75 | 76 | 77 | # When working interactively within a Jupyter notebook we need a way to allow users to set the input requirements. The `node.showControls` method will display a graphical user interface (GUI), from which inputs can be set. 78 | # 79 | # Note that the GUI requires active user input. All input requirements that don't have a default value _must_ be set before the node can proceed. If you try to query the node for one of the user values then an error will be raised. For bounded integer inputs you can use a slider to set the value, or type in the input box and press enter. 80 | # 81 | # When working interactively you will typically be running on a remote server where you won't have access to the local filesystem. In this case you'll need to upload files for any of the `File` or `FileSet` input requirements. The GUI below will provide buttons that allow you to browse your own filesystem and select files. Since Jupyter has a limit of 5MB for file transfers, we provide support for compressed formats, such as `.zip` or `.tar.gz`. (A single archive can contain a set of files, allowing you to set a single value for a `FileSet` requirement.) We've provided some example input files that can be used in the training notebooks, which are available to download from the links below. These can then be re-uploaded using the GUI. 82 | # 83 | # AMBER: [ala.crd](https://raw.githubusercontent.com/michellab/BioSimSpace/devel/demo/amber/ala/ala.crd), [ala.top](https://raw.githubusercontent.com/michellab/BioSimSpace/devel/demo/amber/ala/ala.top) 84 | # 85 | # GROMACS: [kigaki.gro](https://raw.githubusercontent.com/michellab/BioSimSpace/devel/demo/gromacs/kigaki/kigaki.gro), [kigaki.top](https://raw.githubusercontent.com/michellab/BioSimSpace/devel/demo/gromacs/kigaki/kigaki.top) 86 | # 87 | # NAMD: [alanin.pdb](https://raw.githubusercontent.com/michellab/BioSimSpace/devel/demo/namd/alanin/alanin.pdb), [alanin.psf](https://raw.githubusercontent.com/michellab/BioSimSpace/devel/demo/namd/alanin/alanin.psf), [alanin.params](https://raw.githubusercontent.com/michellab/BioSimSpace/devel/demo/namd/alanin/alanin.params) 88 | # 89 | # 90 | # When uploading files the name of the current file(s) will replace the `Upload` button. If you need to change the file, simply click on the button again and choose a new file. 91 | 92 | # In[ ]: 93 | 94 | 95 | node.showControls() 96 | 97 | 98 | # Once all requirements are set then we can acces the values using the `node.getInput` method. The first time this is called the `node` will automatically validate all of the input and report the user if any errors were found. 99 | # 100 | # We'll now create a molecular system using the input files uploaded by the user. Note that we don't specify the format of the files, since this is automatically determined by BioSimSpace. (BioSimSpace has support for a wide range of formats and can convert between certain formats too.) 101 | 102 | # In[ ]: 103 | 104 | 105 | system = BSS.IO.readMolecules(node.getInput("files")) 106 | 107 | 108 | # In order to run a minimisation we need to define a protocol. This can be done using the `BioSimSpace.Protocol` module. Here we will create a "best practice" minimisation protocol, overriding the number of steps with the input from the user. 109 | 110 | # In[ ]: 111 | 112 | 113 | protocol = BSS.Protocol.Minimisation(steps=node.getInput("steps")) 114 | 115 | 116 | # We now have everything that is required to run a minimisation. To do so, we use `BioSimSpace.MD` to find an appropriate molecular dynamics package on our current environment. What package is found will depend upon both the system and protocol, as well as the hardware that is available to the user. (For example, the user can choose to find packages with GPU support.) 117 | 118 | # In[ ]: 119 | 120 | 121 | process = BSS.MD.run(system, protocol, engine=node.getInput("engine")) 122 | 123 | 124 | # We now wait for the process to finish, then check whether there were any errors before continuing. If errors were raised, then we raise an exception and print the last 10 lines of `stdout` and `stderr` to the user. 125 | 126 | # In[ ]: 127 | 128 | 129 | process.wait() 130 | 131 | if process.isError(): 132 | print(process.stdout(10)) 133 | print(process.stdout(10)) 134 | raise RuntimeError("The process exited with an error!") 135 | 136 | 137 | # When the process has finished running we can get the minimised molecular configuration. We will save this to file using the same format as the original system, and set the output requirement to the list of file names that were written. 138 | 139 | # In[ ]: 140 | 141 | 142 | node.setOutput( 143 | "minimised", 144 | BSS.IO.saveMolecules("minimised", process.getSystem(), system.fileFormat()), 145 | ) 146 | 147 | 148 | # Finally, we validate that the node completed succesfully. This will check that all output requirements are satisfied. Any file outputs will be available for the user to download as a compressed archive. 149 | # 150 | # Note that the validation will fail until the cell above finishes running. 151 | 152 | # In[ ]: 153 | 154 | 155 | node.validate() 156 | 157 | 158 | # Once we are satisfied with our node we can choosed to download it as a regular Python script that can be run from the command-line. 159 | # 160 | # In JupyterHub, click on: `File/Download As/Python`\ 161 | # In JupyterLab, click on: `File/Export Notebook As/Export Notebook to Executable Script` 162 | -------------------------------------------------------------------------------- /01_introduction/nodes/parameterise.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "Author: Lester Hedges
\n", 8 | "Email:   lester.hedges@bristol.ac.uk\n", 9 | "\n", 10 | "# Parameterisation\n", 11 | "\n", 12 | "A node to perform parameterisation of a molecule loaded from PDB file. Saves the parameterised molecule in to AMBER format files." 13 | ] 14 | }, 15 | { 16 | "cell_type": "code", 17 | "execution_count": null, 18 | "metadata": {}, 19 | "outputs": [], 20 | "source": [ 21 | "import BioSimSpace as BSS" 22 | ] 23 | }, 24 | { 25 | "cell_type": "code", 26 | "execution_count": null, 27 | "metadata": {}, 28 | "outputs": [], 29 | "source": [ 30 | "node = BSS.Gateway.Node(\n", 31 | " \"A node to perform parameterisation of a molecule loaded from PDB file. Saves the parameterised molecule in to AMBER format files.\"\n", 32 | ")\n", 33 | "node.addAuthor(\n", 34 | " name=\"Lester Hedges\",\n", 35 | " email=\"lester.hedges@bristol.ac.uk\",\n", 36 | " affiliation=\"University of Bristol\",\n", 37 | ")\n", 38 | "node.setLicense(\"GPLv3\")" 39 | ] 40 | }, 41 | { 42 | "cell_type": "markdown", 43 | "metadata": {}, 44 | "source": [ 45 | "Set the input requirements:" 46 | ] 47 | }, 48 | { 49 | "cell_type": "code", 50 | "execution_count": null, 51 | "metadata": {}, 52 | "outputs": [], 53 | "source": [ 54 | "node.addInput(\n", 55 | " \"pdb\",\n", 56 | " BSS.Gateway.File(\n", 57 | " help=\"A Protein Data Bank (PDB) file containing a single molecule.\"\n", 58 | " ),\n", 59 | ")\n", 60 | "\n", 61 | "node.addInput(\n", 62 | " \"forcefield\",\n", 63 | " BSS.Gateway.String(\n", 64 | " help=\"The force field to parameterise the molecule with.\",\n", 65 | " allowed=BSS.Parameters.forceFields(),\n", 66 | " ),\n", 67 | ")\n", 68 | "\n", 69 | "node.addInput(\n", 70 | " \"water_model\",\n", 71 | " BSS.Gateway.String(\n", 72 | " help=\"The water model to use for ion parameters.\",\n", 73 | " allowed=BSS.Solvent.waterModels(),\n", 74 | " default=\"tip3p\",\n", 75 | " ),\n", 76 | ")\n", 77 | "\n", 78 | "node.addInput(\n", 79 | " \"pdb4amber\",\n", 80 | " BSS.Gateway.Boolean(\n", 81 | " help=\"Whether to pre-process the PDB file using pdb4amber.\", default=False\n", 82 | " ),\n", 83 | ")" 84 | ] 85 | }, 86 | { 87 | "cell_type": "markdown", 88 | "metadata": {}, 89 | "source": [ 90 | "We now need to define the output of the node. In this case we will return a set of files representing the parameterised molecule in AMBER format." 91 | ] 92 | }, 93 | { 94 | "cell_type": "code", 95 | "execution_count": null, 96 | "metadata": {}, 97 | "outputs": [], 98 | "source": [ 99 | "node.addOutput(\"parameterised\", BSS.Gateway.FileSet(help=\"The parameterised molecule.\"))" 100 | ] 101 | }, 102 | { 103 | "cell_type": "code", 104 | "execution_count": null, 105 | "metadata": {}, 106 | "outputs": [], 107 | "source": [ 108 | "node.showControls()" 109 | ] 110 | }, 111 | { 112 | "cell_type": "markdown", 113 | "metadata": {}, 114 | "source": [ 115 | "Load the PDB file and pre-process with `pdb4amber` if requested. Since we assume a single molecule PDB, take the first molecule in the file." 116 | ] 117 | }, 118 | { 119 | "cell_type": "code", 120 | "execution_count": null, 121 | "metadata": {}, 122 | "outputs": [], 123 | "source": [ 124 | "molecule = BSS.IO.readPDB(node.getInput(\"pdb\"), pdb4amber=node.getInput(\"pdb4amber\"))[0]" 125 | ] 126 | }, 127 | { 128 | "cell_type": "markdown", 129 | "metadata": {}, 130 | "source": [ 131 | "Perform the parameterisation using the chosen force field and ion water model. Note that we call the generic `BSS.Parameters.parameterise` function so that we can pass the force field name as an argument." 132 | ] 133 | }, 134 | { 135 | "cell_type": "code", 136 | "execution_count": null, 137 | "metadata": {}, 138 | "outputs": [], 139 | "source": [ 140 | "molecule = BSS.Parameters.parameterise(\n", 141 | " molecule, node.getInput(\"forcefield\"), water_model=node.getInput(\"water_model\")\n", 142 | ").getMolecule()" 143 | ] 144 | }, 145 | { 146 | "cell_type": "markdown", 147 | "metadata": {}, 148 | "source": [ 149 | "Now save the parameterise molecule to AMBER format files." 150 | ] 151 | }, 152 | { 153 | "cell_type": "code", 154 | "execution_count": null, 155 | "metadata": {}, 156 | "outputs": [], 157 | "source": [ 158 | "node.setOutput(\n", 159 | " \"parameterised\", BSS.IO.saveMolecules(\"parameterised\", molecule, [\"prm7\", \"rst7\"])\n", 160 | ")" 161 | ] 162 | }, 163 | { 164 | "cell_type": "markdown", 165 | "metadata": {}, 166 | "source": [ 167 | "Validate the node." 168 | ] 169 | }, 170 | { 171 | "cell_type": "code", 172 | "execution_count": null, 173 | "metadata": {}, 174 | "outputs": [], 175 | "source": [ 176 | "node.validate()" 177 | ] 178 | } 179 | ], 180 | "metadata": { 181 | "kernelspec": { 182 | "display_name": "Python 3", 183 | "language": "python", 184 | "name": "python3" 185 | }, 186 | "language_info": { 187 | "codemirror_mode": { 188 | "name": "ipython", 189 | "version": 3 190 | }, 191 | "file_extension": ".py", 192 | "mimetype": "text/x-python", 193 | "name": "python", 194 | "nbconvert_exporter": "python", 195 | "pygments_lexer": "ipython3" 196 | } 197 | }, 198 | "nbformat": 4, 199 | "nbformat_minor": 2 200 | } 201 | -------------------------------------------------------------------------------- /01_introduction/nodes/parameterise.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # coding: utf-8 3 | 4 | # Author: Lester Hedges
5 | # Email:   lester.hedges@bristol.ac.uk 6 | # 7 | # # Parameterisation 8 | # 9 | # A node to perform parameterisation of a molecule loaded from PDB file. Saves the parameterised molecule in to AMBER format files. 10 | 11 | # In[ ]: 12 | 13 | 14 | import BioSimSpace as BSS 15 | 16 | 17 | # In[ ]: 18 | 19 | 20 | node = BSS.Gateway.Node( 21 | "A node to perform parameterisation of a molecule loaded from PDB file. Saves the parameterised molecule in to AMBER format files." 22 | ) 23 | node.addAuthor( 24 | name="Lester Hedges", 25 | email="lester.hedges@bristol.ac.uk", 26 | affiliation="University of Bristol", 27 | ) 28 | node.setLicense("GPLv3") 29 | 30 | 31 | # Set the input requirements: 32 | 33 | # In[ ]: 34 | 35 | 36 | node.addInput( 37 | "pdb", 38 | BSS.Gateway.File( 39 | help="A Protein Data Bank (PDB) file containing a single molecule." 40 | ), 41 | ) 42 | 43 | node.addInput( 44 | "forcefield", 45 | BSS.Gateway.String( 46 | help="The force field to parameterise the molecule with.", 47 | allowed=BSS.Parameters.forceFields(), 48 | ), 49 | ) 50 | 51 | node.addInput( 52 | "water_model", 53 | BSS.Gateway.String( 54 | help="The water model to use for ion parameters.", 55 | allowed=BSS.Solvent.waterModels(), 56 | default="tip3p", 57 | ), 58 | ) 59 | 60 | node.addInput( 61 | "pdb4amber", 62 | BSS.Gateway.Boolean( 63 | help="Whether to pre-process the PDB file using pdb4amber.", default=False 64 | ), 65 | ) 66 | 67 | 68 | # We now need to define the output of the node. In this case we will return a set of files representing the parameterised molecule in AMBER format. 69 | 70 | # In[ ]: 71 | 72 | 73 | node.addOutput("parameterised", BSS.Gateway.FileSet(help="The parameterised molecule.")) 74 | 75 | 76 | # In[ ]: 77 | 78 | 79 | node.showControls() 80 | 81 | 82 | # Load the PDB file and pre-process with `pdb4amber` if requested. Since we assume a single molecule PDB, take the first molecule in the file. 83 | 84 | # In[ ]: 85 | 86 | 87 | molecule = BSS.IO.readPDB(node.getInput("pdb"), pdb4amber=node.getInput("pdb4amber"))[0] 88 | 89 | 90 | # Perform the parameterisation using the chosen force field and ion water model. Note that we call the generic `BSS.Parameters.parameterise` function so that we can pass the force field name as an argument. 91 | 92 | # In[ ]: 93 | 94 | 95 | molecule = BSS.Parameters.parameterise( 96 | molecule, node.getInput("forcefield"), water_model=node.getInput("water_model") 97 | ).getMolecule() 98 | 99 | 100 | # Now save the parameterise molecule to AMBER format files. 101 | 102 | # In[ ]: 103 | 104 | 105 | node.setOutput( 106 | "parameterised", BSS.IO.saveMolecules("parameterised", molecule, ["prm7", "rst7"]) 107 | ) 108 | 109 | 110 | # Validate the node. 111 | 112 | # In[ ]: 113 | 114 | 115 | node.validate() 116 | -------------------------------------------------------------------------------- /01_introduction/nodes/solvate.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "Author: Lester Hedges
\n", 8 | "Email:   lester.hedges@bristol.ac.uk\n", 9 | "\n", 10 | "# Solvation\n", 11 | "\n", 12 | "A node to solvate a parameterised molecular system. An appropriate box size will be determine from the axis-aligned bounding box of the molecules in the system" 13 | ] 14 | }, 15 | { 16 | "cell_type": "code", 17 | "execution_count": null, 18 | "metadata": {}, 19 | "outputs": [], 20 | "source": [ 21 | "import BioSimSpace as BSS" 22 | ] 23 | }, 24 | { 25 | "cell_type": "code", 26 | "execution_count": null, 27 | "metadata": {}, 28 | "outputs": [], 29 | "source": [ 30 | "node = BSS.Gateway.Node(\n", 31 | " \"A node to solvate a parameterised molecular system. An appropriate box size will be determine from the axis-aligned bounding box of the molecules in the system.\"\n", 32 | ")\n", 33 | "node.addAuthor(\n", 34 | " name=\"Lester Hedges\",\n", 35 | " email=\"lester.hedges@bristol.ac.uk\",\n", 36 | " affiliation=\"University of Bristol\",\n", 37 | ")\n", 38 | "node.setLicense(\"GPLv3\")" 39 | ] 40 | }, 41 | { 42 | "cell_type": "markdown", 43 | "metadata": {}, 44 | "source": [ 45 | "Set the input requirements:" 46 | ] 47 | }, 48 | { 49 | "cell_type": "code", 50 | "execution_count": null, 51 | "metadata": {}, 52 | "outputs": [], 53 | "source": [ 54 | "node.addInput(\"files\", BSS.Gateway.FileSet(help=\"A set of molecular input files.\"))\n", 55 | "\n", 56 | "node.addInput(\n", 57 | " \"water_model\",\n", 58 | " BSS.Gateway.String(\n", 59 | " help=\"The water model.\", allowed=BSS.Solvent.waterModels(), default=\"tip3p\"\n", 60 | " ),\n", 61 | ")\n", 62 | "\n", 63 | "node.addInput(\n", 64 | " \"box\",\n", 65 | " BSS.Gateway.String(\n", 66 | " help=\"The type of simulation box.\", allowed=BSS.Box.boxTypes(), default=\"cubic\"\n", 67 | " ),\n", 68 | ")\n", 69 | "\n", 70 | "node.addInput(\n", 71 | " \"neutralise\",\n", 72 | " BSS.Gateway.Boolean(\n", 73 | " help=\"Whether to neutralise the system. Ions will be added on top of any specified with 'ion_conc'\",\n", 74 | " default=True,\n", 75 | " ),\n", 76 | ")\n", 77 | "\n", 78 | "node.addInput(\n", 79 | " \"ion_conc\",\n", 80 | " BSS.Gateway.Float(help=\"The ion concentration in mol per litre\", default=0),\n", 81 | ")" 82 | ] 83 | }, 84 | { 85 | "cell_type": "markdown", 86 | "metadata": {}, 87 | "source": [ 88 | "We now need to define the output of the node. In this case we will return a set of files representing the parameterised molecule in the file format(s) of the original system." 89 | ] 90 | }, 91 | { 92 | "cell_type": "code", 93 | "execution_count": null, 94 | "metadata": {}, 95 | "outputs": [], 96 | "source": [ 97 | "node.addOutput(\"solvated\", BSS.Gateway.FileSet(help=\"The solvated system.\"))" 98 | ] 99 | }, 100 | { 101 | "cell_type": "code", 102 | "execution_count": null, 103 | "metadata": {}, 104 | "outputs": [], 105 | "source": [ 106 | "node.showControls()" 107 | ] 108 | }, 109 | { 110 | "cell_type": "markdown", 111 | "metadata": {}, 112 | "source": [ 113 | "Load the molecular system." 114 | ] 115 | }, 116 | { 117 | "cell_type": "code", 118 | "execution_count": null, 119 | "metadata": {}, 120 | "outputs": [], 121 | "source": [ 122 | "system = BSS.IO.readMolecules(node.getInput(\"files\"))" 123 | ] 124 | }, 125 | { 126 | "cell_type": "markdown", 127 | "metadata": {}, 128 | "source": [ 129 | "Now work out the minimum box size for the molecules in the system." 130 | ] 131 | }, 132 | { 133 | "cell_type": "code", 134 | "execution_count": null, 135 | "metadata": {}, 136 | "outputs": [], 137 | "source": [ 138 | "# Get the minimium and maximum coordinates of the bounding box that\n", 139 | "# minimally encloses the protein.\n", 140 | "box_min, box_max = system.getAxisAlignedBoundingBox()\n", 141 | "\n", 142 | "# Work out the box size from the difference in the coordinates.\n", 143 | "box_size = [y - x for x, y in zip(box_min, box_max)]\n", 144 | "\n", 145 | "# How much to pad each side of the molecule? (Nonbonded cutoff = 10 A)\n", 146 | "padding = 15 * BSS.Units.Length.angstrom\n", 147 | "\n", 148 | "# Work out an appropriate box. This will used in each dimension to ensure\n", 149 | "# that the cutoff constraints are satisfied if the molecule rotates.\n", 150 | "box_length = max(box_size) + 2 * padding" 151 | ] 152 | }, 153 | { 154 | "cell_type": "markdown", 155 | "metadata": {}, 156 | "source": [ 157 | "Get the box parameters for the chosen box type." 158 | ] 159 | }, 160 | { 161 | "cell_type": "code", 162 | "execution_count": null, 163 | "metadata": {}, 164 | "outputs": [], 165 | "source": [ 166 | "box, angles = BSS.Box.generateBoxParameters(node.getInput(\"box\"), box_length)" 167 | ] 168 | }, 169 | { 170 | "cell_type": "markdown", 171 | "metadata": {}, 172 | "source": [ 173 | "Now solvate with the chosen water model and ion concentration." 174 | ] 175 | }, 176 | { 177 | "cell_type": "code", 178 | "execution_count": null, 179 | "metadata": {}, 180 | "outputs": [], 181 | "source": [ 182 | "solvated = BSS.Solvent.solvate(\n", 183 | " node.getInput(\"water_model\"),\n", 184 | " molecule=system,\n", 185 | " box=box,\n", 186 | " angles=angles,\n", 187 | " is_neutral=node.getInput(\"neutralise\"),\n", 188 | " ion_conc=node.getInput(\"ion_conc\"),\n", 189 | ")" 190 | ] 191 | }, 192 | { 193 | "cell_type": "markdown", 194 | "metadata": {}, 195 | "source": [ 196 | "Write the solvated system to file in the same format as the original system." 197 | ] 198 | }, 199 | { 200 | "cell_type": "code", 201 | "execution_count": null, 202 | "metadata": {}, 203 | "outputs": [], 204 | "source": [ 205 | "node.setOutput(\n", 206 | " \"solvated\", BSS.IO.saveMolecules(\"solvated\", solvated, system.fileFormat())\n", 207 | ")" 208 | ] 209 | }, 210 | { 211 | "cell_type": "markdown", 212 | "metadata": {}, 213 | "source": [ 214 | "Validate the node." 215 | ] 216 | }, 217 | { 218 | "cell_type": "code", 219 | "execution_count": null, 220 | "metadata": {}, 221 | "outputs": [], 222 | "source": [ 223 | "node.validate()" 224 | ] 225 | } 226 | ], 227 | "metadata": { 228 | "kernelspec": { 229 | "display_name": "Python 3", 230 | "language": "python", 231 | "name": "python3" 232 | }, 233 | "language_info": { 234 | "codemirror_mode": { 235 | "name": "ipython", 236 | "version": 3 237 | }, 238 | "file_extension": ".py", 239 | "mimetype": "text/x-python", 240 | "name": "python", 241 | "nbconvert_exporter": "python", 242 | "pygments_lexer": "ipython3" 243 | } 244 | }, 245 | "nbformat": 4, 246 | "nbformat_minor": 2 247 | } 248 | -------------------------------------------------------------------------------- /01_introduction/nodes/solvate.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # coding: utf-8 3 | 4 | # Author: Lester Hedges
5 | # Email:   lester.hedges@bristol.ac.uk 6 | # 7 | # # Solvation 8 | # 9 | # A node to solvate a parameterised molecular system. An appropriate box size will be determine from the axis-aligned bounding box of the molecules in the system 10 | 11 | # In[ ]: 12 | 13 | 14 | import BioSimSpace as BSS 15 | 16 | 17 | # In[ ]: 18 | 19 | 20 | node = BSS.Gateway.Node( 21 | "A node to solvate a parameterised molecular system. An appropriate box size will be determine from the axis-aligned bounding box of the molecules in the system." 22 | ) 23 | node.addAuthor( 24 | name="Lester Hedges", 25 | email="lester.hedges@bristol.ac.uk", 26 | affiliation="University of Bristol", 27 | ) 28 | node.setLicense("GPLv3") 29 | 30 | 31 | # Set the input requirements: 32 | 33 | # In[ ]: 34 | 35 | 36 | node.addInput("files", BSS.Gateway.FileSet(help="A set of molecular input files.")) 37 | 38 | node.addInput( 39 | "water_model", 40 | BSS.Gateway.String( 41 | help="The water model.", allowed=BSS.Solvent.waterModels(), default="tip3p" 42 | ), 43 | ) 44 | 45 | node.addInput( 46 | "box", 47 | BSS.Gateway.String( 48 | help="The type of simulation box.", allowed=BSS.Box.boxTypes(), default="cubic" 49 | ), 50 | ) 51 | 52 | node.addInput( 53 | "neutralise", 54 | BSS.Gateway.Boolean( 55 | help="Whether to neutralise the system. Ions will be added on top of any specified with 'ion_conc'", 56 | default=True, 57 | ), 58 | ) 59 | 60 | node.addInput( 61 | "ion_conc", 62 | BSS.Gateway.Float(help="The ion concentration in mol per litre", default=0), 63 | ) 64 | 65 | 66 | # We now need to define the output of the node. In this case we will return a set of files representing the parameterised molecule in the file format(s) of the original system. 67 | 68 | # In[ ]: 69 | 70 | 71 | node.addOutput("solvated", BSS.Gateway.FileSet(help="The solvated system.")) 72 | 73 | 74 | # In[ ]: 75 | 76 | 77 | node.showControls() 78 | 79 | 80 | # Load the molecular system. 81 | 82 | # In[ ]: 83 | 84 | 85 | system = BSS.IO.readMolecules(node.getInput("files")) 86 | 87 | 88 | # Now work out the minimum box size for the molecules in the system. 89 | 90 | # In[ ]: 91 | 92 | 93 | # Get the minimium and maximum coordinates of the bounding box that 94 | # minimally encloses the protein. 95 | box_min, box_max = system.getAxisAlignedBoundingBox() 96 | 97 | # Work out the box size from the difference in the coordinates. 98 | box_size = [y - x for x, y in zip(box_min, box_max)] 99 | 100 | # How much to pad each side of the molecule? (Nonbonded cutoff = 10 A) 101 | padding = 15 * BSS.Units.Length.angstrom 102 | 103 | # Work out an appropriate box. This will used in each dimension to ensure 104 | # that the cutoff constraints are satisfied if the molecule rotates. 105 | box_length = max(box_size) + 2 * padding 106 | 107 | 108 | # Get the box parameters for the chosen box type. 109 | 110 | # In[ ]: 111 | 112 | 113 | box, angles = BSS.Box.generateBoxParameters(node.getInput("box"), box_length) 114 | 115 | 116 | # Now solvate with the chosen water model and ion concentration. 117 | 118 | # In[ ]: 119 | 120 | 121 | solvated = BSS.Solvent.solvate( 122 | node.getInput("water_model"), 123 | molecule=system, 124 | box=box, 125 | angles=angles, 126 | is_neutral=node.getInput("neutralise"), 127 | ion_conc=node.getInput("ion_conc"), 128 | ) 129 | 130 | 131 | # Write the solvated system to file in the same format as the original system. 132 | 133 | # In[ ]: 134 | 135 | 136 | node.setOutput( 137 | "solvated", BSS.IO.saveMolecules("solvated", solvated, system.fileFormat()) 138 | ) 139 | 140 | 141 | # Validate the node. 142 | 143 | # In[ ]: 144 | 145 | 146 | node.validate() 147 | -------------------------------------------------------------------------------- /01_introduction/scripts/workflow.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # Exit immediately on error. 4 | set -e 5 | 6 | # Remove existing output. 7 | rm -f parameterised.* 8 | rm -f solvated.* 9 | rm -f minimised.* 10 | 11 | echo "Parameterising..." 12 | python nodes/parameterise.py --pdb inputs/methanol.pdb --forcefield gaff 13 | 14 | echo "Solvating..." 15 | python nodes/solvate.py --files parameterised.* --water_model tip3p 16 | 17 | echo "Minimising..." 18 | python nodes/minimise.py --files solvated.* --steps 1000 19 | 20 | echo "Equilibrating..." 21 | python nodes/equilibrate.py --files minimised.* --restraint heavy 22 | 23 | echo "Done!" 24 | -------------------------------------------------------------------------------- /02_funnel_metad/01_bss-fun-metad-setup.md: -------------------------------------------------------------------------------- 1 | # Part 2 - Setting up the system, visualising the funnel and preparing the fun-metaD simulations 2 | 3 | In this part of the tutorial I will show you how to set up the BioSimSpace system, parameterising the protein and the ligand, as well as defining the simulation box, adding water and ions. Then, we will define a the funnel and visualise it using NGLview. Finally, we will setup the directories for the fun-metaD simulation itself. 4 | 5 | ## Part 2.1 - setup 6 | 7 | First, let's download inputs for this tutorial 8 | 9 | 10 | ```python 11 | from get_tutorial import download 12 | download("01") 13 | ``` 14 | 15 | Now import BioSimSpace 16 | 17 | 18 | ```python 19 | import BioSimSpace as BSS 20 | import os 21 | ``` 22 | 23 | In order to parameterise the protein, you just need a tleap-ready protein PDB file. We will use Amber ff14sb forcefield. 24 | 25 | 26 | ```python 27 | protein = BSS.IO.readMolecules("inputs_01/protein.pdb") 28 | protein_water = protein.getWaterMolecules() 29 | 30 | protein_search = protein.search("not water") 31 | protein = protein_search.molecules().getResult(0) 32 | 33 | protein = BSS.Parameters.ff14SB(protein, water_model="tip3p").getMolecule() 34 | ``` 35 | 36 | Parameterise the ligand using GAFF2. 37 | 38 | 39 | ```python 40 | # parameterise the ligand 41 | ligand = BSS.IO.readMolecules("inputs_01/ligand.mol2").getMolecule(0) 42 | ligand = BSS.Parameters.gaff2( 43 | ligand, net_charge=BSS.Parameters.formalCharge(ligand) 44 | ).getMolecule() 45 | ``` 46 | 47 | Now we solvate the protein-ligand system using TIP3P water. 48 | 49 | 50 | ```python 51 | # Find the dimensions of the protein 52 | box_min, box_max = protein.getAxisAlignedBoundingBox() 53 | 54 | # Work out the box size from the difference in the coordinates. 55 | box_size = [y - x for x, y in zip(box_min, box_max)] 56 | 57 | # how much to pad each side of the protein (nonbonded cutoff = 10 A) 58 | padding = 15 * BSS.Units.Length.angstrom 59 | 60 | box_length = max(box_size) + 2 * padding 61 | 62 | cmplx = protein + ligand 63 | 64 | solvated = BSS.Solvent.tip3p(molecule=cmplx.toSystem(), box=3 * [box_length]) 65 | ``` 66 | 67 | Save the prepared structures. 68 | 69 | 70 | ```python 71 | BSS.IO.saveMolecules("solvated", solvated, ["PDB", "RST7", "PRM7"]) 72 | ``` 73 | 74 | ## Part 2.2 - visualisation 75 | Before we run any dynamics, let's visualise the funnel defined automatically by BSS. 76 | 77 | 78 | ```python 79 | # Load the system. 80 | system = BSS.IO.readMolecules( 81 | ["solvated.rst7", "solvated.prm7"] 82 | ) 83 | 84 | # Create the funnel parameters. 85 | p0, p1 = BSS.Metadynamics.CollectiveVariable.makeFunnel(system) 86 | 87 | # Define the collective variable. 88 | funnel_cv = BSS.Metadynamics.CollectiveVariable.Funnel(p0, p1) 89 | 90 | # Create a view. 91 | view = BSS.Metadynamics.CollectiveVariable.viewFunnel(system, funnel_cv) 92 | 93 | view.molecules([1, 0, -1]) 94 | ``` 95 | 96 | Lets check what the funnel looks with a larger radius. 97 | 98 | 99 | ```python 100 | # Load the system. 101 | system = BSS.IO.readMolecules( 102 | ["solvated.rst7", "solvated.prm7"] 103 | ) 104 | 105 | # Create the funnel parameters. 106 | p0, p1 = BSS.Metadynamics.CollectiveVariable.makeFunnel(system) 107 | 108 | # Define the collective variable. 109 | funnel_cv = BSS.Metadynamics.CollectiveVariable.Funnel( 110 | p0, p1, width=1.5 * BSS.Units.Length.nanometer 111 | ) 112 | 113 | # Create a view. 114 | view = BSS.Metadynamics.CollectiveVariable.viewFunnel(system, funnel_cv) 115 | 116 | view.molecules([1, 0, -1]) 117 | ``` 118 | 119 | In case the funnel was assigned incorrectly, you can open the structure file and use lists of atom IDs to define the p0 and p1 points. 120 | 121 | Here I will load the same structure file with the p0 and p1 points defined by me. 122 | 123 | 124 | ```python 125 | # Load the system. 126 | system = BSS.IO.readMolecules(["solvated.pdb"]) 127 | 128 | # Create the funnel parameters. 129 | p0, p1 = [2624], [584, 1307, 1474, 1887] 130 | 131 | # Define the collective variable. 132 | funnel_cv = BSS.Metadynamics.CollectiveVariable.Funnel(p0, p1) 133 | 134 | # Create a view. 135 | view = BSS.Metadynamics.CollectiveVariable.viewFunnel(system, funnel_cv) 136 | 137 | view.molecules([1, 0, -1]) 138 | ``` 139 | 140 | # Part 2.3 - Equilibrate the system with OpenMM 141 | Load the prepared system first. 142 | 143 | 144 | ```python 145 | solvated = BSS.IO.readMolecules( 146 | ["solvated.rst7", "solvated.prm7"] 147 | ) 148 | ``` 149 | 150 | The process below will take ~ 5 minutes if run on openbiosim's default Jupyter server. Running the notebook on a GPU powered computer should accelerate the calculations significantly 151 | 152 | 153 | ```python 154 | protocol = BSS.Protocol.Minimisation() 155 | process = BSS.Process.OpenMM(solvated, protocol) 156 | # Start the process in the background. 157 | process.start() 158 | 159 | # Wait for the process to finish. 160 | process.wait() 161 | ``` 162 | 163 | Get the minimised system and save the structure. 164 | 165 | 166 | ```python 167 | minimised = process.getSystem() 168 | # BSS.IO.saveMolecules('minimised',minimised, ['PDB','RST7','PRM7']) # if you want to save the output 169 | ``` 170 | 171 | Equilibrate (note the very short run time). This will take ca. 3 min on openbiosim's Jupyter server. 172 | 173 | 174 | ```python 175 | protocol = BSS.Protocol.Equilibration(runtime=0.001 * BSS.Units.Time.nanosecond) 176 | process = BSS.Process.OpenMM(minimised, protocol) 177 | 178 | # Start the process in the background. 179 | process.start() 180 | 181 | # Wait for the process to finish. 182 | process.wait() 183 | ``` 184 | 185 | Get the equilibrated system and save the structure 186 | 187 | 188 | ```python 189 | equilibrated = process.getSystem() 190 | 191 | # BSS.IO.saveMolecules('equilibrated',equilibrated, ['PDB','RST7','PRM7']) # if you want to save the output 192 | ``` 193 | 194 | ## Part 2.4 - Setup and run fun-metaD 195 | 196 | As I showed in the visualisation part, select the p0, p1 points for the funnel definition. 197 | 198 | 199 | ```python 200 | p0, p1 = BSS.Metadynamics.CollectiveVariable.makeFunnel(equilibrated) 201 | ``` 202 | 203 | Write the funnel CV for the simulation. Additionally, we will set a lower upper_bound value. 204 | 205 | 206 | ```python 207 | new_upper_bound = BSS.Metadynamics.Bound(value=3.5 * BSS.Units.Length.nanometer) 208 | 209 | funnel_cv = BSS.Metadynamics.CollectiveVariable.Funnel( 210 | p0, p1, upper_bound=new_upper_bound 211 | ) 212 | ``` 213 | 214 | Write the metadynamics protocol. 215 | 216 | 217 | ```python 218 | protocol = BSS.Protocol.Metadynamics( 219 | funnel_cv, 220 | runtime=0.01 * BSS.Units.Time.nanosecond, 221 | hill_height=1.5 * BSS.Units.Energy.kj_per_mol, 222 | restart_interval=1000, 223 | bias_factor=10, 224 | ) 225 | ``` 226 | 227 | Run the metadynamics simulation. 228 | 229 | 230 | ```python 231 | process = BSS.Process.OpenMM( 232 | equilibrated, protocol, work_dir="fun-metaD-work_dir" 233 | ) 234 | 235 | process.start() 236 | 237 | process.wait() 238 | ``` 239 | 240 | Save the final structures. 241 | 242 | 243 | ```python 244 | finished = process.getSystem() 245 | 246 | # BSS.IO.saveMolecules('final', finished, ['PDB','RST7','PRM7']) # if you want to save the output 247 | ``` 248 | 249 | ## Closing thoughts 250 | 251 | The purpose of this notebook has been to demonstrate the logic behind using BioSimSpace to setup and run your funnel metadynamics simulations, as well as visualising the suggested funnel restraints. 252 | 253 | In a real use case, you wouldn't do all of these steps in an interactive notebook. The funnel metadynamics simulations usually require 500-2000 ns of simulation time, which takes between 3 to 14 days for moderately sized systems. You want to submit a script that does the setting up, equilibration and production simulations all in one go. With Lester's help, I've prepared a set of nodes and a LSF submit script for doing all of the steps described above. Check out the `example_node` directory. 254 | -------------------------------------------------------------------------------- /02_funnel_metad/02_bss-fun-metad-analysis.md: -------------------------------------------------------------------------------- 1 | # Part 2 - Analysis 2 | 3 | After running the simulations, let's see what information we can get out of it. The primary thing we are interested is the absolute binding free energy (ABFE) of ligand binding. What funnel metadynamics (fun-metaD) fundamentally does is deposit bias along a set of reaction coordinates (aka collective variables or CVs), forcing the ligand to unbind, explore the solvent phase, and eventually allow it to rebind back. 4 | 5 | Since we can track the amount of bias deposited along these CVs, we can construct a free energy surface by inverting the accumulated bias and projecting it along the CVs. To recover the ABFE, we can look at the projection CV and find the difference in free energy between the bound phase and the unbound/solvent phase. 6 | 7 | It is important to remember that we aren't truly samply the unbound phase, instead we limit the exploration of the ligand to a small volume at the 'tip' of the funnel. Correction for the loss of rotational and translational freedom of the ligand in the unbound phase due to the funnel restraints are 8 | calculated as described in [Rhys *et al* 2020](https://www.ncbi.nlm.nih.gov/pmc/articles/PMC7467642/). 9 | 10 | To determine whether the simulation has converged or not, we will have a look at the time evolution of the CVs and the free energy estimate. 11 | 12 | Let's import the inputs for this tutorial 13 | 14 | 15 | ```python 16 | from get_tutorial import download 17 | download("02") 18 | ``` 19 | 20 | Now we import the relevant libraries. 21 | 22 | 23 | ```python 24 | import BioSimSpace as BSS 25 | import numpy as np 26 | import matplotlib.pyplot as plt 27 | ``` 28 | 29 | We will need the CV definitions that we used in running the fun-metaD simulation to reconstruct the free energy surface using PLUMED. We begin with loading the system. 30 | 31 | 32 | ```python 33 | system = BSS.IO.readMolecules("inputs_02/0_run/fun-metaD/openmm.*7") 34 | ``` 35 | 36 | Now we redefine p0 and p1 points. 37 | 38 | 39 | ```python 40 | p0, p1 = BSS.Metadynamics.CollectiveVariable.makeFunnel(system) 41 | ``` 42 | 43 | Write the CV definitions as you put them into the BSS script. 44 | 45 | 46 | ```python 47 | new_upper_bound = BSS.Metadynamics.Bound(3.5 * BSS.Units.Length.nanometer) 48 | cv = BSS.Metadynamics.CollectiveVariable.Funnel(p0, p1, upper_bound=new_upper_bound) 49 | ``` 50 | 51 | Use the CV definitions to reconstruct the protocol. 52 | 53 | 54 | ```python 55 | protocol = BSS.Protocol.Metadynamics(cv) 56 | ``` 57 | 58 | Create a PLUMED process that will run the analysis 59 | 60 | 61 | ```python 62 | plumed = BSS.Process.Plumed(work_dir="inputs_02/0_run/fun-metaD/") 63 | config = plumed.createConfig(system, protocol) 64 | ``` 65 | 66 | ### Part 3.1 - CVs vs time 67 | 68 | Now we can plot the CVs across time. The first column, index 0, is the projection CV and the second column, index 1, is the extent CV. 69 | 70 | We can see that the extent CV is more or less diffusive - the ligand is rapidly exploring across the entire range of allowed CV values. 71 | 72 | The projection variable is much less diffusive, where the ligand is freely exploring the unbound phase, from about 2.0 nm to 3.5 nm, but is far slower in the bound phase. This is understandable, the ligand interacts with protein and is slowed down in its exploration. 73 | 74 | One of the most important things in determining whether the fun-metaD simulation has converged is the return of the ligand from the solvent back into the binding site. Looking at the projection CV, we see the ligand at start is around 1.0-1.2 nm from the origin. It unbinds and returns to that value multiple times, especially clearly at around 35-40 ns. At least one rebinding event is needed to construct an accurate FES. 75 | 76 | 77 | ```python 78 | BSS.Notebook.plot( 79 | x=plumed.getTime(time_series=True), 80 | y=plumed.getCollectiveVariable(0, time_series=True), 81 | ) 82 | BSS.Notebook.plot( 83 | x=plumed.getTime(time_series=True), 84 | y=plumed.getCollectiveVariable(1, time_series=True), 85 | ) 86 | ``` 87 | 88 | ### Part 3.2 - 2D free energy surface 89 | 90 | Now let's have a look at the free energy surfaces. Here we plot the 2D surface. 91 | 92 | The 0th index in the `free_nrg` matrix contains the projection values, 1st index - the extent values and the 2nd index has the free energy values, in kJ/mol. 93 | 94 | 95 | ```python 96 | free_nrg = plumed.getFreeEnergy() 97 | BSS.Notebook.plotContour( 98 | free_nrg[0], free_nrg[1], free_nrg[2], xlabel="proj (nm)", ylabel="ext (nm)" 99 | ) 100 | ``` 101 | 102 | ### Part 3.3 - 1D FES 103 | 104 | We can integrate the FE values for each CV. The smoothness of the FES in the solvent phase (last 0.5 nm of projection CV) is another indicator of convergence. 105 | 106 | Note - the extent CV shouldn't have any free energy below 0.0 nm, but because the bias potential is Gaussian-shaped, the Gaussian 'tails' overspill to non-real values. 107 | 108 | 109 | ```python 110 | # the projection CV vs FE 111 | free_nrg0 = plumed.getFreeEnergy(0, kt=2.49 * BSS.Units.Energy.kt) 112 | BSS.Notebook.plot(free_nrg0[0], free_nrg0[1]) 113 | 114 | # the extent CV vs FE 115 | free_nrg1 = plumed.getFreeEnergy(1, kt=2.49 * BSS.Units.Energy.kt) 116 | BSS.Notebook.plot(free_nrg1[0], free_nrg1[1]) 117 | ``` 118 | 119 | ### Part 3.4 - Absolute binding free energy 120 | 121 | As I mentioned in the beginning of this tutorial, to recover the ABFE, we need to find the difference in free energy between the bound and the unbound/solvent phases. We will use a 1D FES along the projection CV and will add the funnel correction. 122 | 123 | 124 | ```python 125 | funnel_correction = cv.getCorrection().kcal_per_mol() 126 | 127 | # the last 0.5 nm of the projection CV are between indices -75 to -25 128 | free_nrg_floats = [i.kj_per_mol().value() for i in free_nrg0[1][-75:-25]] 129 | 130 | estimate = -(np.mean(free_nrg_floats) / 4.184 + funnel_correction.value()) 131 | 132 | print(f"the funnel correction = {funnel_correction}") 133 | print(f"the ABFE estimate = {estimate:.2f} kcal/mol") 134 | ``` 135 | 136 | ### Part 3.5 - $\Delta$G vs time 137 | 138 | Finally, we need to look at the time evolution of the free energy estimate. We can get the 1D FES every 1 ns, and since we recorded the CV every 2 ps, we select a 500 step stride. 139 | 140 | 141 | ```python 142 | free_nrg_series = plumed.getFreeEnergy(0, stride=500) 143 | ``` 144 | 145 | Now let's find the estimate at every 1 ns of the simulation. 146 | 147 | 148 | ```python 149 | funnel_correction = cv.getCorrection().kcal_per_mol() 150 | 151 | estimates_list = [] 152 | for item in range(np.shape(free_nrg_series)[0]): 153 | free_nrg_floats = [i.value() for i in free_nrg_series[item][1][-75:-25]] 154 | estimate = -(np.mean(free_nrg_floats) / 4.184 + funnel_correction.value()) 155 | estimates_list.append(estimate) 156 | ``` 157 | 158 | One important aspect of fun-metaD, and all metadynamics simulations, is achieving convergence. If the fun-metaD simulation has fully converged i.e. the free energy surface has been fully explored, then the ABFE estimate should plateau at a certain value. 159 | 160 | 161 | ```python 162 | BSS.Notebook.plot(estimates_list) 163 | ``` 164 | 165 | ### Part 3.6 - Multiple repeats 166 | 167 | We can see from the plot above that the free energy estimate still fluctuates quite a lot. The estimate never completely plateaus, just oscillates within a 1 kcal/mol range. 168 | 169 | In order to get a good error estimate, you should do multiple independent simulations. I've provided the data for 5 repeats. Let's plot them all together, while also estimating the mean of the 5 repeats at each timepoint. 170 | 171 | 172 | ```python 173 | funnel_correction = cv.getCorrection().kcal_per_mol() 174 | 175 | plt.title("$\Delta$G vs time") 176 | plt.xlabel("time(ns)") 177 | plt.ylabel("$\Delta$G (kcal/mol)") 178 | all_estimates = [] 179 | 180 | for run_index in range(0, 5): 181 | plumed = BSS.Process.Plumed(work_dir=f"inputs_02/{run_index}_run/fun-metaD/") 182 | config = plumed.createConfig(system, protocol) 183 | 184 | free_nrg_series = plumed.getFreeEnergy(0, stride=500) 185 | 186 | estimates_list = [] 187 | for item in range(np.shape(free_nrg_series)[0]): 188 | free_nrg_floats = [i.value() for i in free_nrg_series[item][1][-75:-25]] 189 | estimate = -(np.mean(free_nrg_floats) / 4.184 + funnel_correction.value()) 190 | estimates_list.append(estimate) 191 | 192 | plt.plot(estimates_list) 193 | all_estimates.append(estimates_list) 194 | 195 | all_estimates = np.array(all_estimates) 196 | mean_estimates = [] 197 | for i in range(len(all_estimates[0])): 198 | mean_estimate = np.mean(all_estimates[:, i]) 199 | mean_estimates.append(mean_estimate) 200 | plt.plot(mean_estimates, linewidth=3, color="black", label="The mean") 201 | exp_affinity = -4.7 202 | plt.axhline(y=exp_affinity, color="red", linestyle="--") 203 | plt.legend() 204 | plt.show() 205 | ``` 206 | 207 | ## Closing thoughts 208 | 209 | You can see that each fun-metaD simulation explores the free energy surface very differently. The ABFE estimates diverge immediately and fluctuate wildly. However, the mean of the estimates is quite stable. This indicates that, while each fun-metaD simulation is exploring a different part of the FES at any given point, the full free energy surface is explored quickly. 210 | -------------------------------------------------------------------------------- /02_funnel_metad/README.md: -------------------------------------------------------------------------------- 1 | # Funnel metadynamics 2 | 3 | Author: Dominykas Lukauskis 4 | 5 | Email: dominykas.lukauskis.19@ucl.ac.uk 6 | 7 | Requirements: 8 | * BioSimSpace 9 | * OpenMM or Gromacs/Amber patched with PLUMED 10 | 11 | This workshop tutorial walks you through how to setup funnel metadynamics (fun-metaD) simulations to estimate the absolute binding free energy (ABFE), how to visualise the funnel restraints and how to analyse the simulations using [BioSimSpace](https://biosimspace.org/index.html). 12 | 13 | The workshop is divided into two notebooks. 14 | 15 | ## 1. Simulation setup and restraint visualisation 16 | For a step-by-step guide on how to visualise the funnel restraints and how to set up the fun-metaD simulations, follow [this Jupyter NB](01_bss-fun-metad-setup.ipynb). 17 | 18 | ## 2. Analysis 19 | For a step-by-step guide on how to analyse fun-metaD simulations go [here](02_bss-fun-metad-analysis.ipynb). This notebook includes a discussion on how to think about convergence in fun-metaD simulations and how to get a robust ABFE estimate. 20 | -------------------------------------------------------------------------------- /02_funnel_metad/example_nodes/equilibrate_no_trj.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # coding: utf-8 3 | 4 | # Author: Dominykas Lukauskis
5 | # Email:   dominykas.lukauskis.19@ucl.ac.uk 6 | # 7 | # # Equilibration 8 | # 9 | # A node to perform equilibration of a molecular system. Saves the equilibration system to AMBER format files along with a DCD trajectory. 10 | 11 | # In[ ]: 12 | 13 | 14 | import BioSimSpace as BSS 15 | import os 16 | 17 | # set this just in case its missing in the env 18 | os.environ["CUDA_VISIBLE_DEVICES"] = "0" 19 | 20 | # In[ ]: 21 | 22 | 23 | node = BSS.Gateway.Node( 24 | "A node to perform equilibration. Saves the equlibrated molecular configuration to file." 25 | ) 26 | node.addAuthor( 27 | name="Dominykas Lukauskis", 28 | email="dominykas.lukauskis.19@ucl.ac.uk", 29 | affiliation="University College London", 30 | ) 31 | node.setLicense("GPLv3") 32 | 33 | 34 | # Set the input requirements: 35 | 36 | # In[ ]: 37 | 38 | 39 | node.addInput("files", BSS.Gateway.FileSet(help="A set of molecular input files.")) 40 | 41 | node.addInput( 42 | "engine", 43 | BSS.Gateway.String( 44 | help="The molecular dynamics engine.", 45 | allowed=BSS.MD.engines(), 46 | default="OpenMM", 47 | ), 48 | ) 49 | 50 | node.addInput( 51 | "runtime", 52 | BSS.Gateway.Time( 53 | help="The run time.", 54 | unit="nanoseconds", 55 | minimum=0.02 * BSS.Units.Time.nanosecond, 56 | maximum=10 * BSS.Units.Time.nanosecond, 57 | default=2 * BSS.Units.Time.nanosecond, 58 | ), 59 | ) 60 | 61 | node.addInput( 62 | "temperature_start", 63 | BSS.Gateway.Temperature( 64 | help="The initial temperature.", 65 | unit="kelvin", 66 | minimum=0 * BSS.Units.Temperature.kelvin, 67 | maximum=1000 * BSS.Units.Temperature.kelvin, 68 | default=0 * BSS.Units.Temperature.kelvin, 69 | ), 70 | ) 71 | 72 | node.addInput( 73 | "temperature_end", 74 | BSS.Gateway.Temperature( 75 | help="The final temperature.", 76 | unit="kelvin", 77 | minimum=0 * BSS.Units.Temperature.kelvin, 78 | maximum=1000 * BSS.Units.Temperature.kelvin, 79 | default=300 * BSS.Units.Temperature.kelvin, 80 | ), 81 | ) 82 | 83 | node.addInput( 84 | "restraint", 85 | BSS.Gateway.String( 86 | help="The type of restraint.", 87 | allowed=BSS.Protocol.Equilibration.restraints(), 88 | default="none", 89 | ), 90 | ) 91 | 92 | # We now need to define the output of the node. In this case we will return a set of files representing the equilibrated molecular system in AMBER format and a single file containing the trajectory frames. 93 | 94 | # In[ ]: 95 | 96 | 97 | node.addOutput( 98 | "equilibrated", BSS.Gateway.FileSet(help="The equilibrated molecular system.") 99 | ) 100 | 101 | 102 | # In[ ]: 103 | 104 | 105 | node.showControls() 106 | 107 | 108 | # Generate the molecular system. 109 | 110 | # In[ ]: 111 | 112 | 113 | system = BSS.IO.readMolecules(node.getInput("files")) 114 | 115 | 116 | # Set up the equilibration protocol. 117 | # 118 | # (Note that the keyword arguments happen to have the same name as the input requirements. This need not be the case.) 119 | 120 | # In[ ]: 121 | 122 | 123 | protocol = BSS.Protocol.Equilibration( 124 | runtime=node.getInput("runtime"), 125 | temperature_start=node.getInput("temperature_start"), 126 | temperature_end=node.getInput("temperature_end"), 127 | restraint=node.getInput("restraint"), 128 | ) 129 | 130 | 131 | # Start the molecular dynamics process. 132 | 133 | # In[ ]: 134 | 135 | engine = node.getInput("engine") 136 | 137 | if engine == "OpenMM": 138 | process = BSS.Process.OpenMM(system, protocol, platform="CUDA") 139 | elif engine == "Amber": 140 | process = BSS.Process.Amber( 141 | system, protocol, exe="/home/model/MD-SOFTWARE/amber18-gnu-cu10/bin/pmemd.cuda" 142 | ) 143 | elif engine == "Gromacs": 144 | process = BSS.Process.Gromacs(system, protocol) 145 | 146 | process.start() 147 | 148 | 149 | # We now wait for the process to finish, then check whether there were any errors before continuing. If errors were raised, then we raise an exception and print the last 10 lines of `stdout` and `stderr` to the user. 150 | 151 | # In[ ]: 152 | 153 | 154 | process.wait() 155 | 156 | if process.isError(): 157 | print(process.stdout(10)) 158 | print(process.stdout(10)) 159 | raise RuntimeError("The process exited with an error!") 160 | 161 | 162 | # Get the equilibrated molecular system and write to file in the same format as the input, along with a PDB file to use as a topology file for the trajectory in [VMD](https://www.ks.uiuc.edu/Research/vmd/). 163 | 164 | # In[ ]: 165 | 166 | 167 | # Get the original file formats associated with the system. 168 | # This is returned as a comma-separated string. 169 | formats = system.fileFormat() 170 | 171 | # Append PDB format, so we can visualise the trajectory with VMD. 172 | formats += ",pdb" 173 | 174 | # Write to file and bind to the output. 175 | node.setOutput( 176 | "equilibrated", BSS.IO.saveMolecules("equilibrated", process.getSystem(), formats) 177 | ) 178 | 179 | 180 | # Validate the node. 181 | 182 | # In[ ]: 183 | 184 | 185 | node.validate() 186 | -------------------------------------------------------------------------------- /02_funnel_metad/example_nodes/fun_metad.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # coding: utf-8 3 | 4 | # # Funnel metadynamics 5 | # 6 | # This is a node to perform a funnel metadynamics simulation on a solvated protein-ligand complex. 7 | 8 | # In[ ]: 9 | 10 | 11 | import BioSimSpace as BSS 12 | 13 | 14 | # Initialise the node and metadata. 15 | 16 | # In[ ]: 17 | 18 | 19 | node = BSS.Gateway.Node( 20 | "Perform funnel metadynamics on a solvated protein-ligand complex." 21 | ) 22 | node.addAuthor(name="Dominykas Lukauskis", email="dominykas.lukauskis.19@ucl.ac.uk") 23 | 24 | 25 | # Add the inputs to the node. 26 | 27 | # In[ ]: 28 | 29 | 30 | node.addInput("files", BSS.Gateway.FileSet(help="A set of molecular input files.")) 31 | 32 | node.addInput( 33 | "runtime", 34 | BSS.Gateway.Time( 35 | help="The run time.", 36 | unit="nanoseconds", 37 | minimum=5 * BSS.Units.Time.nanosecond, 38 | maximum=2000 * BSS.Units.Time.nanosecond, 39 | default=1000 * BSS.Units.Time.nanosecond, 40 | ), 41 | ) 42 | 43 | node.addInput( 44 | "hill_height", 45 | BSS.Gateway.Energy( 46 | help="The hill height.", 47 | unit="kj per mol", 48 | minimum=1 * BSS.Units.Energy.kj_per_mol, 49 | maximum=10 * BSS.Units.Energy.kj_per_mol, 50 | default=1.5 * BSS.Units.Energy.kj_per_mol, 51 | ), 52 | ) 53 | 54 | node.addInput( 55 | "bias_factor", 56 | BSS.Gateway.Float( 57 | help="The bias factor for well-tempered metadynamics.", 58 | minimum=1.0, 59 | maximum=100.0, 60 | default=10.0, 61 | ), 62 | ) 63 | 64 | node.addInput( 65 | "engine", 66 | BSS.Gateway.String( 67 | help="The molecular dynamics engine.", 68 | allowed=BSS.Metadynamics.engines(), 69 | default="OpenMM", 70 | ), 71 | ) 72 | 73 | node.addInput( 74 | "work_dir", 75 | BSS.Gateway.String( 76 | help="The working directory for the simulation.", default="fun_metad" 77 | ), 78 | ) 79 | 80 | 81 | # Add the outputs of the node. 82 | 83 | # In[ ]: 84 | 85 | 86 | node.addOutput( 87 | "final", 88 | BSS.Gateway.FileSet( 89 | help="The final system, in the original format plus a PDB file." 90 | ), 91 | ) 92 | 93 | 94 | # Show the GUI so the user can set inputs interactively. 95 | 96 | # In[ ]: 97 | 98 | 99 | node.showControls() 100 | 101 | 102 | # Create the system from the user specified input files. 103 | 104 | # In[ ]: 105 | 106 | 107 | system = BSS.IO.readMolecules(node.getInput("files")) 108 | 109 | 110 | # Work out "best guess" funnel parameters based on the molecules in the system. 111 | 112 | # In[ ]: 113 | 114 | 115 | p0, p1 = BSS.Metadynamics.CollectiveVariable.makeFunnel(system) 116 | 117 | 118 | # Create the funnel collective variable based on the funnel parameters. 119 | 120 | # In[ ]: 121 | 122 | 123 | cv = BSS.Metadynamics.CollectiveVariable.Funnel( 124 | p0, p1, upper_bound=BSS.Metadynamics.Bound(value=3.5 * BSS.Units.Length.nanometer) 125 | ) 126 | 127 | 128 | # Create the metadynamics protocol, passing the collective variable and user-defined configuration parameters. 129 | 130 | # In[ ]: 131 | 132 | 133 | protocol = BSS.Protocol.Metadynamics( 134 | cv, 135 | runtime=node.getInput("runtime"), 136 | hill_height=node.getInput("hill_height"), 137 | bias_factor=node.getInput("bias_factor"), 138 | restart_interval=500000, 139 | ) 140 | 141 | 142 | # Create a simulation process using the user-defined molecular-dynamics engine. This will automatically start the process. 143 | 144 | # In[ ]: 145 | 146 | 147 | engine = node.getInput("engine") 148 | work_dir = node.getInput("work_dir") 149 | 150 | if engine == "OpenMM": 151 | process = BSS.Process.OpenMM(system, protocol, platform="CUDA", work_dir=work_dir) 152 | elif engine == "Amber": 153 | process = BSS.Process.Amber( 154 | system, 155 | protocol, 156 | exe="/home/model/MD-SOFTWARE/amber18-gnu-cu10/bin/pmemd.cuda", 157 | work_dir=work_dir, 158 | ) 159 | elif engine == "Gromacs": 160 | process = BSS.Process.Gromacs(system, protocol, work_dir=work_dir) 161 | 162 | process.start() 163 | 164 | 165 | # Wait for the process to finish and check for any errors. 166 | 167 | # In[ ]: 168 | 169 | 170 | process.wait() 171 | 172 | # Print the last 10 lines of stdout and stderr if there was an error. 173 | if process.isError(): 174 | print(process.stdout(10)) 175 | print(process.stderr(10)) 176 | raise RuntimeError("The funnel metadynamics process exited with an error!") 177 | 178 | 179 | # Get the system from the process, save to file, and bind to the output requirement. 180 | 181 | # In[ ]: 182 | 183 | 184 | final_system = process.getSystem() 185 | formats = system.fileFormat() + ",pdb" 186 | node.setOutput("final", BSS.IO.saveMolecules("final", final_system, formats)) 187 | 188 | 189 | # Validate the node. 190 | 191 | # In[ ]: 192 | 193 | 194 | node.validate() 195 | -------------------------------------------------------------------------------- /02_funnel_metad/example_nodes/minimise.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # coding: utf-8 3 | 4 | # Author: Lester Hedges
5 | # Email:   lester.hedges@bristol.ac.uk 6 | # 7 | # # Minimisation 8 | # 9 | # In this notebook you'll learn how to use BioSimSpace to write a robust and interoperable workflow node to perform energy minimisation on a molecular system. 10 | # 11 | # First we'll need to import BioSimSpace: 12 | 13 | # In[ ]: 14 | 15 | 16 | import BioSimSpace as BSS 17 | 18 | 19 | # We begin by creating a `Node` object. This is the core of our molecular workflow component. It defines what it does, what input is needed, and the output that is produced. 20 | 21 | # In[ ]: 22 | 23 | 24 | node = BSS.Gateway.Node( 25 | "A node to perform energy minimisation and save the minimised molecular configuration to file." 26 | ) 27 | 28 | 29 | # We'll now set the author and license: 30 | 31 | # In[ ]: 32 | 33 | 34 | node.addAuthor( 35 | name="Lester Hedges", 36 | email="lester.hedges@bristol.ac.uk", 37 | affiliation="University of Bristol", 38 | ) 39 | node.setLicense("GPLv3") 40 | 41 | 42 | # Nodes require inputs. To specify inputs we use the `Gateway` module, which is used as a bridge between BioSimSpace and the outside world. This will allow us to document the inputs, define their type, and specify any constraints on their allowed values. Here we will need a set of files that define the molecular system, and an integer that indicates the number of minimisation steps to perform. 43 | 44 | # In[ ]: 45 | 46 | 47 | node.addInput("files", BSS.Gateway.FileSet(help="A set of molecular input files.")) 48 | 49 | node.addInput( 50 | "steps", 51 | BSS.Gateway.Integer( 52 | help="The number of minimisation steps.", 53 | minimum=0, 54 | maximum=1000000, 55 | default=10000, 56 | ), 57 | ) 58 | 59 | 60 | # Note that the input requirement `steps` has a default value, so it is optional. 61 | # 62 | # We now need to define the output of the node. In this case we will return a set of files representing the minimised molecular system. 63 | 64 | # In[ ]: 65 | 66 | 67 | node.addOutput("minimised", BSS.Gateway.FileSet(help="The minimised molecular system.")) 68 | 69 | 70 | # When working interactively within a Jupyter notebook we need a way to allow users to set the input requirements. The `node.showControls` method will display a graphical user interface (GUI), from which inputs can be set. 71 | # 72 | # Note that the GUI requires active user input. All input requirements that don't have a default value _must_ be set before the node can proceed. If you try to query the node for one of the user values then an error will be raised. For bounded integer inputs you can use a slider to set the value, or type in the input box and press enter. 73 | # 74 | # When working interactively you will typically be running on a remote server where you won't have access to the local filesystem. In this case you'll need to upload files for any of the `File` or `FileSet` input requirements. The GUI below will provide buttons that allow you to browse your own filesystem and select files. Since Jupyter has a limit of 5MB for file transfers, we provide support for compressed formats, such as `.zip` or `.tar.gz`. (A single archive can contain a set of files, allowing you to set a single value for a `FileSet` requirement.) We've provided some example input files that can be used in the training notebooks, which are available to download from the links below. These can then be re-uploaded using the GUI. 75 | # 76 | # AMBER: [ala.crd](https://raw.githubusercontent.com/michellab/BioSimSpace/devel/demo/amber/ala/ala.crd), [ala.top](https://raw.githubusercontent.com/michellab/BioSimSpace/devel/demo/amber/ala/ala.top) 77 | # 78 | # GROMACS: [kigaki.gro](https://raw.githubusercontent.com/michellab/BioSimSpace/devel/demo/gromacs/kigaki/kigaki.gro), [kigaki.top](https://raw.githubusercontent.com/michellab/BioSimSpace/devel/demo/gromacs/kigaki/kigaki.top) 79 | # 80 | # NAMD: [alanin.pdb](https://raw.githubusercontent.com/michellab/BioSimSpace/devel/demo/namd/alanin/alanin.pdb), [alanin.psf](https://raw.githubusercontent.com/michellab/BioSimSpace/devel/demo/namd/alanin/alanin.psf), [alanin.params](https://raw.githubusercontent.com/michellab/BioSimSpace/devel/demo/namd/alanin/alanin.params) 81 | # 82 | # 83 | # When uploading files the name of the current file(s) will replace the `Upload` button. If you need to change the file, simply click on the button again and choose a new file. 84 | 85 | # In[ ]: 86 | 87 | 88 | node.showControls() 89 | 90 | 91 | # Once all requirements are set then we can acces the values using the `node.getInput` method. The first time this is called the `node` will automatically validate all of the input and report the user if any errors were found. 92 | # 93 | # We'll now create a molecular system using the input files uploaded by the user. Note that we don't specify the format of the files, since this is automatically determined by BioSimSpace. (BioSimSpace has support for a wide range of formats and can convert between certain formats too.) 94 | 95 | # In[ ]: 96 | 97 | 98 | system = BSS.IO.readMolecules(node.getInput("files")) 99 | 100 | 101 | # In order to run a minimisation we need to define a protocol. This can be done using the `BioSimSpace.Protocol` module. Here we will create a "best practice" minimisation protocol, overriding the number of steps with the input from the user. 102 | 103 | # In[ ]: 104 | 105 | 106 | protocol = BSS.Protocol.Minimisation(steps=node.getInput("steps")) 107 | 108 | 109 | # We now have everything that is required to run a minimisation. To do so, we use `BioSimSpace.MD` to find an appropriate molecular dynamics package on our current environment. What package is found will depend upon both the system and protocol, as well as the hardware that is available to the user. 110 | 111 | # In[ ]: 112 | 113 | 114 | process = BSS.MD.run(system, protocol, engine="OpenMM", auto_start=False) 115 | 116 | process.start() 117 | 118 | 119 | # We now wait for the process to finish, then check whether there were any errors before continuing. If errors were raised, then we raise an exception and print the last 10 lines of `stdout` and `stderr` to the user. 120 | 121 | # In[ ]: 122 | 123 | 124 | process.wait() 125 | 126 | if process.isError(): 127 | print(process.stdout(10)) 128 | print(process.stdout(10)) 129 | raise RuntimeError("The process exited with an error!") 130 | 131 | 132 | # When the process has finished running we can get the minimised molecular configuration. We will save this to file using the same format as the original system, and set the output requirement to the list of file names that were written. 133 | 134 | # In[ ]: 135 | 136 | 137 | node.setOutput( 138 | "minimised", 139 | BSS.IO.saveMolecules("minimised", process.getSystem(), system.fileFormat()), 140 | ) 141 | 142 | 143 | # Finally, we validate that the node completed succesfully. This will check that all output requirements are satisfied. Any file outputs will be available for the user to download as a compressed archive. 144 | # 145 | # Note that the validation will fail until the cell above finishes running. 146 | 147 | # In[ ]: 148 | 149 | 150 | node.validate() 151 | 152 | 153 | # Once we are satisfied with our node we can choosed to download it as a regular Python script that can be run from the command-line. 154 | # 155 | # In JupyterHub, click on: `File/Download As/Python`\ 156 | # In JupyterLab, click on: `File/Export Notebook As/Export Notebook to Executable Script` 157 | -------------------------------------------------------------------------------- /02_funnel_metad/example_nodes/parameterise_solvate.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # coding: utf-8 3 | 4 | # Author: Dominykas Lukaukis
5 | # Email:   dominykas.lukauskis.19@ucl.ac.uk 6 | # 7 | # # Parameterisation 8 | # 9 | # A node to perform parameterisation of a molecule loaded from PDB file. Saves the parameterised molecule in to AMBER format files. 10 | 11 | # In[ ]: 12 | 13 | 14 | import BioSimSpace as BSS 15 | 16 | 17 | # In[ ]: 18 | 19 | 20 | node = BSS.Gateway.Node( 21 | "A node to perform parameterisation and solvation of protein and ligand molecules loaded from PDB/MOL2 files. Saves the parameterised and solvated system to AMBER format files." 22 | ) 23 | node.addAuthor( 24 | name="Dominykas Lukauskis", 25 | email="dominykas.lukauskis.19@ucl.ac.uk", 26 | affiliation="University College London", 27 | ) 28 | node.setLicense("GPLv3") 29 | 30 | 31 | # Set the input requirements: 32 | 33 | # In[ ]: 34 | 35 | 36 | node.addInput( 37 | "protein_pdb", 38 | BSS.Gateway.File( 39 | help="A Protein Data Bank (PDB) file containing a single molecule." 40 | ), 41 | ) 42 | 43 | node.addInput( 44 | "ligand_file", 45 | BSS.Gateway.File( 46 | help="A Protein Data Bank (PDB) or Tripos MOL2 file containing a single organic molecule." 47 | ), 48 | ) 49 | 50 | node.addInput( 51 | "protein_forcefield", 52 | BSS.Gateway.String( 53 | help="The protein force field to parameterise the molecule with.", 54 | allowed=BSS.Parameters.forceFields(), 55 | default="ff14SB", 56 | ), 57 | ) 58 | 59 | node.addInput( 60 | "ligand_forcefield", 61 | BSS.Gateway.String( 62 | help="The ligand force field to parameterise the molecule with.", 63 | allowed=BSS.Parameters.forceFields(), 64 | default="gaff2", 65 | ), 66 | ) 67 | 68 | node.addInput( 69 | "water_model", 70 | BSS.Gateway.String( 71 | help="The water model to use for ion parameters.", 72 | allowed=BSS.Solvent.waterModels(), 73 | default="tip3p", 74 | ), 75 | ) 76 | 77 | node.addInput( 78 | "pdb4amber", 79 | BSS.Gateway.Boolean( 80 | help="Whether to pre-process the protein PDB file using pdb4amber.", 81 | default=False, 82 | ), 83 | ) 84 | 85 | node.addInput( 86 | "box", 87 | BSS.Gateway.String( 88 | help="The type of simulation box.", allowed=BSS.Box.boxTypes(), default="cubic" 89 | ), 90 | ) 91 | 92 | node.addInput( 93 | "neutralise", 94 | BSS.Gateway.Boolean( 95 | help="Whether to neutralise the system. Ions will be added on top of any specified with 'ion_conc'", 96 | default=True, 97 | ), 98 | ) 99 | 100 | node.addInput( 101 | "ion_conc", 102 | BSS.Gateway.Float(help="The ion concentration in mol per litre", default=0), 103 | ) 104 | 105 | 106 | # We now need to define the output of the node. In this case we will return a set of files representing the parameterised molecule in AMBER format. 107 | 108 | # In[ ]: 109 | 110 | 111 | node.addOutput( 112 | "solvated", BSS.Gateway.FileSet(help="The parameterised and solvated system.") 113 | ) 114 | 115 | 116 | # In[ ]: 117 | 118 | 119 | node.showControls() 120 | 121 | 122 | # Load the PDB file and pre-process with `pdb4amber` if requested. Since we assume a single molecule PDB, take the first molecule in the file. 123 | 124 | # In[ ]: 125 | 126 | 127 | protein = BSS.IO.readPDB( 128 | node.getInput("protein_pdb"), pdb4amber=node.getInput("pdb4amber") 129 | )[0] 130 | 131 | 132 | # Perform the parameterisation using the chosen force field and ion water model. Note that we call the generic `BSS.Parameters.parameterise` function so that we can pass the force field name as an argument. 133 | 134 | # In[ ]: 135 | 136 | 137 | protein = BSS.Parameters.parameterise( 138 | protein, 139 | node.getInput("protein_forcefield"), 140 | water_model=node.getInput("water_model"), 141 | ).getMolecule() 142 | 143 | # Load the PDB file and pre-process with `pdb4amber` if requested. Since we assume a single molecule PDB, take the first molecule in the file. 144 | 145 | # In[ ]: 146 | 147 | 148 | ligand = BSS.IO.readPDB(node.getInput("ligand_file"))[0] 149 | 150 | 151 | # Perform the parameterisation using the chosen force field and ion water model. Note that we call the generic `BSS.Parameters.parameterise` function so that we can pass the force field name as an argument. 152 | 153 | # In[ ]: 154 | 155 | ############ net charge assignment? ############## 156 | ligand = BSS.Parameters.parameterise( 157 | ligand, 158 | node.getInput("ligand_forcefield"), 159 | ).getMolecule() 160 | 161 | # Combine the protein and the ligand 162 | 163 | cmplx = protein + ligand 164 | 165 | # Now work out the minimum box size for the molecules in the system. 166 | 167 | # In[ ]: 168 | 169 | 170 | # Get the minimium and maximum coordinates of the bounding box that 171 | # minimally encloses the protein. 172 | box_min, box_max = cmplx.getAxisAlignedBoundingBox() 173 | 174 | # Work out the box size from the difference in the coordinates. 175 | box_size = [y - x for x, y in zip(box_min, box_max)] 176 | 177 | # How much to pad each side of the molecule? (Nonbonded cutoff = 10 A) 178 | padding = 15 * BSS.Units.Length.angstrom 179 | 180 | # Work out an appropriate box. This will used in each dimension to ensure 181 | # that the cutoff constraints are satisfied if the molecule rotates. 182 | box_length = max(box_size) + 2 * padding 183 | 184 | 185 | # Get the box parameters for the chosen box type. 186 | 187 | # In[ ]: 188 | 189 | 190 | box, angles = BSS.Box.generateBoxParameters(node.getInput("box"), box_length) 191 | 192 | 193 | # Now solvate with the chosen water model and ion concentration. 194 | 195 | # In[ ]: 196 | 197 | 198 | solvated = BSS.Solvent.solvate( 199 | node.getInput("water_model"), 200 | molecule=cmplx, 201 | box=box, 202 | angles=angles, 203 | is_neutral=node.getInput("neutralise"), 204 | ion_conc=node.getInput("ion_conc"), 205 | ) 206 | 207 | 208 | # Write the solvated system to file in PDB and Amber format. 209 | 210 | # In[ ]: 211 | 212 | 213 | node.setOutput( 214 | "solvated", BSS.IO.saveMolecules("solvated", solvated, ["PDB", "RST7", "PRM7"]) 215 | ) 216 | 217 | 218 | # Validate the node. 219 | 220 | # In[ ]: 221 | 222 | 223 | node.validate() 224 | -------------------------------------------------------------------------------- /02_funnel_metad/example_nodes/submit.sub: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # The #BSUB lines are interpreted by LSF 3 | # ------------------------------------- 4 | # LSF job name and queue 5 | #BSUB -J bss_fun-metaD 6 | #BSUB -q phase12_slough 7 | # Output log files 8 | #BSUB -o output.%J 9 | #BSUB -e errors.%J 10 | # Numbers of CPUs 11 | #BSUB -n 1 12 | # Resources 13 | #BSUB -R "rusage[mem=1] span[ptile=8] select[hname!=slhgridap073]" 14 | # GPU resources 15 | #BSUB -gpu "num=1:j_exclusive=yes:mode=exclusive_process" 16 | 17 | source /home/model/MD-SOFTWARE/BSSenv.bashrc 18 | 19 | # set the I/Os 20 | python parameterise_solvate.py --protein_pdb protein.pdb --ligand_file ligand.mol2 21 | 22 | python minimise.py --files solvated.prm7 solvated.rst7 23 | 24 | python equilibrate_no_trj.py --files minimised.rst7 minimised.prm7 25 | 26 | python fun_metad.py --files equilibrated.rst7 equilibrated.prm7 27 | -------------------------------------------------------------------------------- /02_funnel_metad/figures/figure1.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenBioSim/biosimspace_tutorials/ac200df769ba3373c9e0e724020aaaa96e340889/02_funnel_metad/figures/figure1.jpeg -------------------------------------------------------------------------------- /02_funnel_metad/figures/figure2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenBioSim/biosimspace_tutorials/ac200df769ba3373c9e0e724020aaaa96e340889/02_funnel_metad/figures/figure2.png -------------------------------------------------------------------------------- /02_funnel_metad/figures/fun-hsp90.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenBioSim/biosimspace_tutorials/ac200df769ba3373c9e0e724020aaaa96e340889/02_funnel_metad/figures/fun-hsp90.png -------------------------------------------------------------------------------- /02_funnel_metad/get_tutorial.py: -------------------------------------------------------------------------------- 1 | import requests, os 2 | 3 | links = { 4 | "01": ( 5 | "inputs_01.tar.bz2", 6 | "https://openbiosim-my.sharepoint.com/:u:/g/personal/director_openbiosim_org/Ed-8fipPSEFMt01VyyZQBBoBpviQb2k5pmzFsdBulaXXUQ?download=1", 7 | ), 8 | "02": ( 9 | "inputs_02.tar.bz2", 10 | "https://openbiosim-my.sharepoint.com/:u:/g/personal/director_openbiosim_org/ES8xuCuWe4dNop5fJeH1opEBoKG0qZZqLph5ZwMadKkJoQ?download=1", 11 | ), 12 | } 13 | 14 | 15 | def download(link): 16 | localfile, url = links[link] 17 | # Do not download if tarball already found 18 | if os.path.isfile(localfile): 19 | return 20 | print("Downloading %s from openbiosim.org ..." % localfile) 21 | req = requests.get(url, stream=True) 22 | with open(localfile, "wb") as f: 23 | for chunk in req.iter_content(chunk_size=1024): 24 | if chunk: 25 | f.write(chunk) 26 | f.flush() 27 | print("Extracting compressed tarball ...") 28 | os.system("tar -xf %s" % localfile) 29 | # os.system("rm %s" % localfile) 30 | -------------------------------------------------------------------------------- /03_steered_md/02_trajectory_analysis.md: -------------------------------------------------------------------------------- 1 | # Steered MD in BioSimSpace 2 | 3 | This notebook covers analysing sMD trajectories after the run has finished. 4 | 5 | **Reading Time:** 6 | ~ 15 mins 7 | 8 | ### Maintainers 9 | - Adele Hardie -- adele.hardie@ed.ac.uk (@AdeleHardie) 10 | 11 | ### Prerequisites 12 | 13 | - Basic Python and bash 14 | - A completed sMD run 15 | 16 | ### Learning Objectives 17 | 18 | - Analyse the steered MD trajectory 19 | 20 | ### Table of Contents 21 | 1. [Introduction](#intro) 22 | 2. [Plot steering output](#plotting)
23 | 2.1 [Successful steering](#success)
24 | 2.2 [Failed steering](#failed) 25 | 3. [Extract snapshots](#snapshots) 26 | 4. [Example application - Markov State Modelling](#msm) 27 | 28 | 29 | ### Further reading for this topic 30 | - [Applying sMD to study allosteric modulation](https://www.nature.com/articles/s42004-023-00926-1) 31 | 32 | **Jupyter Cheat Sheet** 33 | - To run the currently highlighted cell and move focus to the next cell, hold ⇧ Shift and press ⏎ Enter; 34 | - To run the currently highlighted cell and keep focus in the same cell, hold ⇧ Ctrl and press ⏎ Enter; 35 | - To get help for a specific function, place the cursor within the function's brackets, hold ⇧ Shift, and press ⇥ Tab; 36 | 37 | ### Link to documentation: 38 | You can find the full documentation at [biosimspace.org](https://biosimspace.org). 39 | 40 | ## 1. Introduction 41 |
42 | 43 | The purpose of the steered MD simulation is to access conformational space that would take a very long time (or be inaccessible altogether) at equilibrium. We can check whether the desired conformational change occured by analysing the output CV data. 44 | 45 | Let's download the inputs for the tutorial if necessary 46 | 47 | 48 | ```python 49 | from get_tutorial import download 50 | download("01") 51 | ``` 52 | 53 | Start by importing required libraries: 54 | 55 | 56 | ```python 57 | import pandas as pd 58 | import numpy as np 59 | import matplotlib.pyplot as plt 60 | import BioSimSpace as BSS 61 | import os 62 | ``` 63 | 64 | ## 2. Plot steering output 65 |
66 | 67 | ### 2.1 Successful steering 68 |
69 | 70 | PLUMED outputs a file with the CV value that was used for steering, so we can see the progression during the simulation. The file is automatically named `COLVAR` by BioSimSpace. Here it is loaded as a pandas dataframe. 71 | 72 | 73 | ```python 74 | steering_output_file = "data/COLVAR" 75 | df = pd.read_csv(steering_output_file, sep=" ") 76 | cols = list(df.columns[2:]) 77 | df = pd.read_csv(steering_output_file, sep=" ", comment="#", names=cols) 78 | ``` 79 | 80 | 81 | ```python 82 | df.head() 83 | ``` 84 | 85 | The `COLVAR` file contains all of the CV values (`r1,t1,d1`), as well as more information on the force applied and work done. 86 | 87 | PLUMED outputs time in picoseconds and RMSD in nanometers. For easier plotting, we change time to nanoseconds and distances to Angstrom. 88 | 89 | 90 | ```python 91 | df["time"] = df["time"] / 1000 92 | df["r1"] = df["r1"] * 10 93 | df["d1"] = df["d1"] * 10 94 | df.set_index("time", inplace=True) 95 | ``` 96 | 97 | Now the CV changes can be plotted: 98 | 99 | 100 | ```python 101 | fig, ax = plt.subplots(3, figsize=(12, 12)) 102 | 103 | columns = ["r1", "t1", "d1"] 104 | ylabels = ["RMSD/$\AA$", "Dihedral/radians", "Distance/$\AA$"] 105 | 106 | for i in range(len(columns)): 107 | ax[i].plot(df.index, df[columns[i]], alpha=0.7) 108 | ax[i].set_ylabel(ylabels[i]) 109 | ax[i].set_xlabel("time/ns") 110 | ax[i].set_xlim(0, 152) 111 | 112 | fig.tight_layout() 113 | ``` 114 | 115 | 116 | 117 | Here the loop RMSD went down to below 2 A (around 1.8 A). This indicates that the loop conformation was very similar to the crystal structure of PTP1B with the loop closed (which was used as the target) and so we can proceed with extracting snapshots to use as seeds. Additionally, the Tyr152 $\chi$1 angle was kept in the "down" rotamer consistently (around 1.1 radians) and the Phe196(C$\gamma$)-Phe280(C$\gamma$) distance was decreased to 4 A, which corresponds to the two residues $\pi$-stacking. 118 | 119 | ### 2.2 Failed steering 120 |
121 | 122 | However, sMD might not work on the first try - the steering duration and force constant used is highly dependent on each individual system. Below is an example of a failed WPD loop RMSD steering attempt: 123 | 124 | 125 | 126 | Here steering was carried out for 80 ns only, and the force constant used was 2500 kJ mol$^{-1}$. The RMSD was decreasing as expected, but didn't go below 2 A. This was deemed insufficient and a longer steering protocol with a larger force constant was decided upon in the end. Ultimately this will depend on the system you are working with and what the goal of the steering is. 127 | 128 | ## 3. Extract snapshots 129 |
130 | 131 | There are multiple possible applications of sMD. Here we illustrate the enhanced sampling of conformational space, to study the conformational ensemble of PTP1B and how it may be perturbed by ligands (i.e. allosteric modulators). The sMD trajectory reaches not only active and inactive states of PTP1B, but also the short-lived intermediate conformations. We use these as seeds for further MD simulations, which are then combined in a Markov State Model. This allows to model the probability of protein active states. 132 | 133 | In this case we will be extracting 100 evenly spaced snapshots to be used as starting points for the seeded MD simulations. 134 | 135 | 136 | ```python 137 | download("02") 138 | snapshot_dir = "data" 139 | if not os.path.exists(snapshot_dir): 140 | os.mkdir(snapshot_dir) 141 | ``` 142 | 143 | Get frame indices for snapshots. Note that the end point selected is not the end of the simulation, but the end of the steering part. 144 | 145 | We have provided an example steered MD trajectory in `data/steering.nc`. For ease of downloading, it has been significantly cut down, with each frame corresponding to 0.5 ns, while it is more common to save trajectories every 5-10 ps. This is reflected in dividing the duration in ns by the frame rate in ns below. 146 | 147 | 148 | ```python 149 | number_of_snapshots = 100 150 | end = 150 / 0.5 151 | frames = np.linspace(0, end, number_of_snapshots, dtype=int) 152 | ``` 153 | 154 | Check that the snapshots roughly evenly sample the CVs: 155 | 156 | 157 | ```python 158 | fig, ax = plt.subplots(3, figsize=(12, 12)) 159 | 160 | columns = ["r1", "t1", "d1"] 161 | ylabels = ["RMSD/$\AA$", "Dihedral/radians", "Distance/$\AA$"] 162 | 163 | for i in range(len(columns)): 164 | ax[i].plot(df.index[frames]*100, df.iloc[frames][columns[i]], alpha=0.7) 165 | ax[i].set_ylabel(ylabels[i]) 166 | ax[i].set_xlabel("time/ns") 167 | ax[i].set_xlim(0, 150) 168 | 169 | fig.tight_layout() 170 | ``` 171 | 172 | 173 | 174 | Save each snapshot as a restart file (note: this will take several minutes): 175 | 176 | 177 | ```python 178 | for i, index in enumerate(frames): 179 | frame = BSS.Trajectory.getFrame( 180 | trajectory="data/steering.nc", 181 | topology="data/system.prm7", 182 | index=int(index), 183 | ) 184 | BSS.IO.saveMolecules(f"{snapshot_dir}/snapshot_{i+1}", frame, "rst7") 185 | ``` 186 | 187 | These .rst7 files are to be used as starting points for 100 individual 50 ns simulations, starting with resolvation, minimisation and equilibration. This is very time consuming and best done on an HPC cluster. An [example script](scripts/seededMD.py) that can be used with an array submission is provided. 188 | 189 |
Note: Since the .rst7 files are of the original system, the same topology is reused throughout
190 | 191 | ## 4. Example application - Markov State Modelling 192 |
193 | 194 | There are multiple potential applications, such as studying membrane permeability or ligand residence time. Here we briefly highlight one application of sMD simulations enabled by BioSimSpace in the AMMo software project. AMMo ("Allostery in Markov Models") was developed to evaluate the allosteric effects of protein mutations or ligand binding by combining sMD with Markov State Models (MSMs). AMMo uses the Python library \href{http://emma-project.org/latest/}{PyEMMA} was to implement MSMs. The integration of sMD in this allosteric modulation prediction workflow is illustrated below: 195 | 196 | 197 | 198 | [Hardie *et al*](https://www.nature.com/articles/s42004-023-00926-1) report a detailed study of allosteric modulators of PTP1B using this sMD/MSM methodology with notebooks for the PTP1B case study available on [GitHub](https://github.com/michellab/AMMo/tree/main/examples/PTP1B). 199 | -------------------------------------------------------------------------------- /03_steered_md/README.md: -------------------------------------------------------------------------------- 1 | # Steered molecular dynamics 2 | 3 | Author: Adele Hardie 4 | 5 | Email: adele.hardie@ed.ac.uk 6 | 7 | #### Requirements: 8 | * BioSimSpace 9 | * AMBER or GROMACS compiled with PLUMED 10 | * An equilibrated starting system 11 | * A target structure 12 | 13 | This tutorial covers how to set up and run steered MD (sMD) simulations with BioSimSpace. In this example, the sMD trajectory is used to obtained seeds for equilibirum MD simulations for building a Markov State Model (MSM). 14 | 15 | ### 1. Set up and run sMD 16 | An example of how to set up and run sMD with BioSimSpace is provided in [01_setup_sMD](01_setup_sMD.md). It covers some background for sMD, and creating steering protocols via BioSimSpace. Examples of both a single CV and a multi CV protocol are provided. A [notebook version](01_setup_sMD_AMBER.ipynb) exists as well. 17 | 18 | ### 2. Analysing trajectory and seeded MD 19 | Once an sMD run is complete, the trajectory can be analysed and snapshots for seeded MD extracted [here](02_trajectory_analysis.ipynb). In this case we only look at the `COLVAR` file produced by PLUMED, but, depending on the system, other criteria might be important to decide whether the sMD run was successful. After the snapshots are extracted, they are used as starting points for equilibrium MD simulations, the basics of which are covered in the [introduction](../01_introduction). 20 | -------------------------------------------------------------------------------- /03_steered_md/figures/COLVAR_all.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenBioSim/biosimspace_tutorials/ac200df769ba3373c9e0e724020aaaa96e340889/03_steered_md/figures/COLVAR_all.png -------------------------------------------------------------------------------- /03_steered_md/figures/COLVAR_failed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenBioSim/biosimspace_tutorials/ac200df769ba3373c9e0e724020aaaa96e340889/03_steered_md/figures/COLVAR_failed.png -------------------------------------------------------------------------------- /03_steered_md/figures/COLVAR_snapshots.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenBioSim/biosimspace_tutorials/ac200df769ba3373c9e0e724020aaaa96e340889/03_steered_md/figures/COLVAR_snapshots.png -------------------------------------------------------------------------------- /03_steered_md/figures/ensemble-md-protocol.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenBioSim/biosimspace_tutorials/ac200df769ba3373c9e0e724020aaaa96e340889/03_steered_md/figures/ensemble-md-protocol.png -------------------------------------------------------------------------------- /03_steered_md/figures/open-close.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenBioSim/biosimspace_tutorials/ac200df769ba3373c9e0e724020aaaa96e340889/03_steered_md/figures/open-close.png -------------------------------------------------------------------------------- /03_steered_md/figures/phe196.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenBioSim/biosimspace_tutorials/ac200df769ba3373c9e0e724020aaaa96e340889/03_steered_md/figures/phe196.png -------------------------------------------------------------------------------- /03_steered_md/figures/tyr152.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenBioSim/biosimspace_tutorials/ac200df769ba3373c9e0e724020aaaa96e340889/03_steered_md/figures/tyr152.png -------------------------------------------------------------------------------- /03_steered_md/get_tutorial.py: -------------------------------------------------------------------------------- 1 | import requests, os 2 | 3 | links = { 4 | "01": ( 5 | "data.tar.bz2", 6 | "https://openbiosim-my.sharepoint.com/:u:/g/personal/director_openbiosim_org/EQFPq-ebCEtPmtzWyaJrLiwBUjWOBL_QkqquuGa9x8KP5g?download=1", 7 | ), 8 | "02": ( 9 | "steering.tar.bz2", 10 | "https://openbiosim-my.sharepoint.com/:u:/g/personal/director_openbiosim_org/EYtK_bZ9T9VJm9zWBkSlXakBfvmzupkQWPnMap9_0o1gYg?download=1", 11 | ), 12 | } 13 | 14 | 15 | def download(key): 16 | localfile, url = links[key] 17 | # Do not download if tarball already found 18 | if os.path.isfile(localfile): 19 | return 20 | print("Downloading %s from openbiosim.org ..." % localfile) 21 | req = requests.get(url, stream=True) 22 | with open(localfile, "wb") as f: 23 | for chunk in req.iter_content(chunk_size=1024): 24 | if chunk: 25 | f.write(chunk) 26 | f.flush() 27 | print("Extracting compressed tarball ...") 28 | os.system("tar -xf %s" % localfile) 29 | # os.system("rm %s" % localfile) 30 | -------------------------------------------------------------------------------- /03_steered_md/scripts/sMD_LSF.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | #BSUB -J sMD 4 | #BSUB -q "phase12_slough" 5 | #BSUB -n 1 6 | #BSUB -gpu "num=1:mode=exclusive_process" 7 | #BSUB -R "rusage[mem=1] span[ptile=8] select[type==any]" 8 | #BSUB -M 4000000 9 | #BSUB -cwd /work/slough_md12_scratch/ahardie 10 | #BSUB -outdir /work/slough_md12_scratch/ahardie 11 | #BSUB -o lsf.%J.out 12 | #BSUB -e lsf.%J.err 13 | 14 | cd /work/slough_md12_scratch/ahardie 15 | #source /home/model/MD-SOFTWARE/amber18-gnu-cu10/amber.sh 16 | source /home/model/MD-SOFTWARE/plumed-2.6.1/sourceme.sh 17 | source /home/model/MD-SOFTWARE/BSSenv-new.bashrc 18 | 19 | /home/e628835/software/sire.app/bin/python 01_run_sMD.py --topology system.prm7 --coordinates system.rst7 --reference reference.pdb --residues 179-185 --steering_runtime 150 --total_runtime 152 --force 3500 20 | -------------------------------------------------------------------------------- /03_steered_md/scripts/sMD_multiCV.py: -------------------------------------------------------------------------------- 1 | from importlib.metadata import files 2 | import BioSimSpace as BSS 3 | import os 4 | from argparse import ArgumentParser 5 | from shutil import copyfile 6 | 7 | 8 | def create_rmsd_cv(system, reference): 9 | """Create WPD loop RMSD CV for sMD. 10 | 11 | Parameters 12 | ---------- 13 | system : BioSimSpace._SireWrappers._system.System 14 | steered system 15 | 16 | reference : BioSimSpace._SireWrappers._molecule.Molecule 17 | reference structure for RMSD calculation 18 | 19 | Returns 20 | ------- 21 | rmsd_cv : BioSimSpace.Metadynamics.CollectiveVariable._rmsd.RMSD 22 | CV for use in steering 23 | """ 24 | rmsd_indices = [] 25 | for residue in reference.getResidues(): 26 | if 178 >= residue.index() <= 184: 27 | for atom in residue.getAtoms(): 28 | if atom.element() != "Hydrogen (H, 1)": 29 | rmsd_indices.append(atom.index()) 30 | 31 | rmsd_cv = BSS.Metadynamics.CollectiveVariable.RMSD(system, reference, rmsd_indices) 32 | 33 | return rmsd_cv 34 | 35 | 36 | def create_torsion_cv(system): 37 | """Create a CV for the Chi1 angle of Tyr152 38 | 39 | Parameters 40 | ---------- 41 | system : BioSimSpace._SireWrappers._system.System 42 | steered system 43 | 44 | Returns 45 | ------- 46 | torsion_cv : BioSimSpace.Metadynamics.CollectiveVariable._torsion.Torsion 47 | CV for use in steering 48 | """ 49 | torsion_indices = [] 50 | for atom in system.getMolecule(0).getResidues()[152].getAtoms(): 51 | if atom.name() in ["N", "CA", "CB", "CG"]: 52 | torsion_indices.append(atom.index()) 53 | 54 | torsion_cv = BSS.Metadynamics.CollectiveVariable.Torsion(torsion_indices) 55 | 56 | return torsion_cv 57 | 58 | 59 | def create_distance_cv(system): 60 | """Create a CV for the stacking of Phe196 to Phe280, defined as the distance 61 | between the CG atoms. 62 | 63 | Parameters 64 | ---------- 65 | system : BioSimSpace._SireWrappers._system.System 66 | steered system 67 | 68 | Returns 69 | ------- 70 | distance_cv : BioSimSpace.Metadynamics.CollectiveVariable._distnace.Distance 71 | CV for use in steering 72 | """ 73 | distance_indices = [] 74 | for residue in system.getMolecule(0).getResidues(): 75 | if residue.index() == 196 or residue.index() == 280: 76 | for atom in residue.getAtoms(): 77 | if atom.name() == "CG": 78 | distance_indices.append(atom.index()) 79 | break 80 | 81 | distance_cv = BSS.Metadynamics.CollectiveVariable.Distance( 82 | distance_indices[0], distance_indices[1] 83 | ) 84 | 85 | return distance_cv 86 | 87 | 88 | def create_restraints(force): 89 | """Create restraints for each CV at each steering step 90 | 91 | Parameters 92 | ---------- 93 | force : int 94 | force constant 95 | 96 | Returns 97 | ------- 98 | restraints : [[BioSimSpace.Metadynamics.Restraints]] 99 | steering restraints 100 | """ 101 | nm = BSS.Units.Length.nanometer 102 | rad = BSS.Units.Angle.radian 103 | 104 | restraints = [ 105 | [ 106 | BSS.Metadynamics.Restraint(0.64 * nm, 0), 107 | BSS.Metadynamics.Restraint(1.1 * rad, 0), 108 | BSS.Metadynamics.Restraint(0.56 * nm, 0), 109 | ], # initial 110 | [ 111 | BSS.Metadynamics.Restraint(0.64 * nm, force), 112 | BSS.Metadynamics.Restraint(1.1 * rad, force), 113 | BSS.Metadynamics.Restraint(0.56 * nm, force), 114 | ], # apply force 115 | [ 116 | BSS.Metadynamics.Restraint(0 * nm, force), 117 | BSS.Metadynamics.Restraint(1.1 * rad, force), 118 | BSS.Metadynamics.Restraint(0.4 * nm, force), 119 | ], # steering 120 | [ 121 | BSS.Metadynamics.Restraint(0 * nm, 0), 122 | BSS.Metadynamics.Restraint(1.1 * rad, 0), 123 | BSS.Metadynamics.Restraint(0.4 * nm, 0), 124 | ], 125 | ] # release force 126 | 127 | return restraints 128 | 129 | 130 | def create_protocol(system, reference, steering_runtime, total_runtime, force): 131 | """Create steering protocol 132 | 133 | Parameters 134 | ---------- 135 | system : BioSimSpace._SireWrappers._system.System 136 | steered system 137 | 138 | reference : BioSimSpace._SireWrappers._molecule.Molecule 139 | reference structure for RMSD calculation 140 | 141 | steering_runtime : BioSimSpace.Units.Time 142 | steering time. First 4 ps will be used to apply force 143 | 144 | total_runtime : BioSimSpace.Units.Time 145 | total simulation time. Usually steering_runtime + some short duration for relaxation of system 146 | 147 | Returns 148 | ------- 149 | protocol : BioSimSpace.Protocol._steering.Steering 150 | steering protocol 151 | """ 152 | rmsd_cv = create_rmsd_cv(system, reference) 153 | torsion_cv = create_torsion_cv(system) 154 | distance_cv = create_distance_cv(system) 155 | 156 | restraints = create_restraints(force) 157 | 158 | ns = BSS.Units.Time.nanosecond 159 | schedule = [0 * ns, 0.04 * ns, steering_runtime, total_runtime] 160 | 161 | protocol = BSS.Protocol.Steering( 162 | [rmsd_cv, torsion_cv, distance_cv], 163 | schedule, 164 | restraints, 165 | runtime=total_runtime, 166 | report_interval=2500, 167 | restart_interval=2500, 168 | ) 169 | 170 | return protocol 171 | 172 | 173 | def run_sMD(topology, coordinates, reference, steering_runtime, total_runtime, force): 174 | """Run multi-CV steered MD, steering PTP1B from inactive conformation (open WPD loop) to 175 | active conformation (closed WPD loop). The CVs are predefined, but the force constant and 176 | steering duration can be changed. 177 | 178 | Parameters 179 | ---------- 180 | topology : str 181 | topology file 182 | 183 | coordinates : str 184 | coordinate file 185 | 186 | reference : str 187 | RMSD reference PDB file 188 | 189 | steering_runtime : BioSimSpace.Units.Time 190 | steering duration 191 | 192 | total_runtime : BioSimSpace.Units.Time 193 | total simulation duration 194 | 195 | force : int 196 | force constant 197 | """ 198 | system = BSS.IO.readMolecules([topology, coordinates]) 199 | reference = BSS.IO.readMolecules(reference).getMolecule(0) 200 | 201 | protocol = create_protocol( 202 | system, reference, steering_runtime, total_runtime, force 203 | ) 204 | 205 | process = BSS.Process.Amber( 206 | system, protocol, exe=f'{os.environ["AMBERHOME"]}/bin/pmemd.cuda' 207 | ) 208 | process.start() 209 | process.wait() 210 | 211 | files = [ 212 | "amber.nc", 213 | "COLVAR", 214 | "amber.rst7", 215 | "plumed.dat", 216 | "reference_1.pdb", 217 | "amber.out", 218 | ] 219 | 220 | for file in files: 221 | copyfile(f"{process.workDir()}/{file}", f"./{file}") 222 | 223 | return None 224 | 225 | 226 | def __main__(): 227 | parser = ArgumentParser( 228 | description="Run multi-CV steered MD, steering PTP1B from inactive conformation (open WPD loop) to active conformation (closed WPD loop)." 229 | ) 230 | parser.add_argument( 231 | "--topology", type=str, required=True, help="AMBER topology file" 232 | ) 233 | parser.add_argument( 234 | "--coordinates", type=str, required=True, help="equilibrated system coordinates" 235 | ) 236 | parser.add_argument( 237 | "--reference", type=str, required=True, help="target molecule PDB" 238 | ) 239 | parser.add_argument( 240 | "--steering_runtime", type=float, required=True, help="steering time in ns" 241 | ) 242 | parser.add_argument( 243 | "--total_runtime", type=float, required=True, help="total simulation runtime" 244 | ) 245 | parser.add_argument( 246 | "--force", type=int, required=True, help="steering force_constant" 247 | ) 248 | args = parser.parse_args() 249 | 250 | run_sMD( 251 | args.topology, 252 | args.coordinates, 253 | args.reference, 254 | args.steering_runtime * BSS.Units.Time.nanosecond, 255 | args.total_runtime * BSS.Units.Time.nanosecond, 256 | args.force, 257 | ) 258 | 259 | return None 260 | 261 | 262 | if __name__ == "__main__": 263 | __main__() 264 | -------------------------------------------------------------------------------- /03_steered_md/scripts/sMD_simple.py: -------------------------------------------------------------------------------- 1 | import BioSimSpace as BSS 2 | import os 3 | from argparse import ArgumentParser 4 | from shutil import copyfile 5 | 6 | 7 | def parse_range(residue_range): 8 | """Parse residue range into a list 9 | 10 | Parameters 11 | ---------- 12 | residue_range : str 13 | residue range, e.g. 179-184 14 | 15 | Returns 16 | ------- 17 | residues : [int] 18 | a list of all residues in the range 19 | """ 20 | start = int(residue_range.split("-")[0]) 21 | end = int(residue_range.split("-")[1]) 22 | 23 | residues = [i for i in range(start, end + 1)] 24 | 25 | return residues 26 | 27 | 28 | def create_CV(reference, residues, system): 29 | """Create a collective variable to use in sMD 30 | 31 | Parameters 32 | ---------- 33 | reference : BioSimSpace._SireWrappers._molecule.Molecule 34 | reference structure for RMSD calculation 35 | 36 | residues : [int] 37 | a list of residues that will be used to calculate RMSD 38 | 39 | system : BioSimSpace._SireWrappers._system.System 40 | steered system 41 | 42 | Returns 43 | ------- 44 | rmsd_cv : BioSimSpace.Metadynamics.CollectiveVariable._rmsd.RMSD 45 | CV for use in steering 46 | """ 47 | rmsd_indices = [] 48 | for residue in reference.getResidues(): 49 | if residue.index() in residues: 50 | for atom in residue.getAtoms(): 51 | if atom.element() != "Hydrogen (H, 1)": 52 | rmsd_indices.append(atom.index()) 53 | 54 | rmsd_cv = BSS.Metadynamics.CollectiveVariable.RMSD(system, reference, rmsd_indices) 55 | 56 | return rmsd_cv 57 | 58 | 59 | def setup_sMD_protocol(steering_runtime, total_runtime, force_constant, cv): 60 | """Create steered MD protocol 61 | 62 | Parameters 63 | ---------- 64 | steering_runtime : BioSimSpace.Units.Time 65 | steering time. First 4 ps will be used to apply force 66 | 67 | total_runtime : BioSimSpace.Units.Time 68 | total simulation time. Usually steering_runtime + some short duration for relaxation of system 69 | 70 | force_constant : int 71 | force constant to use for steering 72 | 73 | Returns 74 | ------- 75 | protocol : BioSimSpace.Protocol._steering.Steering 76 | steering protocol 77 | """ 78 | start = 0 * BSS.Units.Time.nanosecond 79 | apply_force = 4 * BSS.Units.Time.picosecond 80 | 81 | nm = BSS.Units.Length.nanometer 82 | restraint_1 = BSS.Metadynamics.Restraint(cv.getInitialValue(), 0) 83 | restraint_2 = BSS.Metadynamics.Restraint(cv.getInitialValue(), force_constant) 84 | restraint_3 = BSS.Metadynamics.Restraint(0 * nm, force_constant) 85 | restraint_4 = BSS.Metadynamics.Restraint(0 * nm, 0) 86 | 87 | protocol = BSS.Protocol.Steering( 88 | cv, 89 | [start, apply_force, steering_runtime, total_runtime], 90 | [restraint_1, restraint_2, restraint_3, restraint_4], 91 | runtime=total_runtime, 92 | report_interval=2500, 93 | restart_interval=2500, 94 | ) 95 | 96 | return protocol 97 | 98 | 99 | def run_sMD( 100 | topology, 101 | coordinates, 102 | reference, 103 | residues, 104 | steering_runtime, 105 | total_runtime, 106 | force_constant, 107 | ): 108 | """Run steered MD with PLUMED and BioSimSpace. 109 | 110 | Parameters 111 | ---------- 112 | topology : str 113 | AMBER topology file 114 | 115 | coordinates : str 116 | equilibrated system coordinates 117 | 118 | reference : str 119 | steering target PDB structure 120 | 121 | residues : [int] 122 | a list of residues that will be used to calculate RMSD 123 | 124 | steering_runtime : BioSimSpace.Units.Time 125 | steering time 126 | 127 | total_runtime : BioSimSpace.Units.Time 128 | total simulation runtime 129 | 130 | force_constant : int 131 | force to use for steering 132 | 133 | Returns 134 | ------- 135 | None 136 | """ 137 | system = BSS.IO.readMolecules([topology, coordinates]) 138 | reference = BSS.IO.readMolecules(reference).getMolecule(0) 139 | 140 | rmsd_cv = create_CV(reference, residues, system) 141 | 142 | protocol = setup_sMD_protocol( 143 | steering_runtime, total_runtime, force_constant, rmsd_cv 144 | ) 145 | 146 | process = BSS.Process.Amber( 147 | system, protocol, exe=f'{os.environ["AMBERHOME"]}/bin/pmemd.cuda' 148 | ) 149 | 150 | process.start() 151 | process.wait() 152 | 153 | files = ["amber.nc", "COLVAR", "stdout", "amber.out"] 154 | for file in files: 155 | copyfile(f"{process.workDir()}/{file}", file) 156 | 157 | return None 158 | 159 | 160 | def __main__(): 161 | parser = ArgumentParser( 162 | description="Set up and run sMD with PLUMED and BioSimSpace" 163 | ) 164 | parser.add_argument( 165 | "--topology", type=str, required=True, help="AMBER topology file" 166 | ) 167 | parser.add_argument( 168 | "--coordinates", type=str, required=True, help="equilibrated system coordinates" 169 | ) 170 | parser.add_argument( 171 | "--reference", type=str, required=True, help="target molecule PDB" 172 | ) 173 | parser.add_argument( 174 | "--residues", 175 | type=str, 176 | required=True, 177 | help="residues that will be used to calculate RMSD, given as a range, e.g. 179-185", 178 | ) 179 | parser.add_argument( 180 | "--steering_runtime", type=float, required=True, help="steering time in ns" 181 | ) 182 | parser.add_argument( 183 | "--total_runtime", type=float, required=True, help="total simulation runtime" 184 | ) 185 | parser.add_argument( 186 | "--force", type=int, required=True, help="steering force_constant" 187 | ) 188 | args = parser.parse_args() 189 | 190 | run_sMD( 191 | args.topology, 192 | args.coordinates, 193 | args.reference, 194 | parse_range(args.residues), 195 | args.steering_runtime * BSS.Units.Time.nanosecond, 196 | args.total_runtime * BSS.Units.Time.nanosecond, 197 | args.force, 198 | ) 199 | 200 | return None 201 | 202 | 203 | if __name__ == "__main__": 204 | __main__() 205 | -------------------------------------------------------------------------------- /03_steered_md/scripts/sMD_slurm.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | #SBATCH --job-name=steeredMD 3 | #SBATCH --gres=gpu:1 4 | #SBATCH --time=80:00:00 5 | #SBATCH -o steered_MD.out 6 | 7 | #load modules 8 | #ensure AMBERHOME is set 9 | source /home/adele/software/amber22/amber.sh 10 | 11 | #get the right python environment 12 | source /home/adele/anaconda3/etc/profile.d/conda.sh 13 | conda activate bss 14 | 15 | #run python script 16 | #output will be saved in current directory 17 | python scripts/sMD_simple.py --topology data/system.prm7 --coordinates data/system.rst7 --reference data/reference.pdb --residues 178-184 --steering_runtime 150 --total_runtime 152 --force 3500 18 | 19 | -------------------------------------------------------------------------------- /03_steered_md/scripts/seededMD.py: -------------------------------------------------------------------------------- 1 | import BioSimSpace as BSS 2 | import os 3 | from shutil import copyfile 4 | from argparse import ArgumentParser 5 | 6 | 7 | def run_MD(system, runtime=50 * BSS.Units.Time.nanosecond, output_dir=""): 8 | """Run equilibrium MD with AMBER (pmemd.cuda). Requires $AMBERHOME to be set. 9 | 10 | Parameters 11 | ---------- 12 | system : BioSimSpace._SireWrappers._system.System 13 | prepared system 14 | 15 | runtime : BioSimSpace.Units.Time 16 | simulation duration 17 | 18 | output_dir : str 19 | output directory 20 | 21 | Returns 22 | ------- 23 | None 24 | """ 25 | protocol = BSS.Protocol.Production(runtime=runtime) 26 | process = BSS.Process.Amber( 27 | system, protocol, exe=f'{os.environ["AMBERHOME"]}/bin/pmemd.cuda' 28 | ) 29 | 30 | process.start() 31 | process.wait() 32 | 33 | # copy over results 34 | [ 35 | copyfile(f"{process.workDir()}/{file}", f"{output_dir}{file}") 36 | for file in ["amber.nc", "stdout", "amber.rst7"] 37 | ] 38 | 39 | return None 40 | 41 | 42 | def seededMD(snapshot, topology, output_dir=""): 43 | """Run an equilibrium MD simulation using AMBER (pmemd.cuda). Requires $AMBERHOME to be set to a correct AMBER installation. 44 | 45 | Parameters 46 | ---------- 47 | snapshot : str 48 | snapshot PDB path 49 | 50 | phosphate_parameters : str 51 | path to additional phospho residue parameters 52 | 53 | output_dir : str 54 | output directory 55 | 56 | Returns 57 | ------- 58 | None 59 | """ 60 | system = BSS.IO.readMolecules([snapshot, topology]) 61 | run_MD(system, output_dir=output_dir) 62 | 63 | return None 64 | 65 | 66 | def __main__(): 67 | parser = ArgumentParser( 68 | description="Set up and run seeded MD from snapshots. For PTP1B with peptide substrate" 69 | ) 70 | parser.add_argument( 71 | "--snapshot", type=str, required=True, help="path to snapshot coordinates" 72 | ) 73 | parser.add_argument( 74 | "--topology", type=str, required=True, help="path to system topology" 75 | ) 76 | parser.add_argument( 77 | "--output", 78 | type=str, 79 | default="", 80 | help="output directory. If not specified, output will be saved in current directory", 81 | ) 82 | args = parser.parse_args() 83 | 84 | seededMD(args.snapshot, args.topology, args.output) 85 | 86 | return None 87 | 88 | 89 | if __name__ == "__main__": 90 | __main__() 91 | -------------------------------------------------------------------------------- /03_steered_md/scripts/seededMD_LSF.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | #BSUB -J "seededMD[1-3]" 4 | #BSUB -q "phase12_slough" 5 | #BSUB -n 1 6 | #BSUB -gpu "num=1:mode=exclusive_process" 7 | #BSUB -R "rusage[mem=1] span[ptile=8] select[type==any]" 8 | #BSUB -M 4000000 9 | #BSUB -cwd /work/slough_md12_scratch/ahardie/seededMD 10 | #BSUB -outdir /work/slough_md12_scratch/ahardie/seededMD 11 | #BSUB -o lsf.%J.out 12 | #BSUB -e lsf.%J.err 13 | 14 | mkdir snapshots/snapshot_$LSB_JOBINDEX 15 | source /home/model/MD-SOFTWARE/BSSenv.bashrc 16 | 17 | python 02_run_seededMD.py --snapshot snapshot_${LSB_JOBINDEX}.pdb --phosphate_params /home/e628835/Documents/BioSimSpaceTutorials/02_steered_md/data/phosphate_params/ --output snapshot_${LSB_JOBINDEX}/ 18 | -------------------------------------------------------------------------------- /04_fep/01_intro_to_alchemy/example_output/mbar_bound_output.txt: -------------------------------------------------------------------------------- 1 | # Analysing data contained in file(s) ['o_xylene_benzene_for_analysis/bound/lambda_0.0000/simfile.dat', 'o_xylene_benzene_for_analysis/bound/lambda_0.1250/simfile.dat', 'o_xylene_benzene_for_analysis/bound/lambda_0.2500/simfile.dat', 'o_xylene_benzene_for_analysis/bound/lambda_0.3750/simfile.dat', 'o_xylene_benzene_for_analysis/bound/lambda_0.5000/simfile.dat', 'o_xylene_benzene_for_analysis/bound/lambda_0.6250/simfile.dat', 'o_xylene_benzene_for_analysis/bound/lambda_0.7500/simfile.dat', 'o_xylene_benzene_for_analysis/bound/lambda_0.8750/simfile.dat', 'o_xylene_benzene_for_analysis/bound/lambda_1.0000/simfile.dat'] 2 | #Overlap matrix 3 | 0.9653 0.0346 0.0000 0.0000 0.0000 0.0000 0.0000 0.0000 0.0000 4 | 0.0346 0.8711 0.0937 0.0006 0.0000 0.0000 0.0000 0.0000 0.0000 5 | 0.0000 0.0937 0.7665 0.1387 0.0011 0.0000 0.0000 0.0000 0.0000 6 | 0.0000 0.0006 0.1387 0.7037 0.1558 0.0013 0.0000 0.0000 0.0000 7 | 0.0000 0.0000 0.0011 0.1558 0.7035 0.1390 0.0007 0.0000 0.0000 8 | 0.0000 0.0000 0.0000 0.0013 0.1390 0.7233 0.1358 0.0007 0.0000 9 | 0.0000 0.0000 0.0000 0.0000 0.0007 0.1358 0.7285 0.1346 0.0005 10 | 0.0000 0.0000 0.0000 0.0000 0.0000 0.0007 0.1346 0.7475 0.1172 11 | 0.0000 0.0000 0.0000 0.0000 0.0000 0.0000 0.0005 0.1172 0.8823 12 | #DG from neighbouring lambda in kcal/mol 13 | 0.0000 0.1250 -3.3726 0.0858 14 | 0.1250 0.2500 -0.5972 0.0486 15 | 0.2500 0.3750 0.6233 0.0375 16 | 0.3750 0.5000 0.8770 0.0344 17 | 0.5000 0.6250 1.0015 0.0374 18 | 0.6250 0.7500 0.8816 0.0381 19 | 0.7500 0.8750 1.0312 0.0384 20 | 0.8750 1.0000 1.2659 0.0422 21 | #PMF from MBAR in kcal/mol 22 | 0.0000 0.0000 0.0000 23 | 0.1250 -3.3726 0.0858 24 | 0.2500 -3.9697 0.1013 25 | 0.3750 -3.3464 0.1104 26 | 0.5000 -2.4694 0.1179 27 | 0.6250 -1.4679 0.1258 28 | 0.7500 -0.5863 0.1334 29 | 0.8750 0.4449 0.1407 30 | 1.0000 1.7108 0.1487 31 | #TI average gradients and standard deviation in kcal/mol 32 | 0.0000 -41.4818 22.7248 33 | 0.1250 -13.3982 17.6651 34 | 0.2500 1.6842 14.3815 35 | 0.3750 6.9031 12.3559 36 | 0.5000 7.7999 12.7966 37 | 0.6250 7.0291 13.1364 38 | 0.7500 7.4244 13.1842 39 | 0.8750 8.7508 14.2247 40 | 1.0000 15.3754 13.9575 41 | #PMF from TI in kcal/mol 42 | 0.0000 0.0000 43 | 0.1250 -3.4300 44 | 0.2500 -4.1621 45 | 0.3750 -3.6254 46 | 0.5000 -2.7065 47 | 0.6250 -1.7797 48 | 0.7500 -0.8763 49 | 0.8750 0.1346 50 | 1.0000 1.6425 51 | #MBAR free energy difference in kcal/mol: 52 | 1.710801, 0.148698 53 | #TI free energy difference in kcal/mol: 54 | 1.642510 55 | -------------------------------------------------------------------------------- /04_fep/01_intro_to_alchemy/example_output/mbar_free_output.txt: -------------------------------------------------------------------------------- 1 | # Analysing data contained in file(s) ['o_xylene_benzene_for_analysis/free/lambda_0.0000/simfile.dat', 'o_xylene_benzene_for_analysis/free/lambda_0.1250/simfile.dat', 'o_xylene_benzene_for_analysis/free/lambda_0.2500/simfile.dat', 'o_xylene_benzene_for_analysis/free/lambda_0.3750/simfile.dat', 'o_xylene_benzene_for_analysis/free/lambda_0.5000/simfile.dat', 'o_xylene_benzene_for_analysis/free/lambda_0.6250/simfile.dat', 'o_xylene_benzene_for_analysis/free/lambda_0.7500/simfile.dat', 'o_xylene_benzene_for_analysis/free/lambda_0.8750/simfile.dat', 'o_xylene_benzene_for_analysis/free/lambda_1.0000/simfile.dat'] 2 | #Overlap matrix 3 | 0.9400 0.0598 0.0001 0.0000 0.0000 0.0000 0.0000 0.0000 0.0000 4 | 0.0598 0.8297 0.1096 0.0008 0.0000 0.0000 0.0000 0.0000 0.0000 5 | 0.0001 0.1096 0.7426 0.1465 0.0012 0.0000 0.0000 0.0000 0.0000 6 | 0.0000 0.0008 0.1465 0.7086 0.1431 0.0010 0.0000 0.0000 0.0000 7 | 0.0000 0.0000 0.0012 0.1431 0.7109 0.1441 0.0008 0.0000 0.0000 8 | 0.0000 0.0000 0.0000 0.0010 0.1441 0.7181 0.1362 0.0006 0.0000 9 | 0.0000 0.0000 0.0000 0.0000 0.0008 0.1362 0.7326 0.1300 0.0005 10 | 0.0000 0.0000 0.0000 0.0000 0.0000 0.0006 0.1300 0.7426 0.1267 11 | 0.0000 0.0000 0.0000 0.0000 0.0000 0.0000 0.0005 0.1267 0.8728 12 | #DG from neighbouring lambda in kcal/mol 13 | 0.0000 0.1250 -2.4184 0.0475 14 | 0.1250 0.2500 -0.3672 0.0329 15 | 0.2500 0.3750 0.5587 0.0270 16 | 0.3750 0.5000 0.8085 0.0274 17 | 0.5000 0.6250 0.8132 0.0274 18 | 0.6250 0.7500 0.8360 0.0285 19 | 0.7500 0.8750 0.9724 0.0294 20 | 0.8750 1.0000 1.1929 0.0300 21 | #PMF from MBAR in kcal/mol 22 | 0.0000 0.0000 0.0000 23 | 0.1250 -2.4184 0.0475 24 | 0.2500 -2.7856 0.0604 25 | 0.3750 -2.2269 0.0683 26 | 0.5000 -1.4184 0.0756 27 | 0.6250 -0.6052 0.0822 28 | 0.7500 0.2308 0.0887 29 | 0.8750 1.2032 0.0950 30 | 1.0000 2.3962 0.1011 31 | #TI average gradients and standard deviation in kcal/mol 32 | 0.0000 -31.3318 20.7957 33 | 0.1250 -8.6938 15.9879 34 | 0.2500 1.7760 13.2496 35 | 0.3750 5.9410 12.5258 36 | 0.5000 6.4975 12.6298 37 | 0.6250 6.7461 12.9909 38 | 0.7500 7.2350 13.4474 39 | 0.8750 8.4256 13.6510 40 | 1.0000 13.8161 13.9399 41 | #PMF from TI in kcal/mol 42 | 0.0000 0.0000 43 | 0.1250 -2.5016 44 | 0.2500 -2.9340 45 | 0.3750 -2.4516 46 | 0.5000 -1.6742 47 | 0.6250 -0.8465 48 | 0.7500 0.0273 49 | 0.8750 1.0061 50 | 1.0000 2.3962 51 | #MBAR free energy difference in kcal/mol: 52 | 2.396172, 0.101130 53 | #TI free energy difference in kcal/mol: 54 | 2.396190 55 | -------------------------------------------------------------------------------- /04_fep/01_intro_to_alchemy/get_tutorial.py: -------------------------------------------------------------------------------- 1 | import requests, os 2 | 3 | links = { 4 | "01": ( 5 | "input.tar.bz2", 6 | "https://openbiosim-my.sharepoint.com/:u:/g/personal/director_openbiosim_org/EeclcaEfAPlAjtMQzXo3QcIBnZOSX_lX9w81a6yKU6p-NQ?download=1", 7 | ), 8 | "02": ( 9 | "o_xylene_benzene_for_analysis.tar.bz2", 10 | "https://openbiosim-my.sharepoint.com/:u:/g/personal/director_openbiosim_org/EYucKLAsmghNknd5914lCTQBC5uQL7fn0Fca_LeOfpaXcA?download=1", 11 | ), 12 | "03": ( 13 | "exercise_4_5.tar.bz2", 14 | "https://openbiosim-my.sharepoint.com/:u:/g/personal/director_openbiosim_org/EcD3SVH8VHpMowLj9MvPWksBYWVAlvKLEV_W5g1R3c4n_Q?download=1", 15 | ), 16 | "04": ( 17 | "example_output.tar.bz2", 18 | "https://openbiosim-my.sharepoint.com/:u:/g/personal/director_openbiosim_org/EbXnbu36ozpFq1WNraQeeSQB6wQExM4rFkveOZVkh3dNyw?download=1", 19 | ), 20 | } 21 | 22 | 23 | def download(link): 24 | localfile, url = links[link] 25 | # Do not download if tarball already found 26 | if os.path.isfile(localfile): 27 | return 28 | print("Downloading %s from openbiosim.org ..." % localfile) 29 | req = requests.get(url, stream=True) 30 | with open(localfile, "wb") as f: 31 | for chunk in req.iter_content(chunk_size=1024): 32 | if chunk: 33 | f.write(chunk) 34 | f.flush() 35 | print("Extracting compressed tarball ...") 36 | os.system("tar -xf %s" % localfile) 37 | # os.system("rm %s" % localfile) 38 | -------------------------------------------------------------------------------- /04_fep/01_intro_to_alchemy/images/MCS.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenBioSim/biosimspace_tutorials/ac200df769ba3373c9e0e724020aaaa96e340889/04_fep/01_intro_to_alchemy/images/MCS.png -------------------------------------------------------------------------------- /04_fep/01_intro_to_alchemy/images/Therm_cycle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenBioSim/biosimspace_tutorials/ac200df769ba3373c9e0e724020aaaa96e340889/04_fep/01_intro_to_alchemy/images/Therm_cycle.png -------------------------------------------------------------------------------- /04_fep/01_intro_to_alchemy/images/overlap_MBAR_example.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenBioSim/biosimspace_tutorials/ac200df769ba3373c9e0e724020aaaa96e340889/04_fep/01_intro_to_alchemy/images/overlap_MBAR_example.png -------------------------------------------------------------------------------- /04_fep/01_intro_to_alchemy/images/overlap_MBAR_example_circles.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenBioSim/biosimspace_tutorials/ac200df769ba3373c9e0e724020aaaa96e340889/04_fep/01_intro_to_alchemy/images/overlap_MBAR_example_circles.png -------------------------------------------------------------------------------- /04_fep/01_intro_to_alchemy/images/overlap_MBAR_example_less.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenBioSim/biosimspace_tutorials/ac200df769ba3373c9e0e724020aaaa96e340889/04_fep/01_intro_to_alchemy/images/overlap_MBAR_example_less.png -------------------------------------------------------------------------------- /04_fep/01_intro_to_alchemy/images/overlap_MBAR_example_less_circles.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenBioSim/biosimspace_tutorials/ac200df769ba3373c9e0e724020aaaa96e340889/04_fep/01_intro_to_alchemy/images/overlap_MBAR_example_less_circles.png -------------------------------------------------------------------------------- /04_fep/01_intro_to_alchemy/images/thermo_cycle_rel_eq.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenBioSim/biosimspace_tutorials/ac200df769ba3373c9e0e724020aaaa96e340889/04_fep/01_intro_to_alchemy/images/thermo_cycle_rel_eq.png -------------------------------------------------------------------------------- /04_fep/01_intro_to_alchemy/slides/CCPBioSimTraining2022_slides.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenBioSim/biosimspace_tutorials/ac200df769ba3373c9e0e724020aaaa96e340889/04_fep/01_intro_to_alchemy/slides/CCPBioSimTraining2022_slides.pdf -------------------------------------------------------------------------------- /04_fep/02_RBFE/get_tutorial.py: -------------------------------------------------------------------------------- 1 | import requests, os 2 | 3 | links = { 4 | "01": ( 5 | "inputs.tar.bz2", 6 | "https://openbiosim-my.sharepoint.com/:u:/g/personal/director_openbiosim_org/EfHshdM9FoVBvAXUp0x1zxMBcoGb4nNcfnSkkxfj51ij6g?download=1", 7 | ), 8 | "02": ( 9 | "analysis.tar.bz2", 10 | "https://openbiosim-my.sharepoint.com/:u:/g/personal/director_openbiosim_org/EQ8kWX_hGy5PvzhqvnLWpMwBaqxd_Cd2ez5zEjFI0EfT2g?download=1", 11 | ), 12 | "03": ( 13 | "example_output.tar.bz2", 14 | "https://openbiosim-my.sharepoint.com/:u:/g/personal/director_openbiosim_org/EbPiCqbEriZCuhP0SMkRLX0BKdvODAkjIxPDtUf1t8CxmA?download=1", 15 | ), 16 | } 17 | 18 | 19 | def download(link): 20 | localfile, url = links[link] 21 | # Do not download if tarball already found 22 | if os.path.isfile(localfile): 23 | return 24 | print("Downloading %s from openbiosim.org ..." % localfile) 25 | req = requests.get(url, stream=True) 26 | with open(localfile, "wb") as f: 27 | for chunk in req.iter_content(chunk_size=1024): 28 | if chunk: 29 | f.write(chunk) 30 | f.flush() 31 | print("Extracting compressed tarball ...") 32 | os.system("tar -xf %s" % localfile) 33 | # os.system("rm %s" % localfile) 34 | -------------------------------------------------------------------------------- /04_fep/02_RBFE/images/fep_pipeline.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenBioSim/biosimspace_tutorials/ac200df769ba3373c9e0e724020aaaa96e340889/04_fep/02_RBFE/images/fep_pipeline.png -------------------------------------------------------------------------------- /04_fep/02_RBFE/images/tyk2_protlig.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenBioSim/biosimspace_tutorials/ac200df769ba3373c9e0e724020aaaa96e340889/04_fep/02_RBFE/images/tyk2_protlig.png -------------------------------------------------------------------------------- /04_fep/02_RBFE/scripts/.svn/entries: -------------------------------------------------------------------------------- 1 | 12 2 | -------------------------------------------------------------------------------- /04_fep/02_RBFE/scripts/.svn/format: -------------------------------------------------------------------------------- 1 | 12 2 | -------------------------------------------------------------------------------- /04_fep/02_RBFE/scripts/.svn/pristine/01/015f3293a2b13c2bb141480cf0a46d2710d7e505.svn-base: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | echo $SLURM_JOB_NAME 3 | date 4 | 5 | $BSSHOME/bin/python scripts/BSSprepFEP.py $1 $2 6 | date 7 | sleep 5 8 | exit 0 9 | -------------------------------------------------------------------------------- /04_fep/02_RBFE/scripts/.svn/pristine/09/09b149f7700b981e3a51dbb930632c234a099ab8.svn-base: -------------------------------------------------------------------------------- 1 | import BioSimSpace as BSS 2 | from BioSimSpace import _Exceptions 3 | import sys 4 | import csv 5 | import os 6 | import numpy as np 7 | 8 | import matplotlib as mpl 9 | mpl.use('Agg') 10 | import matplotlib.pyplot as plt 11 | from matplotlib import colors 12 | import matplotlib.pyplot as plt 13 | import seaborn as sns 14 | 15 | print ("%s %s %s %s" % (sys.argv[0], sys.argv[1], sys.argv[2], sys.argv[3])) 16 | results_file_path = "./outputs/summary.csv" 17 | 18 | def plotOverlapMatrix(overlap_matrix, savepath): 19 | """ 20 | Given a SOMD-style overlap numpy matrix, plot heatmap in best practices style. 21 | 22 | --args 23 | overlap matrix (array): numpy 2D array of (n,n) 24 | 25 | --returns 26 | None; saves plot instead. 27 | """ 28 | 29 | 30 | ovlp_mtx = overlap_matrix 31 | 32 | cmap = colors.ListedColormap(['#FBE8EB','#88CCEE','#78C592', '#117733']) 33 | bounds=[0.0, 0.025, 0.1, 0.3,0.8] 34 | norm = colors.BoundaryNorm(bounds, cmap.N, clip=False) 35 | cbar_kws=dict(ticks=[.025, .1, .3,0.8], label='Phase space overlap') 36 | ax = sns.heatmap(ovlp_mtx,annot=False, fmt='.2f', linewidths=.3, 37 | annot_kws={"size": 14},square=True,robust=True,cmap=cmap, 38 | norm=norm,cbar_kws=cbar_kws) 39 | ax.xaxis.tick_top() 40 | 41 | plt.savefig(savepath, dpi=200) 42 | 43 | # simply load the FEP directory of the corresponding ligand using BSS. 44 | # this function computes the binding free energy as well. 45 | 46 | #print (sys.argv) 47 | engine = sys.argv[3].rstrip() 48 | #print ("@%s@" % sys.argv[1]) 49 | #print ("@%s@" % sys.argv[2]) 50 | #print ("@%s@" % engine) 51 | 52 | path_to_dir = f"./outputs/{engine}/{sys.argv[1]}~{sys.argv[2]}" 53 | print (path_to_dir) 54 | #path_to_dir = "./outputs/%s/%s~%s" % (engine, sys.argv[1], sys.argv[2]) 55 | #print (path_to_dir) 56 | freenrg_val = "NaN" 57 | freenrg_err = "NaN" 58 | try: 59 | pmf_bound, overlap_matrix_bound = BSS.FreeEnergy.Relative.analyse(path_to_dir+"/bound") 60 | pmf_free, overlap_matrix_free = BSS.FreeEnergy.Relative.analyse(path_to_dir+"/free") 61 | 62 | freenrg = BSS.FreeEnergy.Relative.difference(pmf_bound, pmf_free) 63 | freenrg_val = round(freenrg[0].value(), 4) 64 | freenrg_err = round(freenrg[1].value(), 4) 65 | 66 | except _Exceptions.AnalysisError: 67 | freenrg_val = freenrg_err = "NaN" 68 | overlap_matrix_bound = overlap_matrix_free = None 69 | 70 | 71 | data_point = [sys.argv[1], sys.argv[2], str(freenrg_val), str(freenrg_err), engine] 72 | 73 | ####### WRITING DATA 74 | 75 | # use csv to open the results file. 76 | with open(results_file_path, "a") as freenrg_writefile: 77 | writer = csv.writer(freenrg_writefile) 78 | 79 | # first, write a header if the file is created for the first time. 80 | if os.path.getsize(results_file_path) == 0: 81 | print(f"Starting {results_file_path} file.") 82 | writer.writerow(["lig_1", "lig_2", "freenrg", "error", "engine"]) 83 | 84 | 85 | with open(results_file_path, "r") as freenrg_readfile: 86 | # then, grab all of the data that is already in the file. 87 | reader = csv.reader(freenrg_readfile) 88 | data_entries = [ row for row in reader ] 89 | 90 | # check if our data entry is not already in the results file. Raise an error if is. 91 | if data_point in data_entries: 92 | raise Exception(f"Results for this run are already in {results_file_path}. Exiting.") 93 | 94 | # at this point we know that we are writing a new entry in the results file. Append the line to the file. 95 | # use csv to open the results file. 96 | with open(results_file_path, "a") as freenrg_writefile: 97 | writer = csv.writer(freenrg_writefile) 98 | 99 | print("Writing MBAR results. Free energy of binding and error are (rsp.):") 100 | print(freenrg) 101 | writer.writerow(data_point) 102 | 103 | 104 | # in case of SOMD, we will also have overlap matrices for both legs. These are helpful for troubleshooting, so store 105 | # them in ./logs/ 106 | if overlap_matrix_bound: 107 | np.save(f"logs/overlap_bound_{sys.argv[1]}~{sys.argv[2]}", np.matrix(overlap_matrix_bound)) 108 | plotOverlapMatrix(np.matrix(overlap_matrix_bound), f"logs/overlap_bound_{sys.argv[1]}~{sys.argv[2]}.png") 109 | else: 110 | print("Failed to write overlap matrix for bound leg.") 111 | if overlap_matrix_free: 112 | np.save(f"logs/overlap_free_{sys.argv[1]}~{sys.argv[2]}", np.matrix(overlap_matrix_free)) 113 | plotOverlapMatrix(np.matrix(overlap_matrix_free), f"logs/overlap_free_{sys.argv[1]}~{sys.argv[2]}.png") 114 | else: 115 | print("Failed to write overlap matrix for free leg.") 116 | -------------------------------------------------------------------------------- /04_fep/02_RBFE/scripts/.svn/pristine/4c/4cdea129ef2fd591a19b69c88e8b8182b9c293ce.svn-base: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | 4 | # retrieve the array ID depending on whether we're running on LSF or slurm. 5 | if ((${#LSB_JOBINDEX[@]})); then 6 | echo "This is an LSF job." 7 | ARRAY_TASK_ID=$(($LSB_JOBINDEX-1)) 8 | 9 | elif ((${#SLURM_ARRAY_TASK_ID[@]})); then 10 | echo "This is a Slurm job." 11 | ARRAY_TASK_ID=$SLURM_ARRAY_TASK_ID 12 | 13 | else 14 | echo "No array ID found - are you sure you are running this script on a cluster?" 15 | fi 16 | 17 | echo "Array ID is" 18 | date 19 | $BSSHOME/bin/ipython scripts/BSSligprep.py $ARRAY_TASK_ID 20 | 21 | date 22 | 23 | exit 0 24 | -------------------------------------------------------------------------------- /04_fep/02_RBFE/scripts/.svn/pristine/e4/e489348848a63603fd82f847f97faf1e5598002c.svn-base: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | echo $SLURM_JOB_NAME 3 | date 4 | 5 | lig0=$1 6 | lig1=$2 7 | engine=$3 8 | 9 | sleep 5 10 | 11 | echo "$BSSHOME/bin/python scripts/BSSanalyseFEP.py $lig0 $lig1 $engine" 12 | $BSSHOME/bin/python scripts/BSSanalyseFEP.py $lig0 $lig1 $engine 13 | 14 | exit 0 15 | -------------------------------------------------------------------------------- /04_fep/02_RBFE/scripts/.svn/pristine/ec/ec0594e6d6479b8b830af7315ad23adaed62f79e.svn-base: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | date 3 | 4 | lig0=$1 5 | lig1=$2 6 | lambdastring=$3 7 | engine=$4 8 | 9 | IFS=',' read -r -a lambdas <<< "$lambdastring" 10 | 11 | nwin=${#lambdas[@]} 12 | 13 | # retrieve the array ID depending on whether we're running on LSF or slurm. 14 | if ((${#LSB_JOBINDEX[@]})); then 15 | echo "This is an LSF job." 16 | ARRAY_TASK_ID=$(($LSB_JOBINDEX-1)) 17 | 18 | elif ((${#SLURM_ARRAY_TASK_ID[@]})); then 19 | echo "This is a Slurm job." 20 | ARRAY_TASK_ID=$SLURM_ARRAY_TASK_ID 21 | 22 | else 23 | echo "No array ID found - are you sure you are running this script on a cluster?" 24 | fi 25 | 26 | echo "Array ID is" $ARRAY_TASK_ID 27 | 28 | 29 | if [ "$ARRAY_TASK_ID" -ge "$nwin" ]; then 30 | stage="free" 31 | idx=$(( $ARRAY_TASK_ID - $nwin )) 32 | else 33 | stage="bound" 34 | idx=$ARRAY_TASK_ID 35 | fi 36 | 37 | 38 | echo "idx is " $idx 39 | lambda=${lambdas[$idx]} 40 | echo "lambda is: "$lambda 41 | echo "stage is: "$stage 42 | echo "engine is: "$engine 43 | 44 | 45 | if [[ $engine == *"SOMD"* ]]; then 46 | echo "USING SOMD" 47 | echo "cd outputs/SOMD/$lig0~$lig1/$stage/lambda_$lambda/" 48 | cd outputs/SOMD/$lig0~$lig1/$stage/lambda_$lambda/ 49 | # run SOMD simulation. 50 | echo "$BSSHOME/bin/somd-freenrg -C ./somd.cfg -l $lambda -c ./somd.rst7 -t ./somd.prm7 -m ./somd.pert 1> somd.log 2> somd.err" 51 | $BSSHOME/bin/somd-freenrg -C ./somd.cfg -l $lambda -c ./somd.rst7 -t ./somd.prm7 -m ./somd.pert 1> somd.log 2> somd.err 52 | if [[ $REMOVE_SIM_DATA == "True" ]]; then 53 | echo "Removing simulation data. If this is unintended, set REMOVE_SIM_DATA to False in processFEP-*.sh." 54 | rm *.dcd *.s3* latest* 55 | fi 56 | 57 | elif [[ $engine == *"GROMACS"* ]]; then 58 | echo "USING GROMACS" 59 | echo "cd outputs/GROMACS/$lig0~$lig1/$stage/lambda_$lambda/" 60 | cd outputs/GROMACS/$lig0~$lig1/$stage/lambda_$lambda/ 61 | # run GROMACS simulation. 62 | echo "gmx mdrun -v -deffnm gromacs 1> gromacs.log 2> gromacs.err" 63 | gmx mdrun -v -deffnm gromacs 1> gromacs.log 2> gromacs.err 64 | else 65 | echo "The FEP engine $engine is not supported" 66 | fi 67 | 68 | exit 0 69 | -------------------------------------------------------------------------------- /04_fep/02_RBFE/scripts/.svn/pristine/f3/f3791f681e50dd384968ce79e03b599e355721aa.svn-base: -------------------------------------------------------------------------------- 1 | import BioSimSpace as BSS 2 | from BioSimSpace import _Exceptions 3 | import os,sys 4 | import csv 5 | print(BSS.__version__) 6 | 7 | print ("%s %s %s" % (sys.argv[0],sys.argv[1],sys.argv[2])) 8 | 9 | # Load equilibrated free inputs for both ligands. Complain if input not found. These systems already contain equil. waters. 10 | print(f"Loading ligands {sys.argv[1]} and {sys.argv[2]}.") 11 | ligs_path = "prep/ligands/" 12 | ligand_1_sys = BSS.IO.readMolecules([f"{ligs_path}{sys.argv[1]}_lig_equil_solv.rst7", f"{ligs_path}{sys.argv[1]}_lig_equil_solv.prm7"]) 13 | ligand_2_sys = BSS.IO.readMolecules([f"{ligs_path}{sys.argv[2]}_lig_equil_solv.rst7", f"{ligs_path}{sys.argv[2]}_lig_equil_solv.prm7"]) 14 | 15 | # Extract ligands. 16 | ligand_1 = ligand_1_sys.getMolecule(0) 17 | ligand_2 = ligand_2_sys.getMolecule(0) 18 | 19 | # Align ligand2 on ligand1 20 | print("Mapping and aligning..") 21 | print(ligand_1, ligand_2) 22 | mapping = BSS.Align.matchAtoms(ligand_1, ligand_2, sanitize=True, complete_rings_only=True) 23 | inv_mapping = {v:k for k,v in mapping.items()} 24 | ligand_2_a = BSS.Align.rmsdAlign(ligand_2, ligand_1, inv_mapping) 25 | 26 | # Generate merged molecule. 27 | print("Merging..") 28 | merged_ligs = BSS.Align.merge(ligand_1, ligand_2_a, mapping) 29 | 30 | #### Get equilibrated waters and waterbox information for both bound and free. Get all information from lambda==0 31 | # Following is work around because setBox() doesn't validate correctly boxes with lengths and angles 32 | 33 | ligand_1_sys.removeMolecules(ligand_1) 34 | ligand_1_sys.addMolecules(merged_ligs) 35 | system_free = ligand_1_sys 36 | 37 | 38 | 39 | ################ now repeat above steps, but for the protein + ligand systems. 40 | # Load equilibrated bound inputs for both ligands. Complain if input not found 41 | print(f"Loading bound ligands {sys.argv[1]} and {sys.argv[2]}.") 42 | ligs_path = "prep/protein/" 43 | system_1 = BSS.IO.readMolecules([f"{ligs_path}{sys.argv[1]}_sys_equil_solv.rst7", f"{ligs_path}{sys.argv[1]}_sys_equil_solv.prm7"]) 44 | system_2 = BSS.IO.readMolecules([f"{ligs_path}{sys.argv[2]}_sys_equil_solv.rst7", f"{ligs_path}{sys.argv[2]}_sys_equil_solv.prm7"]) 45 | 46 | # Extract ligands and protein. Do this based on nAtoms and nResidues, as sometimes 47 | # the order of molecules is switched, so we can't use index alone. 48 | # bugfix in BSS makes the below redundant but keeping this in to be 100% sure we're getting the correct structures. 49 | system_ligand_1 = None 50 | protein = None 51 | n_residues = [mol.nResidues() for mol in system_1] 52 | n_atoms = [mol.nAtoms() for mol in system_1] 53 | for i, (n_resi, n_at) in enumerate(zip(n_residues[:20], n_atoms[:20])): 54 | if n_resi == 1 and n_at > 5: 55 | system_ligand_1 = system_1.getMolecule(i) 56 | elif n_resi > 1: 57 | protein = system_1.getMolecule(i) 58 | else: 59 | pass 60 | 61 | # loop over molecules in system to extract the ligand 62 | system_ligand_2 = None 63 | 64 | n_residues = [mol.nResidues() for mol in system_2] 65 | n_atoms = [mol.nAtoms() for mol in system_2] 66 | for i, (n_resi, n_at) in enumerate(zip(n_residues, n_atoms)): 67 | # grab the system's ligand and the protein. ignore the waters. 68 | if n_resi == 1 and n_at > 5: 69 | system_ligand_2 = system_2.getMolecule(i) 70 | else: 71 | pass 72 | 73 | # extract ions. 74 | #ions_bound = system_2.search("not mols with atomidx 2") 75 | 76 | if system_ligand_1 and system_ligand_2 and protein: 77 | print("Using molecules ligand_1, ligand_2, protein:") 78 | print(system_ligand_1, system_ligand_2, protein) 79 | else: 80 | raise _Exceptions.AlignmentError("Could not extract ligands or protein from input systems. Check that your ligands/proteins are properly prepared by BSSligprep.sh!") 81 | 82 | # Align ligand2 on ligand1 83 | print("Mapping..") 84 | mapping = BSS.Align.matchAtoms(system_ligand_1, system_ligand_2, sanitize=True, complete_rings_only=True) 85 | inv_mapping = {v:k for k,v in mapping.items()} 86 | 87 | print("Aligning..") 88 | system_ligand_2_a = BSS.Align.rmsdAlign(system_ligand_2, system_ligand_1, inv_mapping) 89 | 90 | # Generate merged molecule. 91 | print("Merging..") 92 | system_merged_ligs = BSS.Align.merge(system_ligand_1, system_ligand_2_a, mapping) 93 | 94 | 95 | 96 | system_1.removeMolecules(system_ligand_1) 97 | system_1.addMolecules(system_merged_ligs) 98 | system_bound = system_1 99 | 100 | ########################### now set up the SOMD or GROMACS MD directories. 101 | #first, figure out which engine and what runtime the user has specified in protocol. 102 | stream = open("protocol.dat","r") 103 | lines = stream.readlines() 104 | 105 | ### get the requested engine. 106 | engine_query = lines[7].rstrip().replace(" ","").split("=")[-1].upper() 107 | if engine_query not in ["SOMD", "GROMACS"]: 108 | raise NameError("Input MD engine not recognised. Please use any of ['SOMD', 'GROMACS']" \ 109 | +"on the eighth line of protocol.dat in the shape of (e.g.):\nengine = SOMD") 110 | 111 | ### get the requested runtime. 112 | runtime_query = lines[6].rstrip().replace(" ","").split("=")[-1].split("*")[0] 113 | try: 114 | runtime_query = int(runtime_query) 115 | except ValueError: 116 | raise NameError("Input runtime value not supported. Please use an integer" \ 117 | +" on the seventh line of protocol.dat in the shape of (e.g.):\nsampling = 2*ns") 118 | 119 | # make sure user has set ns or ps. 120 | runtime_unit_query = lines[6].rstrip().replace(" ","").split("=")[-1].split("*")[1] 121 | if runtime_unit_query not in ["ns", "ps"]: 122 | raise NameError("Input runtime unit not supported. Please use 'ns' or 'ps'" \ 123 | +" on the seventh line of protocol.dat in the shape of (e.g.):\nsampling = 2*ns") 124 | 125 | if runtime_unit_query == "ns": 126 | runtime_unit = BSS.Units.Time.nanosecond 127 | elif runtime_unit_query == "ps": 128 | runtime_unit = BSS.Units.Time.picosecond 129 | 130 | ### get the number of lambda windows for this pert. 131 | num_lambda = None 132 | with open("network.dat", "r") as lambdas_file: 133 | reader = csv.reader(lambdas_file, delimiter=" ") 134 | for row in reader: 135 | 136 | if (row[0] == sys.argv[1] and row[1] == sys.argv[2]) or \ 137 | (row[1] == sys.argv[1] and row[0] == sys.argv[2]): 138 | num_lambda = int(row[2]) 139 | if not num_lambda: 140 | raise NameError(f"The perturbation {sys.argv[1]}~{sys.argv[2]} (or the reverse) was not found in network.dat.") 141 | 142 | # define the free energy protocol with all this information. User could customise settings further here, see docs. 143 | freenrg_protocol = BSS.Protocol.FreeEnergy(num_lam=num_lambda, runtime=runtime_query*runtime_unit) 144 | 145 | 146 | ############# Set up the directory environment. 147 | # testing is already done by BSS. 148 | workdir = f"outputs/{engine_query}/{sys.argv[1]}~{sys.argv[2]}/" 149 | print(f"Setting up {engine_query} directory environment in {workdir}.") 150 | 151 | # set up a bound folder with standard settings. 152 | # Use solvation to prep the bound leg 153 | print("Bound..") 154 | workdir=f"outputs/{engine_query}/{sys.argv[1]}~{sys.argv[2]}" 155 | BSS.FreeEnergy.Relative( 156 | system_bound, 157 | freenrg_protocol, 158 | engine=f"{engine_query}", 159 | work_dir=workdir+"/bound" 160 | ) 161 | 162 | # set up a free folder. 163 | print("Free..") 164 | BSS.FreeEnergy.Relative( 165 | system_free, 166 | freenrg_protocol, 167 | engine=f"{engine_query}", 168 | work_dir=workdir+"/free" 169 | ) 170 | -------------------------------------------------------------------------------- /04_fep/02_RBFE/scripts/.svn/wc.db: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenBioSim/biosimspace_tutorials/ac200df769ba3373c9e0e724020aaaa96e340889/04_fep/02_RBFE/scripts/.svn/wc.db -------------------------------------------------------------------------------- /04_fep/02_RBFE/scripts/.svn/wc.db-journal: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenBioSim/biosimspace_tutorials/ac200df769ba3373c9e0e724020aaaa96e340889/04_fep/02_RBFE/scripts/.svn/wc.db-journal -------------------------------------------------------------------------------- /04_fep/02_RBFE/scripts/analysis.py: -------------------------------------------------------------------------------- 1 | # analysis script 2 | 3 | import warnings 4 | import BioSimSpace as BSS 5 | import sys 6 | import csv 7 | import os as _os 8 | 9 | # someway that this is for the transformations listed in the csv file made at the start for the setup 10 | # or alternatively do this in bash 11 | trans = sys.argv[1].rstrip() 12 | lig_1 = trans.split("~")[0] 13 | lig_2 = trans.split("~")[1] 14 | engine = sys.argv[2].rstrip() 15 | 16 | main_dir = _os.environ["MAINDIRECTORY"] 17 | final_results_file_path = f"{main_dir}/outputs/final_summary_{engine}.csv" 18 | 19 | path_to_dir = f"{main_dir}/outputs/{engine}/{trans}" 20 | 21 | if not _os.path.exists(path_to_dir): 22 | raise OSError(f"{path_to_dir} does not exist.") 23 | 24 | print(f"analysing results for {path_to_dir}") 25 | print(f"using {chosen_method} and {chosen_estimator} for analysis") 26 | 27 | try: 28 | pmf_bound, overlap_matrix_bound = BSS.FreeEnergy.Relative.analyse( 29 | f"{path_to_dir}/bound" 30 | ) 31 | except Exception as e: 32 | print(e) 33 | print(f"Unable to analyse values for bound in {path_to_dir}.") 34 | 35 | try: 36 | pmf_free, overlap_matrix_free = BSS.FreeEnergy.Relative.analyse( 37 | f"{path_to_dir}/free" 38 | ) 39 | except Exception as e: 40 | print(e) 41 | print(f"Unable to analyse values for free in {path_to_dir}.") 42 | 43 | freenrg_rel = BSS.FreeEnergy.Relative.difference(pmf_bound, pmf_free) 44 | freenrg_val = freenrg_rel[0].value() 45 | freenrg_err = freenrg_rel[1].value() 46 | 47 | # ####### WRITING DATA for the final result 48 | # data point for average 49 | data_point = [lig_1, lig_2, str(freenrg_val), str(freenrg_err), engine] 50 | print(data_point) 51 | 52 | # use csv to open the results file. 53 | with open(final_results_file_path, "a") as freenrg_writefile: 54 | writer = csv.writer(freenrg_writefile) 55 | 56 | # first, write a header if the file is created for the first time. 57 | if _os.path.getsize(final_results_file_path) == 0: 58 | print(f"Starting {final_results_file_path} file.") 59 | writer.writerow(["lig_1", "lig_2", "freenrg", "error", "engine"]) 60 | 61 | 62 | with open(final_results_file_path, "r") as freenrg_readfile: 63 | # then, grab all of the data that is already in the file. 64 | reader = csv.reader(freenrg_readfile) 65 | data_entries = [row for row in reader] 66 | 67 | # check if our data entry is not already in the results file. Raise an error if is. 68 | if data_point in data_entries: 69 | warnings.warn( 70 | f"Results for in {trans}, {engine} are already in {final_results_file_path}." 71 | ) 72 | 73 | else: 74 | # at this point we know that we are writing a new entry in the results file. Append the line to the file. 75 | # use csv to open the results file. 76 | with open(final_results_file_path, "a") as freenrg_writefile: 77 | writer = csv.writer(freenrg_writefile) 78 | print( 79 | f"Writing results. Free energy of binding is {freenrg_rel[0]} and the error is {freenrg_rel[1]} for {trans}, {engine}." 80 | ) 81 | writer.writerow(data_point) 82 | -------------------------------------------------------------------------------- /04_fep/02_RBFE/scripts/extract_execution_model_bash.sh: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | # script to turn the execution model files into bash suitable arrays and variables. 3 | 4 | # remove any ^M from end of file lines 5 | dos2unix "$lig_file" 6 | dos2unix "$net_file" 7 | dos2unix "$prot_file" 8 | 9 | # For all the ligands in ligands.dat, make an array 10 | lig_array=() 11 | while read lig; do 12 | lig_clean=$(sed 's/\r$//' <<< $lig); lig_array+=("${lig_clean}"); 13 | done < $lig_file 14 | 15 | # Make a list of the transformations from the network.dat file 16 | trans_array=() 17 | eng_array=() 18 | win_array=() 19 | IFS=' ' 20 | while read trans; do 21 | while read -a tra; do tran=${tra[0]}~${tra[1]}; eng=${tra[-1]}; win=${tra[2]}; 22 | trans_array+=("$tran"); eng_array+=("$eng"); win_array+=("$win"); done <<< $trans 23 | done < $net_file 24 | 25 | # check that the trans, eng, and win arrays are the same length. 26 | -------------------------------------------------------------------------------- /04_fep/02_RBFE/scripts/run_all_slurm.sh: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | # 3 | # Run all the lig prep, FEP prep, and the production runs 4 | 5 | # TODO set the main directory filepath, make sure BSS branch and engines are sourced correctly, replace all XXX 6 | export MAINDIRECTORY="XXX" # Set file path ; this should be the absolute file path to the main folder 7 | export scripts_dir="$MAINDIRECTORY/scripts" # choose location of scripts 8 | export protein_file="$MAINDIRECTORY/inputs/prot_water" # this should be the prm7 and rst7 file name (without the extension) for the protein 9 | export ligands_folder="$MAINDIRECTORY/inputs/ligands" # location of the input files for the ligands 10 | 11 | module load cuda/11.6 12 | module load amber/22 13 | module load gromacs/22.2 14 | export BSS="/export/users/XXX/anaconda3/bin/activate biosimspace-dev" 15 | source $BSS 16 | 17 | # might have to source like this, depending on where it is 18 | # export amber="/usr/local/amber22/amber.sh" 19 | # export gromacs="/usr/local/gromacs/bin/GMXRC" 20 | # source $amber 21 | # source $gromacs 22 | 23 | # export all files for later scripts 24 | export lig_file="$MAINDIRECTORY/ligands.dat" 25 | export net_file="$MAINDIRECTORY/network.dat" 26 | export prot_file="$MAINDIRECTORY/protocol.dat" 27 | 28 | # sourcing - as needed in the othe sh scripts 29 | source extract_execution_model_bash.sh 30 | 31 | ########### 32 | echo "The folder for all these runs is $MAINDIRECTORY" 33 | echo ${lig_array[@]} 34 | echo ${trans_array[@]} 35 | echo ${eng_array[@]} 36 | echo ${win_array[@]} 37 | 38 | # make output dir for slurm out and err files 39 | if [[ ! -d ../slurm_logs ]]; then 40 | mkdir ../slurm_logs 41 | fi 42 | 43 | # make directory for tmp from lig prep 44 | if [[ ! -d ../tmp ]]; then 45 | mkdir ../tmp 46 | fi 47 | 48 | # chmod all files so can be executed by sbatch. 49 | chmod u+x run_ligprep_slurm.sh 50 | chmod u+x run_fepprep_slurm.sh 51 | chmod u+x run_production_slurm.sh 52 | 53 | # Run the runs 54 | # ligand prep 55 | jidlig=$(sbatch --parsable --array=0-$((${#lig_array[@]} - 1)) run_ligprep_slurm.sh) 56 | echo "ligand prep jobid is $jidlig" 57 | 58 | # FEP prep 59 | jidfep=$(sbatch --dependency=afterany:${jidlig} --parsable --array=0-$((${#trans_array[@]} - 1)) run_fepprep_slurm.sh) 60 | echo "FEP prep jobid is $jidfep" 61 | 62 | # Production runs for the transformation 63 | for i in "${!trans_array[@]}"; do 64 | 65 | # if using SOMD2 do not submit an array job 66 | if [[ " ${eng_array[*]} " =~ "SOMD2" ]]; then 67 | jidprod=$(sbatch --dependency=afterany:"${jidfep}" --parsable run_production_slurm.sh "${trans_array[i]}" "${eng_array[i]}") 68 | echo "Production jobid for ${trans_array[i]}, ${eng_array[i]} is $jidprod" 69 | else 70 | jidprod=$(sbatch --dependency=afterany:"${jidfep}" --parsable --array=0-$(("${win_array[i]}" - 1)) run_production_slurm.sh "${trans_array[i]}" "${eng_array[i]}" ${win_array[i]} $repeats) 71 | echo "Production jobid for ${trans_array[i]}, ${eng_array[i]} is $jidprod" 72 | fi 73 | jidana=$(sbatch --dependency=afterany:${jidprod} --parsable $scripts_dir/run_analysis_slurm.sh ${trans_array[i]} ${eng_array[i]}) 74 | echo "Analysis jobid for ${trans_array[i]} is $jidana" 75 | done 76 | -------------------------------------------------------------------------------- /04_fep/02_RBFE/scripts/run_analysis_slurm.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | #SBATCH -n 1 3 | #SBATCH --gres=gpu:1 4 | #SBATCH --cpus-per-task=5 5 | #SBATCH --job-name=ana 6 | #SBATCH -o ../slurm_logs/ana_%A.out 7 | #SBATCH -e ../slurm_logs/ana_%A.err 8 | 9 | # sourcing 10 | source $BSS 11 | source $amber 12 | source $gromacs 13 | source $scripts_dir/extract_execution_model_bash.sh 14 | 15 | # cd /home/anna/BioSimSpace 16 | # git checkout feature-amber-pre-2023 17 | # export BSS="/home/anna/anaconda3/bin/activate biosimspace-dev" 18 | # source $BSS 19 | # cd $MAINDIRECTORY 20 | 21 | date 22 | echo "Folder for these runs is : $MAINDIRECTORY" 23 | echo "Analysis for the transformation $1, $2." 24 | 25 | python $scripts_dir/analysis_single_trans.py $1 $2 26 | -------------------------------------------------------------------------------- /04_fep/02_RBFE/scripts/run_fepprep_slurm.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | #SBATCH -n 1 3 | #SBATCH --gres=gpu:1 4 | #SBATCH --job-name=fepprep 5 | #SBATCH -o ../slurm_logs/fepprep_%A_%a.out 6 | #SBATCH -e ../slurm_logs/fepprep_%A_%a.err 7 | 8 | # sourcing 9 | module load cuda/11.6 10 | module load amber/22 11 | module load gromacs/20.4 12 | source $BSS 13 | source extract_execution_model_bash.sh 14 | 15 | date 16 | echo "Folder for these runs is : $MAINDIRECTORY" 17 | echo "Network file is : $net_file" 18 | 19 | trans=${trans_array[$SLURM_ARRAY_TASK_ID]} 20 | eng=${eng_array[$SLURM_ARRAY_TASK_ID]} 21 | win=${win_array[$SLURM_ARRAY_TASK_ID]} 22 | 23 | echo "fepprep for $trans using $eng" 24 | python $scripts_dir/fepprep.py $trans 25 | -------------------------------------------------------------------------------- /04_fep/02_RBFE/scripts/run_ligprep_slurm.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | #SBATCH -n 1 3 | #SBATCH --gres=gpu:1 4 | #SBATCH --job-name=ligprep 5 | #SBATCH -o ../slurm_logs/ligprep_%A_%a.out 6 | #SBATCH -e ../slurm_logs/ligprep_%A_%a.err 7 | 8 | # sourcing 9 | source $BSS 10 | source $amber 11 | source $gromacs 12 | source extract_execution_model_bash.sh 13 | 14 | date 15 | echo "Folder for these runs is : $MAINDIRECTORY" 16 | echo "Ligands file is : $lig_file" 17 | 18 | lig=${lig_array[$SLURM_ARRAY_TASK_ID]} 19 | 20 | echo "prep for $lig..." 21 | python $scripts_dir/ligprep.py $lig 22 | -------------------------------------------------------------------------------- /04_fep/02_RBFE/scripts/run_production_slurm.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | #SBATCH -n 1 3 | #SBATCH --gres=gpu:1 4 | #SBATCH --job-name=prod 5 | #SBATCH -o ../slurm_logs/prod_%A_%a.out 6 | #SBATCH -e ../slurm_logs/prod_%A_%a.err 7 | 8 | # sourcing 9 | module load cuda/11.6 10 | module load gromacs/22.2 11 | somdfreenrg="/export/users/XXX/sire.app/bin/somd-freenrg" 12 | source $BSS 13 | source extract_execution_model_bash.sh 14 | 15 | # keep_traj="None" 16 | # remove any trailing '\r' characters 17 | transformation=$(echo "$1" | tr -d '\r') 18 | engine=$(echo "$2" | tr -d '\r') 19 | num_lambda=$(echo "$3" | tr -d '\r') 20 | date 21 | echo "Folder for these runs is : $MAINDIRECTORY" 22 | echo "The transformation is $transformation using $num_lambda windows and $engine as the MD engine" 23 | 24 | # define no of windows based on sys arg 2 25 | if [ $num_lambda = 11 ]; then 26 | lamvals=( 0.0000 0.1000 0.2000 0.3000 0.4000 0.5000 0.6000 0.7000 0.8000 0.9000 1.0000 ) 27 | fi 28 | if [ $num_lambda = 17 ]; then 29 | lamvals=( 0.0000 0.0625 0.1250 0.1875 0.2500 0.3125 0.3750 0.4375 0.5000 0.5625 0.6250 0.6875 0.7500 0.8125 0.8750 0.9375 1.0000 ) 30 | fi 31 | 32 | # define lambda based on slurm array 33 | lam=${lamvals[SLURM_ARRAY_TASK_ID]} 34 | 35 | # change to the trans dir, abort and message if not there 36 | cd "$MAINDIRECTORY"/outputs/"$engine"/"$transformation" || exit 37 | if [[ ! -d $MAINDIRECTORY/outputs/$engine/$transformation ]]; then 38 | echo "$MAINDIRECTORY/outputs/$engine/$transformation does not exist. Production run aborted..." 39 | exit 1 40 | fi 41 | trans_dir=$(pwd) 42 | 43 | for dir in 'bound' 'free'; do 44 | 45 | if [ "$engine" = "SOMD" ]; then 46 | cd $dir 47 | cd lambda_$lam 48 | $somdfreenrg -c somd.rst7 -t somd.prm7 -m somd.pert -C somd.cfg -p CUDA 49 | 50 | cd $trans_dir 51 | 52 | elif [ "$engine" = "SOMD2" ]; then 53 | cd $dir 54 | somd2 "$transformation".bss --config config.yaml 55 | 56 | cd $trans_dir 57 | 58 | elif [ "$engine" = "GROMACS" ]; then 59 | 60 | cd $dir 61 | 62 | echo "min" 63 | gmx grompp -f min/lambda_$lam/gromacs.mdp -c min/lambda_$lam/initial_gromacs.gro -p min/lambda_$lam/gromacs.top -o min/lambda_$lam/gromacs.tpr 64 | gmx mdrun -ntmpi 1 -deffnm min/lambda_$lam/gromacs 65 | 66 | echo "heat" 67 | gmx grompp -f heat/lambda_$lam/gromacs.mdp -c min/lambda_$lam/gromacs.gro -p heat/lambda_$lam/gromacs.top -o heat/lambda_$lam/gromacs.tpr 68 | gmx mdrun -ntmpi 1 -deffnm heat/lambda_$lam/gromacs 69 | 70 | echo "eq" 71 | gmx grompp -f eq/lambda_$lam/gromacs.mdp -c heat/lambda_$lam/gromacs.gro -p eq/lambda_$lam/gromacs.top -t heat/lambda_$lam/gromacs.cpt -o eq/lambda_$lam/gromacs.tpr 72 | gmx mdrun -ntmpi 1 -deffnm eq/lambda_$lam/gromacs 73 | 74 | echo "prod" 75 | gmx grompp -f lambda_$lam/gromacs.mdp -c eq/lambda_$lam/gromacs.gro -p lambda_$lam/gromacs.top -t eq/lambda_$lam/gromacs.cpt -o lambda_$lam/gromacs.tpr 76 | gmx mdrun -ntmpi 1 -deffnm lambda_$lam/gromacs 77 | 78 | fi 79 | 80 | cd $trans_dir 81 | 82 | done 83 | 84 | echo "done." 85 | -------------------------------------------------------------------------------- /04_fep/03_ABFE/get_tutorial.py: -------------------------------------------------------------------------------- 1 | import requests, os 2 | 3 | links = { 4 | "01": ( 5 | "input.tar.bz2", 6 | "https://openbiosim-my.sharepoint.com/:u:/g/personal/director_openbiosim_org/EZSZkfg60i5OmU1KqvQCSkwBLDu56K9Q_dAreagL3i2IoQ?download=1", 7 | ), 8 | "02": ( 9 | "output.tar.bz2", 10 | "https://openbiosim-my.sharepoint.com/:u:/g/personal/director_openbiosim_org/Eakue8dBxrVBjBj1p6p6r04BnKhgJaynZ7oCx7UXCut_oA?download=1", 11 | ), 12 | "03": ( 13 | "example_output.tar.bz2", 14 | "https://openbiosim-my.sharepoint.com/:u:/g/personal/director_openbiosim_org/EW0ii-odLAJIqz2exgZxSbMB_hgkfRtSBHmkeH6cnANJLQ?download=1", 15 | ), 16 | } 17 | 18 | 19 | def download(link): 20 | localfile, url = links[link] 21 | # Do not download if tarball already found 22 | if os.path.isfile(localfile): 23 | return 24 | print("Downloading %s from openbiosim.org ..." % localfile) 25 | req = requests.get(url, stream=True) 26 | with open(localfile, "wb") as f: 27 | for chunk in req.iter_content(chunk_size=1024): 28 | if chunk: 29 | f.write(chunk) 30 | f.flush() 31 | print("Extracting compressed tarball ...") 32 | os.system("tar -xf %s" % localfile) 33 | # os.system("rm %s" % localfile) 34 | -------------------------------------------------------------------------------- /04_fep/03_ABFE/images/MCS.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenBioSim/biosimspace_tutorials/ac200df769ba3373c9e0e724020aaaa96e340889/04_fep/03_ABFE/images/MCS.png -------------------------------------------------------------------------------- /04_fep/03_ABFE/images/abfe_cycle_details.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenBioSim/biosimspace_tutorials/ac200df769ba3373c9e0e724020aaaa96e340889/04_fep/03_ABFE/images/abfe_cycle_details.png -------------------------------------------------------------------------------- /04_fep/03_ABFE/images/alt_poses.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenBioSim/biosimspace_tutorials/ac200df769ba3373c9e0e724020aaaa96e340889/04_fep/03_ABFE/images/alt_poses.png -------------------------------------------------------------------------------- /04_fep/03_ABFE/images/boresch_dof.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenBioSim/biosimspace_tutorials/ac200df769ba3373c9e0e724020aaaa96e340889/04_fep/03_ABFE/images/boresch_dof.png -------------------------------------------------------------------------------- /04_fep/03_ABFE/images/boresch_restr_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenBioSim/biosimspace_tutorials/ac200df769ba3373c9e0e724020aaaa96e340889/04_fep/03_ABFE/images/boresch_restr_2.png -------------------------------------------------------------------------------- /04_fep/03_ABFE/images/convergence_equil.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenBioSim/biosimspace_tutorials/ac200df769ba3373c9e0e724020aaaa96e340889/04_fep/03_ABFE/images/convergence_equil.png -------------------------------------------------------------------------------- /04_fep/03_ABFE/images/convergence_no_equil.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenBioSim/biosimspace_tutorials/ac200df769ba3373c9e0e724020aaaa96e340889/04_fep/03_ABFE/images/convergence_no_equil.png -------------------------------------------------------------------------------- /04_fep/03_ABFE/images/mif_mif180.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenBioSim/biosimspace_tutorials/ac200df769ba3373c9e0e724020aaaa96e340889/04_fep/03_ABFE/images/mif_mif180.png -------------------------------------------------------------------------------- /04_fep/03_ABFE/images/ngl_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenBioSim/biosimspace_tutorials/ac200df769ba3373c9e0e724020aaaa96e340889/04_fep/03_ABFE/images/ngl_1.png -------------------------------------------------------------------------------- /04_fep/03_ABFE/images/ngl_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenBioSim/biosimspace_tutorials/ac200df769ba3373c9e0e724020aaaa96e340889/04_fep/03_ABFE/images/ngl_2.png -------------------------------------------------------------------------------- /04_fep/03_ABFE/images/ngl_new_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenBioSim/biosimspace_tutorials/ac200df769ba3373c9e0e724020aaaa96e340889/04_fep/03_ABFE/images/ngl_new_1.png -------------------------------------------------------------------------------- /04_fep/03_ABFE/images/ngl_new_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenBioSim/biosimspace_tutorials/ac200df769ba3373c9e0e724020aaaa96e340889/04_fep/03_ABFE/images/ngl_new_2.png -------------------------------------------------------------------------------- /04_fep/03_ABFE/images/ngl_new_3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenBioSim/biosimspace_tutorials/ac200df769ba3373c9e0e724020aaaa96e340889/04_fep/03_ABFE/images/ngl_new_3.png -------------------------------------------------------------------------------- /04_fep/03_ABFE/images/overlap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenBioSim/biosimspace_tutorials/ac200df769ba3373c9e0e724020aaaa96e340889/04_fep/03_ABFE/images/overlap.png -------------------------------------------------------------------------------- /04_fep/03_ABFE/images/pmf_evolution.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenBioSim/biosimspace_tutorials/ac200df769ba3373c9e0e724020aaaa96e340889/04_fep/03_ABFE/images/pmf_evolution.png -------------------------------------------------------------------------------- /04_fep/04_PFEP/images/pfep_tutorial_tcycle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenBioSim/biosimspace_tutorials/ac200df769ba3373c9e0e724020aaaa96e340889/04_fep/04_PFEP/images/pfep_tutorial_tcycle.png -------------------------------------------------------------------------------- /04_fep/05_ATM/get_tutorial.py: -------------------------------------------------------------------------------- 1 | import requests, os 2 | import BioSimSpace as BSS 3 | 4 | links = { 5 | "01": ( 6 | "tyk2_atm.tar.bz2", 7 | os.path.join(BSS.tutorialUrl(), "tyk2_atm.tar.bz2"), 8 | ), 9 | } 10 | 11 | 12 | def download(link): 13 | localfile, url = links[link] 14 | # Do not download if tarball already found 15 | if os.path.isfile(localfile): 16 | return 17 | print("Downloading %s from openbiosim.org ..." % localfile) 18 | req = requests.get(url, stream=True) 19 | with open(localfile, "wb") as f: 20 | for chunk in req.iter_content(chunk_size=1024): 21 | if chunk: 22 | f.write(chunk) 23 | f.flush() 24 | print("Extracting compressed tarball ...") 25 | 26 | os.system("tar -xf %s" % localfile) 27 | # os.system("rm %s" % localfile) 28 | -------------------------------------------------------------------------------- /04_fep/README.md: -------------------------------------------------------------------------------- 1 | # Alchemical free energy Tutorials 2 | 3 | Requirements: 4 | 5 | - BioSimSpace 6 | - GROMACS 7 | 8 | ## 1. Intro to Alchemistry 9 | 10 | Authors: 11 | - [Antonia Mey -- @ppxasjsm](https://github.com/ppxasjsm) 12 | - [Lester Hedges -- @lohedges](https://github.com/lohedges) 13 | - [Finlay Clark -- @fjclark](https://github.com/fjclark) 14 | - [Anna Herz -- @annamherz](https://github.com/annamherz) 15 | 16 | An introduction to the basics of running alchemical free energy calculations in BioSimSpace, including loading and parameterising systems, morphing ligands, running solvation and binding free energy calculations, and analysis. 17 | 18 | ## 2. Relative Binding Free Energy Calculations 19 | 20 | Authors: 21 | - [Jenke Scheen -- @jenkescheen](https://github.com/jenkescheen) 22 | - [Anna Herz -- @annamherz](https://github.com/annamherz) 23 | 24 | An overview for setting up, running, and analysing a perturbation network for alchemical relative binding free energies. This includes a setup and analysis notebook, along with sample scripts for running on a slurm cluster. 25 | ## 03. Absolute Binding Free Energy Calculations 26 | 27 | Authors: 28 | - [Finlay Clark -- @fjclark](https://github.com/fjclark) 29 | 30 | Functionality for running alchemical absolute binding free energy calculations is currently present in the Exscientia Sandpit within BioSimSpace. These notebooks discuss the idea of Sandpits for including experimental features in BioSimSpace, and detail setting up and analysing absolute binding free energy calculations. 31 | 32 | ## 03. Protein Free Energy Calculations 33 | 34 | Authors: 35 | - [Audrius Kalpokas -- @akalpokas](https://github.com/akalpokas) 36 | 37 | An introduction to alchemical protein free energy calculations. Specifically, this includes an overview of BioSimSpace region-of-interest (ROI) functionality and examples of how to setup, view and export alchemical protein systems. 38 | 39 | ## 05. Alchemical Transfer RBFE Calculations 40 | 41 | Authors: 42 | - [Matthew Burman -- @mb2055](https://github.com/mb2055) 43 | 44 | An overview of alchemical transfer functionality within BioSimSpace. Includes methods for setting up ATM-compatible systems, as well as custom protocols for minimisation, equilibration and production. -------------------------------------------------------------------------------- /LIVECOMS/01_introduction/livecoms.tex: -------------------------------------------------------------------------------- 1 | This tutorial consists of five separate notebooks. 2 | \\ 3 | The first notebook \href{https://github.com/OpenBioSim/biosimspace_tutorials/blob/main/01_introduction/01_introduction.ipynb}{01-introduction} describes what functionality is currently available in BioSimSpace; what are the key concepts behind BioSimSpace, such as the use of a Sire system object to describe a generic molecular system; how interoperability between packages is achieved through a library of file converters; what steps are taken to preserve the topology of a molecular system after it has been passed to various third-party tools. 4 | \\ 5 | %\input{LIVECOMS/01_introduction/02_molecular_setup} 6 | The second notebook \href{https://github.com/OpenBioSim/biosimspace_tutorials/blob/main/01_introduction/02_molecular_setup.ipynb}{02-molecularsetup} 7 | provides an example of how to use BioSimSpace to set up a molecular system ready for simulation. Starting from a molecular topology in the form of a Protein Data Bank format file, the notebook teaches how to parameterize molecules using different molecular force fields, then solvate them using various water models before exporting the solvated topologies in a chosen file format. 8 | \\ 9 | %\input{LIVECOMS/01_introduction/03_molecular_dynamics} 10 | The third notebook \href{https://github.com/OpenBioSim/biosimspace_tutorials/blob/main/01_introduction/03_molecular_dynamics.ipynb}{03-molecular-dynamics} describes how to use BioSimSpace to configure and run some basic molecular dynamics simulations. This notebook introduces the concept of \href{https://biosimspace.org/api/index_Protocol.html}{BioSimSpace.Protocol} to codify a shareable, re-usable and extensible simulation protocol. The concept of \href{https://biosimspace.org/api/index_Process.html}{BioSimSpace.Process} 11 | is then introduced to provide functionality for configuring and running processes with several common molecular dynamics engines. The process is then illustrated with code that implements interactive molecular dynamics simulations in the notebook. Finally, \href{https://biosimspace.openbiosim.org/api/index_Trajectory.html}{BioSimSpace.Trajectory} is introduced as a wrapper for the python software MDTraj and MDAnalysis to facilitate trajectory analyzes. 12 | \\ 13 | %\input{LIVECOMS/01_introduction/04_writing_nodes} 14 | The fourth notebook \href{https://github.com/OpenBioSim/biosimspace_tutorials/blob/main/01_introduction/04_writing_nodes.ipynb}{04-writing-nodes} introduces the concept of nodes as interoperable workflow components. Nodes are robust and portable Python scripts that typically do a small, well-defined piece of work. All inputs and outputs from the node are validated and the node is written in such a way that it is independent of the underlying software packages, i.e. the same script can work with a range of different packages. In addition, nodes are aware of the environment in which they are run, so can be used interactively, from the command-line, or within a workflow engine. 15 | \\ 16 | %\input{LIVECOMS/01_introduction/05_running_nodes} 17 | The fifth notebook \href{https://github.com/OpenBioSim/biosimspace_tutorials/blob/main/01_introduction/05_running_nodes.ipynb}{05-running-nodes} describes how BioSimSpace nodes can be exported as regular python scripts and executed in a variety of environments. This allows users to prototype BioSimSpace nodes in an interactive Jupyter notebook, and then deploy the same script without having to insert additional code. BioSimSpace nodes can be currently called from the command line and can also be imported within a BioSimSpace script, enabling the construction of complex workflows by reusing libraries of nodes. Nodes can also generate \href{https://www.commonwl.org}{common workflow language} wrappers, allowing BSS nodes to be plugged into any workflow engine that supports this standard. 18 | \\ 19 | -------------------------------------------------------------------------------- /LIVECOMS/02_funnel_metad/fun-hsp90-analyses.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenBioSim/biosimspace_tutorials/ac200df769ba3373c9e0e724020aaaa96e340889/LIVECOMS/02_funnel_metad/fun-hsp90-analyses.png -------------------------------------------------------------------------------- /LIVECOMS/02_funnel_metad/funmetad-hsp90.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenBioSim/biosimspace_tutorials/ac200df769ba3373c9e0e724020aaaa96e340889/LIVECOMS/02_funnel_metad/funmetad-hsp90.png -------------------------------------------------------------------------------- /LIVECOMS/02_funnel_metad/funmetadfig1.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenBioSim/biosimspace_tutorials/ac200df769ba3373c9e0e724020aaaa96e340889/LIVECOMS/02_funnel_metad/funmetadfig1.jpeg -------------------------------------------------------------------------------- /LIVECOMS/02_funnel_metad/livecoms.tex: -------------------------------------------------------------------------------- 1 | \hypertarget{funnel-metadynamics-tutorial}{% 2 | \label{funnel-metadynamics-tutorial}} 3 | \hypertarget{Introduction}{% 4 | \subsubsection{Introduction}\label{Introduction}} 5 | 6 | Funnel metadynamics (\emph{fun-metaD}) is a molecular dynamics-based method that calculates the absolute binding free energy (ABFE) between a small organic ligand and a protein. It uses the enhanced sampling method metadynamics. To best separate the bound 7 | and unbound phases, as well as increase the rate of convergence of the binding free energy, the exploration of the ligand in 3D space is limited by funnel-shaped 8 | restraints. 9 | 10 | Readers that lack familiarity with metadynamics may wish to study first a simple \href{https://biosimspace.openbiosim.org/tutorials/metadynamics.html}{BioSimSpace metadynamics tutorials} for a alanine dipeptide molecule in the gas phase. 11 | 12 | \emph{fun-metaD} was originally implemented by Limogelli et al.~\cite{Limongelli2013}. This tutorial is based on the implementation described by Rhys et al~\cite{Evans2020}, and Saleh et al~\cite{Saleh2017}. The main difference between the original and later implementations is the functional form of the funnel restraints: the original \emph{fun-metaD} relied on a cone and a cylinder joined to make a funnel using a step function, while the new implementation uses a single 13 | sigmoid function. The Limogelli implementation also requires the protein to be realigned with a reference structure to keep the funnel strictly in place over the binding site, which negatively affects performance. The implementation described here allows the funnel to move with the protein. 14 | 15 | A challenge with using \emph{fun-metaD} for ABFE calculations is that it can be difficult to select optimised funnel parameters, and tedious to write multiple input files for PLUMED. To facilitate use of \emph{fun-metaD} by non-experts BioSimSpace implements automated setup algorithms for selecting funnel parameters, and producing input files. 16 | 17 | By the end of this tutorial, you should understand: 18 | \begin{itemize} 19 | \item The theory that underpins \emph{fun-metaD} calculations. 20 | \item How to set up \emph{fun-metaD} simulations and how to 21 | visualise the funnel restraints. 22 | \item How to analyse the results of a \emph{fun-metaD} simulation. 23 | \end{itemize} 24 | 25 | \hypertarget{the-theory}{% 26 | \subsubsection{Theory}\label{the-theory}} 27 | 28 | Metadynamics is an enhanced sampling method that biases a simulation 29 | along a chosen set of reaction coordinates, or as MetaD practitioners 30 | call them, collective variables (CVs). This bias is deposited at defined 31 | time intervals and takes the shape of a Gaussian potential. 32 | Investigation of drug binding should involve at least one CV, the distance 33 | from the drug molecule to the protein, where the distance between them 34 | can be biased, causing the drug to unbind. However, that single distance 35 | is degenerate, meaning many different configurations of the drug in 3D 36 | space will not be described by that single distance. It also involves 37 | the exploration of a very large volume, hindering convergence. 38 | 39 | \emph{Fun-metaD} gets around both of these problems, restricting the 40 | exploration by using funnel-shaped restraints. The restraints are defined with a 2D coordinates system based on the two CVs - `projection' and `extent'. The first CV measures the distance along the axis-vector defined by the coordinates of the points $P_{0}$ and $P_{X}$ in the laboratory frame. The second CV measures the distance along a second axis-vector orthogonal to the first axis-vector. See Figure \ref{fig:funnel}. 41 | 42 | \begin{figure}[htp] 43 | \includegraphics[width=\linewidth]{LIVECOMS/02_funnel_metad/funmetadfig1.jpeg} 44 | \caption{Visualisation of the funnel restraints defined using the CV projection axis and the CV extent axis.} 45 | \label{fig:funnel} 46 | \end{figure} 47 | 48 | The funnel parameters $h$ and $f$ controls the maximal and minimal width of the funnel along the extent axis respectively. The smoothness of the transition between maximal and minimal widths along the projection axis is controlled by the parameters $b$ and $x$ that together control the location and steepness of the inflection point located at $S_{cent}$ in fig \ref{fig:funnel}. All together these parameters define the radius $S$ of the funnel at a given distance $i$ along the projection axis. 49 | 50 | \begin{equation} 51 | S(i) = h\left(\frac{1}{1+b^{b(i-x)}}\right) + f, 52 | \end{equation} 53 | 54 | Clearly, there is still some degeneracy in the CVs - the plane 55 | perpendicular to the projection axis is a one-dimensional representation of a two-dimensional space. However, this is a good compromise between having sufficient accuracy for describing the binding of a ligand and the tolerable simulation slowdown of using only two CVs. 56 | 57 | In order to have a good separation between the bound and unbound phases for the ABFE estimations, the funnel needs to point \emph{out}, with the narrow end in the solvent, 58 | and away from any protein residues. The BSS automated funnel assignment code does this generally well. Typically the vectors picked for defining the $P_{0}$ and $P_{1}$ points offer an unobstructed exit path for the ligand. It is nonetheless still a good idea to visually check the proposed funnel, especially for new protein systems. This can be done through use of the funnel visualisation functionality within BSS (see below). 59 | 60 | The size of the funnel radius has to be determined on a case-by-case basis. The metadynamics code tracks the center of mass the ligand, therefore ligand atoms are able to sample positions outside the visualised funnel, provided the center of mass remains inside the funnel. the volume that the small molecule will explore is much larger than implied by the visualization of the funnel. There is usually only one binding site and the funnel should enclose only it, excluding other protein features, by setting a small value of $h$. This helps accelerate convergence by preventing the ligand from exploring irrelevant regions in the free energy surface (FES). 61 | 62 | \hypertarget{setupfunmetad}{% 63 | \subsubsection{Setting up funnel metadynamics simulations}\label{setupfunmetad}} 64 | 65 | The first tutorial notebook \href{https://github.com/OpenBioSim/biosimspace_tutorials/blob/main/02_funnel_metad/01_bss-fun-metad-setup.ipynb}{01-bss-fun-metad-setup} shows how to set up a BioSimSpace system, parameterizing the protein and the ligand, as well as defining the simulation box, adding water and ions. A funnel is then defined and visualized using NGLview. Finally, directories for the \emph{fun-metaD} simulation are setup and a short 10 ps simulation is run for illustrative purposes. 66 | 67 | 68 | \begin{figure}[htp] 69 | \includegraphics[width=\linewidth]{LIVECOMS/02_funnel_metad/funmetad-hsp90.png} 70 | \caption{Visualisation of funnel restraints autogenerated by BioSimSpace for a HSP90 protein-ligand system.} 71 | \label{fig:fun-hsp90} 72 | \end{figure} 73 | 74 | The notebook uses as example a fully solvated HSP90 protein-ligand system originally setup from PDBID:2WI2. 75 | Figure \ref{fig:fun-hsp90} depicts a rendered funnel overlayed on the HSP90 protein-ligand system in the Jupyter Notebook. The funnel vector is clearly pointing out into the solvent, with no protein residues blocking the way. The default radius is sufficient to encompass the binding site, excluding the rest of the protein. 76 | 77 | 78 | To obtain converged free energies we recommend simulations of the order of 500 - 2000 ns sampling time. This is best done out of a notebook. For convenience we also \href{https://github.com/OpenBioSim/biosimspace_tutorials/tree/main/02_funnel_metad/example_nodes}{provide with this tutorial} as companion resource an LSF submission script that re-implements the notebook functionality in a set of BioSimSpace nodes. 79 | 80 | \hypertarget{analysis}{% 81 | \subsubsection{Analysing funnel metadynamics simulations}\label{analysis}} 82 | 83 | The second notebook of this tutorial \href{https://github.com/OpenBioSim/biosimspace_tutorials/blob/main/02_funnel_metad/02_bss-fun-metad-analysis.ipynb}{02-bss-fun-metad-analysis} describes how to analyze a funnel metadynamics simulation. The precision of the ABFE estimate derived from a funnel metadynamics run is linked to the convergence of the free energy profile along the collective variables that define the funnel. The notebook uses provided sample trajectories to show how the range of CV values sampled during a trajectory and two-dimensional free energy surfaces can be plotted with the help of \href{https://biosimspace.openbiosim.org/api/index_Notebook.html}{BSS.Notebook}. The notebook also shows how to compute funnel correction terms using BSS, and how to perform convergence analysis of the absolute binding free energies for different trajectories. Selected visualizations generated by the notebook are depicted in Figure~\ref{fig:fun-hsp90-analyses}. 84 | 85 | \begin{figure}[htp] 86 | \includegraphics[width=\linewidth]{LIVECOMS/02_funnel_metad/fun-hsp90-analyses.png} 87 | \caption{Selected analyses of funnel metadynamics simulations of the HSP90 protein-ligand system. A) Reconstructed two-dimensional free energy profile from a sample 100 ns trajectory. B) Convergence plots for five replicates of a 100-ns trajectory. The bold black line denotes the mean of the replicates, and the dashed red line the experimental estimate of the absolute binding free energy of the ligand.} 88 | \label{fig:fun-hsp90-analyses} 89 | \end{figure} 90 | 91 | -------------------------------------------------------------------------------- /LIVECOMS/03_steered_md/COLVAR_all.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenBioSim/biosimspace_tutorials/ac200df769ba3373c9e0e724020aaaa96e340889/LIVECOMS/03_steered_md/COLVAR_all.png -------------------------------------------------------------------------------- /LIVECOMS/03_steered_md/ensemble-md-protocol.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenBioSim/biosimspace_tutorials/ac200df769ba3373c9e0e724020aaaa96e340889/LIVECOMS/03_steered_md/ensemble-md-protocol.png -------------------------------------------------------------------------------- /LIVECOMS/03_steered_md/livecoms.tex: -------------------------------------------------------------------------------- 1 | \subsubsection{Introduction} 2 | Many relevant biological processes, such as transmembrane permeation or transitions between active and inactive protein conformations, occur on a timescale of microseconds to seconds ~\cite{Zwier2010,Choy2017,Wells2007}. However, even with GPU acceleration, the timescales accessible via MD simulations are only a few hundred ns/day ~\cite{HecBioSim_benchmark}. One of the methods to get around this limitation is steered molecular dynamics (sMD). sMD involves applying a harmonic restraint to bias the system towards a conformation defined through one or more collective variables (CVs): 3 | 4 | \begin{equation} 5 | V(\vec{s},t) = \frac{1}{2} \kappa(t) ( \vec{s} - \vec{s}_0(t) )^2, 6 | \label{eq:sMD} 7 | \end{equation} 8 | 9 | where $\kappa$ is the force constant, $\vec{s}_0$ is the expected CV value at a specific timestep, and $\vec{s}$ is the actual CV value at that timestep \cite{Isralewitz2001,Tribello2014}. 10 | 11 | This section of the tutorial summarizes how to use BioSimSpace to set up and run sMD simulations. BSS prepares input files for PLUMED, which is the software that works together with MD engines such as AMBER and GROMACS to add the restraint in eq~\ref{eq:sMD}. 12 | 13 | We use protein tyrosine phosphatase 1B (PTP1B) as the system of choice for this tutorial. It is a negative regulator of insulin signalling ~\cite{sMD_ptp1b-diabetes}, and is an attractive target for type II diabetes ~\cite{sMD_Wiesman}. The function of PTP1B depends on the conformation of its WPD loop, which can be closed (active) or open (inactive) (Figure~\ref{fig:ptp1b}). The WPD loop of PTP1B opens and closes on a $\mu$s timescale ~\cite{Choy2017}, and therefore this transition is not observed on conventional computational timescales. 14 | 15 | \begin{figure}[htp] 16 | \includegraphics[width=\linewidth]{LIVECOMS/03_steered_md/open-close.png} 17 | \caption{The WPD loop of PTP1B, in two conformations: open (yellow, PDB ID: 2HNP) and closed (red, PDB ID: 1SUG).} 18 | \label{fig:ptp1b} 19 | \end{figure} 20 | 21 | \subsubsection{Running sMD using BioSimSpace} 22 | 23 | The first notebook of this tutorial \href{https://github.com/OpenBioSim/biosimspace_tutorials/blob/main/03_steered_md/01_setup_sMD.ipynb}{01-setup-sMD} describes how to set up a steered MD simulation with BioSimSpace. 24 | The notebook illustrates the use of \href{https://biosimspace.openbiosim.org/api/generated/BioSimSpace.Metadynamics.CollectiveVariable.RMSD.html#BioSimSpace.Metadynamics.CollectiveVariable.RMSD}{BSS.Metadynamics.CollectiveVariable.RMSD} to define a collective variable that enforces a conformational change of the 'WPD' loop in the enzyme PTP1B. 25 | Next \href{https://biosimspace.openbiosim.org/api/generated/BioSimSpace.Protocol.Steering.html#BioSimSpace.Protocol.Steering}{BSS.Protocol.Steering} is used to specify a steering schedule alongside the RMSD CV. The notebook illustrates how input files for the \emph{gmx}, \emph{sander} or \emph{pmemd} MD engines can be subsequently prepared. The notebook also shows how a more complex steering schedule that combines multiple CVs can be written. 26 | 27 | For production simulations, we recommend long sMD simulations to minimize the strength of the bias that needs to be applied to enforce the desired conformational change by the end of the steered MD simulation. Optimizing the steered MD schedule parameters requires trial and error. 28 | For convenience, we provide simple Python scripts with a command line interface to execute steered MD runs and scan schedule parameters for two specified sMD protocols (\href{https://github.com/OpenBioSim/biosimspace_tutorials/blob/main/03_steered_md/scripts/sMD_simple.py}{a single CV} and a \href{https://github.com/OpenBioSim/biosimspace_tutorials/blob/main/03_steered_md/scripts/sMD_multiCV.py}{multiple CV} example). We also provide sample \href{https://github.com/OpenBioSim/biosimspace_tutorials/blob/main/03_steered_md/scripts/sMD_slurm.sh}{slurm} and \href{https://github.com/OpenBioSim/biosimspace_tutorials/blob/main/03_steered_md/scripts/sMD_LSF.sh}{LSF} submission scripts to deploy the BSS steered MD scripts in different HPC environments. 29 | 30 | \subsubsection{sMD trajectory analysis} 31 | 32 | The second notebook of this tutorial \href{https://github.com/OpenBioSim/biosimspace_tutorials/blob/main/03_steered_md/02_trajectory_analysis.ipynb}{02-trajectory-analysis} describes how to analyze data generated by a steered MD run. As the sMD simulation is run, the CV values are saved to a \textbf{COLVAR} file. It can be plotted to assess whether the sMD simulation has been successful. An example is shown in Figure \ref{fig:rmsd}. 33 | 34 | \begin{figure}[htp] 35 | \centering 36 | \includegraphics[width=\linewidth]{LIVECOMS/03_steered_md/COLVAR_all.png} 37 | \caption{Evolution of Collective Variables throughout an sMD simulation. A) RMSD CV measuring distance to closed WPD loop conformation. B) Torsional angle CV measuring the Chi1 angle of residue Tyr 152. C) CV measuring the distance between C$_{\gamma}$ atoms in residues Phe196 and Phe280. As the simulation progresses, the WPD loop RMSD is gradually lower (i.e. the loop is adopting a closed loop conformation).} 38 | \label{fig:rmsd} 39 | \end{figure} 40 | 41 | The notebook also illustrates a "failed" steered MD trajectory, where the steering duration and force were insufficient to reach the target CV value. 42 | 43 | \subsubsection{Example application - combining steered MD with Markov State Modeling} 44 | While the information provided here focuses on running sMD simulations with BSS, there are multiple potential applications, such as studying membrane permeability~\cite{Wells2007} or ligand residence time\cite{Potterton2019}. Here we briefly highlight one application of sMD simulations enabled by BioSimSpace in the AMMo software project. AMMo ("Allostery in Markov Models") was developed to evaluate the allosteric effects of protein mutations or ligand binding by combining sMD with Markov State Models (MSMs). MSMs are used to give the probability of protein conformations and therefore can be used to model how a ligand affects the conformation ensemble of a target (e.g. whether the presence of a ligand decreases the active state probability and therefore is an allosteric inhibitor). 45 | There is a lot to consider when building MSMs, and the method is not covered in this tutorial. AMMo uses the Python library \href{http://emma-project.org/latest/}{PyEMMA} was to implement MSMs. Extensive examples and documentation for PyEMMA are available elsewhere ~\cite{Wehmeyer_2019}. The integration of sMD with MSM in this allosteric modulation prediction workflow is illustrated in Figure \ref{fig:ensemble-protocol}. The notebook \href{https://github.com/OpenBioSim/BioSimSpaceTutorials/blob/main/03_steered_md/02_trajectory_analysis.ipynb}{02-trajectory-analysis} shows how a sMD trajectory can be sampled to extract a range of protein conformations suitable for inputs to an MSM workflow. Hardie et al. report a detailed study of allosteric modulators of PTP1B using this sMD/MSM methodology ~\cite{Hardie2023} and notebooks for the PTP1B case study are available on the \href{https://github.com/michellab/AMMo/tree/main/examples/ptp1b}{GitHub} page for AMMo. 46 | 47 | \begin{figure}[htp] 48 | \includegraphics[width=\linewidth]{LIVECOMS/03_steered_md/ensemble-md-protocol.png} 49 | \caption{The steps used for enhanced sampling methods to gather data for statistical analysis of protein conformation ensemble. (1) Run steered MD along some collective variable (CV); (2) Extract snapshots that evenly sample available conformational space; (3) Run equilibrium MD simulations using extracted coordinates as seeds; (4) construct an MSM using trajectory data from step 3.} 50 | \label{fig:ensemble-protocol} 51 | \end{figure} -------------------------------------------------------------------------------- /LIVECOMS/03_steered_md/msm-final.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenBioSim/biosimspace_tutorials/ac200df769ba3373c9e0e724020aaaa96e340889/LIVECOMS/03_steered_md/msm-final.png -------------------------------------------------------------------------------- /LIVECOMS/03_steered_md/open-close.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenBioSim/biosimspace_tutorials/ac200df769ba3373c9e0e724020aaaa96e340889/LIVECOMS/03_steered_md/open-close.png -------------------------------------------------------------------------------- /LIVECOMS/04_fep/abfe-tutorial.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenBioSim/biosimspace_tutorials/ac200df769ba3373c9e0e724020aaaa96e340889/LIVECOMS/04_fep/abfe-tutorial.png -------------------------------------------------------------------------------- /LIVECOMS/04_fep/alignment_visualisation.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenBioSim/biosimspace_tutorials/ac200df769ba3373c9e0e724020aaaa96e340889/LIVECOMS/04_fep/alignment_visualisation.png -------------------------------------------------------------------------------- /LIVECOMS/04_fep/introfep.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenBioSim/biosimspace_tutorials/ac200df769ba3373c9e0e724020aaaa96e340889/LIVECOMS/04_fep/introfep.png -------------------------------------------------------------------------------- /LIVECOMS/04_fep/introfep_updated.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenBioSim/biosimspace_tutorials/ac200df769ba3373c9e0e724020aaaa96e340889/LIVECOMS/04_fep/introfep_updated.png -------------------------------------------------------------------------------- /LIVECOMS/04_fep/pfep-tutorial_tcycle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenBioSim/biosimspace_tutorials/ac200df769ba3373c9e0e724020aaaa96e340889/LIVECOMS/04_fep/pfep-tutorial_tcycle.png -------------------------------------------------------------------------------- /LIVECOMS/04_fep/rbfe-analysis.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenBioSim/biosimspace_tutorials/ac200df769ba3373c9e0e724020aaaa96e340889/LIVECOMS/04_fep/rbfe-analysis.png -------------------------------------------------------------------------------- /LIVECOMS/04_fep/rbfe-setup.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenBioSim/biosimspace_tutorials/ac200df769ba3373c9e0e724020aaaa96e340889/LIVECOMS/04_fep/rbfe-setup.png -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # BioSimSpace tutorials 2 | 3 | A suite of tutorials that provide an introduction to BioSimSpace, and examples of advanced functionality 4 | covering different scientific use cases. 5 | These tutorials have been tested with BioSimSpace 2023.5.0 on a linux-64 platform and require the following dependencies 6 | 7 | * Gromacs (tested with 2023.1) 8 | * AmberTools (tested with 23.3) 9 | * PLUMED (tested with 2.9.0) 10 | * cinnabar (tested with 0.3.0) 11 | * alchemlyb (tested with 1.0.1) 12 | 13 | # Installation instructions 14 | 15 | ## Option 1) Local installation using conda 16 | 17 | This route can be used to install the tutorials on a computer. 18 | 19 | We recommend using mamba to install the dependencies. 20 | 21 | ``` 22 | conda create -n bsstutorials "python<3.11" 23 | conda install -c conda-forge mamba 24 | mamba install -n bsstutorials -c openbiosim -c conda-forge biosimspace=2023.5.0 gromacs=2023.1 ambertools=23.3 plumed=2.9.0 cinnabar=0.3.0 pymbar=3 alchemlyb=1.0.1 25 | mamba activate bsstutorials 26 | ``` 27 | 28 | Next clone the repository 29 | 30 | ``` 31 | git clone https://github.com/OpenBioSim/biosimspace_tutorials 32 | ``` 33 | 34 | ## Option 2) Use OpenBioSim's Jupyter Hub server 35 | 36 | [OpenBioSim](https://www.openbiosim.org/) provides access to a Jupyter Hub server with a preinstalled python environment to run the tutorials. Use this route to run the tutorials from a compatible web-browser. Access to the server require a GitHub account. 37 | 38 | Go to [try.openbiosim.org](https://try.openbiosim.org/) 39 | 40 | And follow the instructions in the file TUTORIALS.txt. 41 | 42 | # Tutorials suite 43 | 44 | * 01 - [Introduction](01_introduction) 45 | * 02 - [Funnel metadynamics](02_funnel_metad) 46 | * 03 - [Steered molecular dynamics and ensemble building](03_steered_md) 47 | * 04 - [Alchemical free energy calculations](04_fep) 48 | -------------------------------------------------------------------------------- /Style_guide.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "id": "06155887", 6 | "metadata": {}, 7 | "source": [ 8 | "# Title of notebook" 9 | ] 10 | }, 11 | { 12 | "cell_type": "markdown", 13 | "id": "ded0a015", 14 | "metadata": {}, 15 | "source": [ 16 | "Some description\n", 17 | "\n", 18 | "**Reading Time:**\n", 19 | "~ 30 mins" 20 | ] 21 | }, 22 | { 23 | "cell_type": "markdown", 24 | "id": "7c556056", 25 | "metadata": {}, 26 | "source": [ 27 | "### Maintainers\n", 28 | "- maintainer name -- email (github handle)\n", 29 | "\n", 30 | "### Prerequisites\n", 31 | "\n", 32 | "- List prerequisites of this particular notebook\n", 33 | "\n", 34 | "### Learning Objectives\n", 35 | "\n", 36 | "- List learning objectives\n", 37 | "\n", 38 | "### TOC\n", 39 | "- [first item in toc](#link)\n", 40 | "- [second item in toc](#link2)\n", 41 | "\n", 42 | "### Further reading for this topic\n", 43 | "- give links here\n", 44 | "\n", 45 | "**Jupyter Cheat Sheet**\n", 46 | "- To run the currently highlighted cell and move focus to the next cell, hold ⇧ Shift and press ⏎ Enter;\n", 47 | "- To run the currently highlighted cell and keep focus in the same cell, hold ⇧ Ctrl and press ⏎ Enter;\n", 48 | "- To get help for a specific function, place the cursor within the function's brackets, hold ⇧ Shift, and press ⇥ Tab;\n", 49 | "\n", 50 | "### Link to documentation:\n", 51 | "You can find the full documentation at [biosimspace.org](https://biosimspace.org)." 52 | ] 53 | }, 54 | { 55 | "cell_type": "markdown", 56 | "id": "cd5006e9", 57 | "metadata": {}, 58 | "source": [ 59 | "## 1 Use Level 2 heading for section headings\n", 60 | "### 1.1 Use Level 3 heading for subsection headings" 61 | ] 62 | }, 63 | { 64 | "cell_type": "markdown", 65 | "id": "aea5a64d", 66 | "metadata": {}, 67 | "source": [ 68 | "## Exercises:\n", 69 | "\n", 70 | "Exercises are announced using an alert alert-success box in this way:\n", 71 | "
\n", 72 | "Exercise 5.2: Write a function that computes bond lengths:\n", 73 | "
\n", 74 | "and followed by an incomplete cell. All exercises should be numbered. \n", 75 | "Missing parts are indicated by:\n", 76 | "\n", 77 | "```python\n", 78 | "#FIXME\n", 79 | "```\n", 80 | "\n", 81 | "Solutions to exercises are given in the cell below using the following details tag that can be used for expansion using the standardised text to do so:\n", 82 | "\n", 83 | "
Click here to see solution to Exercise. \n", 84 | " \n", 85 | "```python\n", 86 | "from math import sqrt\n", 87 | "```\n", 88 | " \n", 89 | " " 90 | ] 91 | }, 92 | { 93 | "cell_type": "markdown", 94 | "id": "afe47f49", 95 | "metadata": {}, 96 | "source": [ 97 | "
\n", 98 | "Exercise 5.3: Extra exercises are given in yellow (Extra)\n", 99 | "
" 100 | ] 101 | }, 102 | { 103 | "cell_type": "markdown", 104 | "id": "4336102f", 105 | "metadata": {}, 106 | "source": [ 107 | "## Additional information noteworthy things\n", 108 | "\n", 109 | "
\n", 110 | "Anything noteworthy should be added to an info box. You can use the I am code tags to write code here\n", 111 | "
\n", 112 | "\n", 113 | "To give extra attention use the exclamation mark warning sign: ⚠️" 114 | ] 115 | }, 116 | { 117 | "cell_type": "markdown", 118 | "id": "9979b807", 119 | "metadata": {}, 120 | "source": [ 121 | "### Some formatting:\n", 122 | "\n", 123 | "- All equations should be type set using LaTex, i.e. $\\Delta \\Delta G$ and not ΔΔG. \n", 124 | "- BSS packages should be highlighted in code, i.e. `BSS.Gateway`. \n", 125 | "- Common abbreviations could be highlighted either in the readme or the first notebook. " 126 | ] 127 | }, 128 | { 129 | "cell_type": "markdown", 130 | "id": "e4c4a542", 131 | "metadata": {}, 132 | "source": [ 133 | "## Structure of each Tutorial directory:" 134 | ] 135 | }, 136 | { 137 | "cell_type": "markdown", 138 | "id": "c3186607", 139 | "metadata": {}, 140 | "source": [ 141 | "`01_README.md`:\n", 142 | "This file should contain the overview of the tutorial and all additional information not shown in the tutorial pdf. In fact I would move a lot of the tutorial pdf here as it it will be an easier place to edit and will be tightly connected with the tutorial, then there are no issues around formatting and one can easily refer to the markdown file. \n", 143 | "\n", 144 | "Sections are:\n", 145 | "#### Author's:\n", 146 | "- This should list all authors and their contributions\n", 147 | "\n", 148 | "#### Overview of tutorial:\n", 149 | "This is the general introduction, there can be some duplication with the jupyter notebook if helpful, but the focus here should be more on background. \n", 150 | "\n", 151 | "#### Specific installation/environment package requirement for this given tutorial:\n", 152 | "Would here be a good place to put this?\n", 153 | "\n", 154 | "#### Changelog:\n", 155 | "I think it would be useful to have a changelog in the readme itself, or linking to a changelog for each tutorial itself so that there is a well kept record of who changed what. This should acknowledge authors so it is easy to track contributions. \n", 156 | "\n", 157 | "`02_first_notebook.ipynb`\n", 158 | "This will be the first notebook complying with the above suggested formatting. \n", 159 | "\n", 160 | "`0X_nth-notebook`\n", 161 | "This will be the xth notebook complying with the above suggested formatting. \n", 162 | "\n", 163 | "\n", 164 | "`directories`:\n", 165 | "This will be harder to standardise. It should contain the minimum of input and output that is required for the tutorial. There may be additional directories, such as one for images--I would prefer the name image over the name figures--as not all images will be figures. " 166 | ] 167 | } 168 | ], 169 | "metadata": { 170 | "kernelspec": { 171 | "display_name": "Python 3.9.13 ('working': conda)", 172 | "language": "python", 173 | "name": "python3" 174 | }, 175 | "language_info": { 176 | "codemirror_mode": { 177 | "name": "ipython", 178 | "version": 3 179 | }, 180 | "file_extension": ".py", 181 | "mimetype": "text/x-python", 182 | "name": "python", 183 | "nbconvert_exporter": "python", 184 | "pygments_lexer": "ipython3" 185 | }, 186 | "vscode": { 187 | "interpreter": { 188 | "hash": "53a7075aa6ba074ad24f268d28330e8ae9ce5d32ba0a76f329c5ee8540da26d4" 189 | } 190 | } 191 | }, 192 | "nbformat": 4, 193 | "nbformat_minor": 5 194 | } 195 | -------------------------------------------------------------------------------- /releases/LiveCoMS_Article_v1.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenBioSim/biosimspace_tutorials/ac200df769ba3373c9e0e724020aaaa96e340889/releases/LiveCoMS_Article_v1.pdf -------------------------------------------------------------------------------- /releases/header_v1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenBioSim/biosimspace_tutorials/ac200df769ba3373c9e0e724020aaaa96e340889/releases/header_v1.jpg --------------------------------------------------------------------------------