├── .gitignore ├── README.md ├── analyse.py ├── analyse.svg ├── data ├── Mpro-x0387_0.mol ├── Mpro-x0387_0.pdb ├── Mpro-x0387_0_apo-desolv.pdb ├── Mpro-x0397_0.mol ├── Mpro-x0397_0_apo-desolv.pdb ├── Mpro-x0874_0.mol ├── Mpro-x0874_0_apo-desolv.pdb ├── Mpro-x1093_0.mol └── Mpro-x1093_0_apo-desolv.pdb ├── environment.yml ├── ligand1.mol ├── ligand1.pdb ├── ligand1.sdf ├── prepareComplex.py ├── prepareProtein.py ├── protein.pdb ├── protein_orig.pdb ├── simulateComplex.py ├── simulateProtein.py └── utils.py /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | complex*.pdb 3 | minimised*.pdb 4 | output* 5 | ligand_prepared.pdb 6 | protein_prepared.pdb 7 | Mpro-x* -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # OpenMM simulation example 2 | 3 | This repo is an effort to work out how to run a simulation of a protein-ligand complex with OpenMM. 4 | 5 | What I'm looking for is for this to be as simple as possible: 6 | 1. read protein from PDB file 7 | 2. read ligand ideally from molfile or SDF 8 | 3. combine the protein and ligand into a complex 9 | 4. parameterise the ligand using GAFF 10 | 5. simulate 11 | 6. analyse 12 | 13 | You might also want to look at this detailed example from openforcefields: 14 | https://github.com/openforcefield/openff-toolkit/tree/stable/examples/toolkit_showcase. 15 | It is a more elegant approach as it would allow to prepare a system for multiple MM toolkits (Gromacs, Amber etc.) 16 | However, I can't get it to work for a number of reasons, so the examples here stick to using mostly OpenMM tooling. 17 | 18 | Many thanks to @jchodera and others in the OpenMM community for help in putting these scripts together. 19 | 20 | ## Versions and branches 21 | 22 | OpenMM and its related tools change quite a bit over time. The original work here was based on OpenMM version 7.4, 23 | and the tools have now been updated for newer versions. Because the code is somewhat version dependent I have created 24 | branches for specific versions. The code on master branch will typically be for the latest version I have used (not 25 | always the latest version of OpenMM). 26 | 27 | ### History 28 | 29 | #### 2020 30 | * initial version based on OpenMM 7.4 31 | * main tools are for simulating a ligand-protein complex with and without solvent 32 | 33 | #### Sep 2023 34 | * updated for OpenMM 7.7 35 | * combined `simulateComplex.py` and `simulateComplexWithSolvent.py` into a single script 36 | * better handling of commandline options (using argparse) 37 | * many more options specifiable 38 | 39 | ## simulateProtein.py: Basic Protein Simulation 40 | 41 | ``` 42 | python simulateProtein.py protein.pdb 43 | ``` 44 | 45 | Minimal example of setting up a protein and doing a simple minimisation. 46 | 47 | Not much to say about this. 48 | 49 | ## simulateComplex.py: Protein-ligand Complex Simulation 50 | 51 | How to set up a protein-ligand complex simulation. 52 | This script replaces the original one and `simulateComplexWithSolvent.py` and provides a single script that assembles 53 | a protein and a ligand and optionally allows to solvate the system. 54 | 55 | Options: 56 | ``` 57 | $ python simulateComplex.py -h 58 | usage: simulateComplex.py [-h] -p PROTEIN -l LIGAND [-o OUTPUT] [-s STEPS] [-z STEP_SIZE] [-f FRICTION_COEFF] [-i INTERVAL] [-t TEMPERATURE] [--solvate] [--padding PADDING] 59 | [--water-model {tip3p,spce,tip4pew,tip5p,swm4ndp}] [--positive-ion POSITIVE_ION] [--negative-ion NEGATIVE_ION] [--ionic-strength IONIC_STRENGTH] [--no-neutralize] [-e EQUILIBRATION_STEPS] 60 | [--protein-force-field PROTEIN_FORCE_FIELD] [--ligand-force-field LIGAND_FORCE_FIELD] [--water-force-field WATER_FORCE_FIELD] 61 | 62 | simulateComplexWithSolvent 63 | 64 | options: 65 | -h, --help show this help message and exit 66 | -p PROTEIN, --protein PROTEIN 67 | Protein PDB file 68 | -l LIGAND, --ligand LIGAND 69 | Ligand molfile 70 | -o OUTPUT, --output OUTPUT 71 | Base name for output files 72 | -s STEPS, --steps STEPS 73 | Number of steps 74 | -z STEP_SIZE, --step-size STEP_SIZE 75 | Step size (ps 76 | -f FRICTION_COEFF, --friction-coeff FRICTION_COEFF 77 | Friction coefficient (ps) 78 | -i INTERVAL, --interval INTERVAL 79 | Reporting interval 80 | -t TEMPERATURE, --temperature TEMPERATURE 81 | Temperature (K) 82 | --solvate Add solvent box 83 | --padding PADDING Padding for solvent box (A) 84 | --water-model {tip3p,spce,tip4pew,tip5p,swm4ndp} 85 | Water model for solvation 86 | --positive-ion POSITIVE_ION 87 | Positive ion for solvation 88 | --negative-ion NEGATIVE_ION 89 | Negative ion for solvation 90 | --ionic-strength IONIC_STRENGTH 91 | Ionic strength for solvation 92 | --no-neutralize Don't add ions to neutralize 93 | -e EQUILIBRATION_STEPS, --equilibration-steps EQUILIBRATION_STEPS 94 | Number of equilibration steps 95 | --protein-force-field PROTEIN_FORCE_FIELD 96 | Protein force field 97 | --ligand-force-field LIGAND_FORCE_FIELD 98 | Ligand force field 99 | --water-force-field WATER_FORCE_FIELD 100 | Ligand force field 101 | ``` 102 | 103 | Many of the options are related to the solvation and are not needed unless you use the `--solvate` option. Most options 104 | have sensible defaults. 105 | 106 | The protein is read in PDB format and added to a Modeller object. 107 | The ligand is then added to the Modeller to generate the complex. 108 | If `--solvate` is specified a solvent box is added. 109 | The system is then prepared using the appropriate force fields, the complex minimised, then equilibrated and finally 110 | the MD simulation is run. 111 | 112 | Try the simulation without solvation as: 113 | 114 | ``` 115 | $ python simulateComplex.py -p protein.pdb -l ligand1.mol 116 | simulateComplexWithSolvent: Namespace(protein='protein.pdb', ligand='ligand1.mol', output='output', steps=5000, step_size=0.002, friction_coeff=1, interval=1000, temperature=300, solvate=False, padding=10, water_model='tip3p', positive_ion='Na+', negative_ion='Cl-', ionic_strength=0.0, no_neutralize=False, equilibration_steps=200, protein_force_field='amber/ff14SB.xml', ligand_force_field='gaff-2.11', water_force_field='amber/tip3p_standard.xml') 117 | Processing protein.pdb and ligand1.mol with 5000 steps generating outputs output_complex.pdb output_minimised.pdb output_traj.dcd 118 | Using platform CUDA 119 | Set precision for platform CUDA to mixed 120 | Reading ligand 121 | Preparing system 122 | Reading protein 123 | Preparing complex 124 | System has 4645 atoms 125 | Adding ligand... 126 | System has 4666 atoms 127 | Simulating for 0.01 ns 128 | No Periodic Box 129 | Minimising ... 130 | Equilibrating ... 131 | Starting simulation with 5000 steps ... 132 | #"Step","Potential Energy (kJ/mole)","Temperature (K)" 133 | 5000,-19906.630997571585,297.9683027771655 134 | Simulation complete in 0.02 mins at 300 K. Total wall clock time was 0.106 mins 135 | Simulation time was 0.01 ns 136 | ``` 137 | 138 | The files `output_complex.pdb`, `output_minimised.pdb` and `output_traj.dcd` are generated. 139 | The first is the complex, the second is that complex mimimised ready for simulation, the third the MD trajectory in DCD 140 | format. 141 | 142 | See the code for details and gotchas. 143 | 144 | To run with solvation use something like this: 145 | 146 | ``` 147 | $ python simulateComplex.py -p protein.pdb -l ligand1.mol --solvate 148 | simulateComplexWithSolvent: Namespace(protein='protein.pdb', ligand='ligand1.mol', output='output', steps=5000, step_size=0.002, friction_coeff=1, interval=1000, temperature=300, solvate=True, padding=10, water_model='tip3p', positive_ion='Na+', negative_ion='Cl-', ionic_strength=0.0, no_neutralize=False, equilibration_steps=200, protein_force_field='amber/ff14SB.xml', ligand_force_field='gaff-2.11', water_force_field='amber/tip3p_standard.xml') 149 | Processing protein.pdb and ligand1.mol with 5000 steps generating outputs output_complex.pdb output_minimised.pdb output_traj.dcd 150 | Using platform CUDA 151 | Set precision for platform CUDA to mixed 152 | Reading ligand 153 | Preparing system 154 | Reading protein 155 | Preparing complex 156 | System has 4645 atoms 157 | Adding ligand... 158 | System has 4666 atoms 159 | Adding solvent... 160 | System has 58052 atoms 161 | Simulating for 0.01 ns 162 | Default Periodic box: [Quantity(value=Vec3(x=8.481300000000001, y=0.0, z=0.0), unit=nanometer), Quantity(value=Vec3(x=0.0, y=8.481300000000001, z=0.0), unit=nanometer), Quantity(value=Vec3(x=0.0, y=0.0, z=8.481300000000001), unit=nanometer)] 163 | Minimising ... 164 | Equilibrating ... 165 | Starting simulation with 5000 steps ... 166 | #"Step","Potential Energy (kJ/mole)","Temperature (K)" 167 | 5000,-742769.3383788001,301.45460839045137 168 | Simulation complete in 0.145 mins at 300 K. Total wall clock time was 0.508 mins 169 | Simulation time was 0.01 ns 170 | ``` 171 | 172 | The system now has 58,052 atoms and takes quite a lot longer to simulate. 173 | On my laptop's GeForce GTX 1050 GPU it takes almost 2 mins. A 1ns simulation takes just under one hour. (these were using 174 | OpenMM 7.4). 175 | On a newer Geforce RTX 3060 GPU (a modern mid-range gamers card) it takes just over half a minute, and the 1ns simulation 176 | takes approx 15 mins, a 100ns simulation about 20 hours. 177 | 178 | Output is similar to the previous example. 179 | 180 | Note that when adding solvent you are creating a periodic box system, and this can introduce weird visual quirks. 181 | See [this discussion](https://github.com/openmm/openmm/issues/4218) for more details. 182 | Those quirks can be resolved by using the `analyse.py` script that is described below. 183 | 184 | ## Protein and ligand preparation 185 | 186 | *Note: these scripts have not yet been updated for OpenMM 77* 187 | 188 | The previous methods were a bit of a cheat as they used a protein that had been fully prepared for 189 | simulation. It's pretty unlikely you will start with a protein in that state. There are 2 scripts that 190 | illustrate how preparation can be done. The aim is to be able to do this entirely within OpenMM, but it seems 191 | that's not quite possible. 192 | 193 | The scripts are: 194 | 195 | [prepareProtein.py]() 196 | ``` 197 | python prepareProtein.py protein.pdb protein 198 | ``` 199 | This strips out everything that is not the protein, fixes problems in the protein, adds hydrogens and writes the 200 | file `protein_prepared.pdb`. That file can be used as inputs to the previous simulations. 201 | 202 | [prepareComplex.py]() 203 | ``` 204 | python prepareComplex.py data/Mpro-x0387_0_apo-desolv.pdb Mpro-x0387_0 205 | ``` 206 | This aims to build a PDB file with protein and ligand (and optionally the crystallographic waters) that is 207 | ready for simulation. It writes the files `protein_prepared.pdb` and `ligand_prepared.pdb`. 208 | It doesn't do everything that's needed, so other toolkits will be required: 209 | - ligand does not have hydrogens added 210 | - ligand can only be written to PDB format 211 | 212 | ## Analysis 213 | 214 | The MD trajectories are analysed using the script [analyse.py]() which uses [MDTraj](http://mdtraj.org/). 215 | 216 | Usage: 217 | ``` 218 | $ python analyse.py -h 219 | usage: analyse.py [-h] -p PROTEIN -t TRAJECTORY -o OUTPUT [-r] 220 | 221 | analyse 222 | 223 | options: 224 | -h, --help show this help message and exit 225 | -p PROTEIN, --protein PROTEIN 226 | Protein PDB file 227 | -t TRAJECTORY, --trajectory TRAJECTORY 228 | Trajectory DCD file 229 | -o OUTPUT, --output OUTPUT 230 | Output base name 231 | -r, --remove-waters Remove waters, salts etc. 232 | ``` 233 | This requires the trajectory to be written out using the DCD reporter. The topology can be read from the minimised 234 | starting point of the MD run. This can be used for simulations with or without water. 235 | 236 | The RMSD of the ligand and the protein C-alpha atoms compared to the start of the trajectory are displayed in a chart 237 | that is generated using [Plotly](https://plotly.com/graphing-libraries/)) with the name output.svg. 238 | 239 | The trajectory is also re-imaged so that the ligand is in the correct location with respect to the protein (the periodic 240 | box can cause some wierd visual aberations), and the waters, ions etc. removed if required. 241 | 242 | Example: 243 | ``` 244 | $ python analyse.py -p output_minimised.pdb -t output_traj.dcd -o output_reimaged -r 245 | analyse: Namespace(protein='output_minimised.pdb', trajectory='output_traj.dcd', output='output_reimaged', remove_waters=True) 246 | Reading trajectory output_traj.dcd 247 | Removing waters 248 | Realigning 249 | Writing re-imaged PDB output_reimaged.pdb 250 | Writing re-imaged trajectory output_reimaged.dcd 251 | Number of frames: 500 252 | 21 ligand atoms 253 | 1216 backbone atoms 254 | Writing RMSD output to output_reimaged.svg 255 | ``` 256 | 257 | In this case 3 files are created: 258 | * output_reimaged.svg - SVG of the RMSD of the backbone and ligand 259 | * output_reimaged.pdb - the re-imaged PDB file 260 | * output_reimaged.dcd - the re-imaged trajectory 261 | 262 | Example RMSD analysis: 263 | ![Example analysis](analyse.svg?raw=true "Example analysis]") 264 | 265 | The trajectory can be nicely viewed in [NGLView](http://nglviewer.org/ngl/). 266 | First load the PDB file, then click on the menu for the item in the selector on the right, find the Trajectory section 267 | and load the DCD file. 268 | 269 | For complexes that are stable the RMSDs should not change dramatically. For a complex that is unstable the ligand may 270 | detach from the protein and the RMSD will increase dramatically. Relatively long simulations will be needed, maybe in the 271 | order of 100s of ns (input welcome on this and on how valid these simulations will be without explicit water). 272 | 273 | ## Improvements 274 | 275 | Suggestions for how to improve these scripts and/or additional examples are welcome. 276 | -------------------------------------------------------------------------------- /analyse.py: -------------------------------------------------------------------------------- 1 | import sys, argparse 2 | import mdtraj as md 3 | import plotly.graph_objects as go 4 | 5 | 6 | # Typical usage: 7 | # python analyse.py -p output_minimised.pdb -t output_traj.dcd -o output_reimaged -r 8 | 9 | parser = argparse.ArgumentParser(description="analyse") 10 | 11 | parser.add_argument("-p", "--protein", required=True, help="Protein PDB file") 12 | parser.add_argument("-t", "--trajectory", required=True, help="Trajectory DCD file") 13 | parser.add_argument("-o", "--output", required=True, help="Output base name") 14 | parser.add_argument("-r", "--remove-waters", action='store_true', help="Remove waters, salts etc.") 15 | 16 | args = parser.parse_args() 17 | print("analyse: ", args) 18 | 19 | traj_in = args.trajectory 20 | topol_in = args.protein 21 | out_base = args.output 22 | 23 | print('Reading trajectory', traj_in) 24 | t = md.load(traj_in, top=topol_in) 25 | t.image_molecules(inplace=True) 26 | 27 | if args.remove_waters: 28 | print('Removing waters') 29 | t = t.atom_slice(t.top.select('not resname HOH POPC CL NA')) 30 | 31 | print('Realigning') 32 | prot = t.top.select('protein') 33 | t.superpose(t[0], atom_indices=prot) 34 | 35 | print('Writing re-imaged PDB', out_base + '.pdb') 36 | t[0].save(out_base + '.pdb') 37 | 38 | print('Writing re-imaged trajectory', out_base + '.dcd') 39 | t.save(out_base + '.dcd') 40 | 41 | topology = t.topology 42 | # print(topology) 43 | print('Number of frames:', t.n_frames) 44 | 45 | # print('All residues: %s' % [residue for residue in t.topology.residues]) 46 | 47 | atoms = t.topology.select("chainid 1") 48 | print(len(atoms), 'ligand atoms') 49 | rmsds_lig = md.rmsd(t, t, frame=0, atom_indices=atoms, parallel=True, precentered=False) 50 | # print(rmsds_lig) 51 | 52 | atoms = t.topology.select("chainid 0 and backbone") 53 | print(len(atoms), 'backbone atoms') 54 | rmsds_bck = md.rmsd(t, t, frame=0, atom_indices=atoms, parallel=True, precentered=False) 55 | 56 | fig = go.Figure() 57 | fig.add_trace(go.Scatter(x=t.time, y=rmsds_lig, mode='lines', name='Ligand')) 58 | fig.add_trace(go.Scatter(x=t.time, y=rmsds_bck, mode='lines', name='Backbone')) 59 | 60 | fig.update_layout(title='Trajectory for ' + traj_in, xaxis_title='Frame', yaxis_title='RMSD') 61 | 62 | file = out_base + '.svg' 63 | print('Writing RMSD output to', file) 64 | fig.write_image(file) -------------------------------------------------------------------------------- /analyse.svg: -------------------------------------------------------------------------------- 1 | 010k20k30k40k50k00.10.20.30.40.5LigandBackboneTrajectory for Mpro-x1093_0_traj_nosolv_100ns_330K.dcdFrameRMSD -------------------------------------------------------------------------------- /data/Mpro-x0387_0.mol: -------------------------------------------------------------------------------- 1 | 2 | RDKit 3D 3 | 4 | 13 14 0 0 0 0 0 0 0 0999 V2000 5 | 9.0650 -4.7370 27.6980 O 0 0 0 0 0 0 0 0 0 0 0 0 6 | 9.2630 -5.0400 26.3380 C 0 0 2 0 0 0 0 0 0 0 0 0 7 | 10.5520 -4.5380 25.9490 C 0 0 0 0 0 0 0 0 0 0 0 0 8 | 10.4810 -3.0380 25.6850 C 0 0 0 0 0 0 0 0 0 0 0 0 9 | 9.7300 -2.7840 24.4990 N 0 0 0 0 0 0 0 0 0 0 0 0 10 | 9.6860 -1.6170 24.1270 C 0 0 0 0 0 0 0 0 0 0 0 0 11 | 11.0770 -1.2320 23.6120 C 0 0 0 0 0 0 0 0 0 0 0 0 12 | 11.9690 -0.1320 24.1710 C 0 0 0 0 0 0 0 0 0 0 0 0 13 | 13.2030 -0.0890 23.4410 C 0 0 0 0 0 0 0 0 0 0 0 0 14 | 13.2070 -1.2250 22.2850 S 0 0 0 0 0 0 0 0 0 0 0 0 15 | 11.7550 -1.8900 22.5360 C 0 0 0 0 0 0 0 0 0 0 0 0 16 | 8.5600 -3.6210 24.3460 C 0 0 0 0 0 0 0 0 0 0 0 0 17 | 8.1750 -4.4830 25.5640 C 0 0 0 0 0 0 0 0 0 0 0 0 18 | 2 1 1 1 19 | 2 3 1 0 20 | 2 13 1 0 21 | 3 4 1 0 22 | 13 12 1 0 23 | 4 5 1 0 24 | 5 6 1 0 25 | 5 12 1 0 26 | 6 7 1 0 27 | 7 8 1 0 28 | 7 11 2 0 29 | 8 9 2 0 30 | 11 10 1 0 31 | 9 10 1 0 32 | M END 33 | -------------------------------------------------------------------------------- /data/Mpro-x0387_0.pdb: -------------------------------------------------------------------------------- 1 | HETATM 2724 O01 LIG A1101 9.065 -4.737 27.698 0.64 51.80 A O 2 | HETATM 2725 C02 LIG A1101 9.263 -5.040 26.338 0.64 52.20 A C 3 | HETATM 2726 C03 LIG A1101 10.552 -4.538 25.949 0.64 51.74 A C 4 | HETATM 2727 C04 LIG A1101 10.481 -3.038 25.685 0.64 52.42 A C 5 | HETATM 2728 N05 LIG A1101 9.730 -2.784 24.499 0.64 51.71 A N 6 | HETATM 2729 C06 LIG A1101 9.686 -1.617 24.127 0.64 49.45 A C 7 | HETATM 2730 C07 LIG A1101 11.077 -1.232 23.612 0.64 47.50 A C 8 | HETATM 2731 C08 LIG A1101 11.969 -0.132 24.171 0.64 45.93 A C 9 | HETATM 2732 C09 LIG A1101 13.203 -0.089 23.441 0.64 45.57 A C 10 | HETATM 2733 S10 LIG A1101 13.207 -1.225 22.285 0.64 46.60 A S 11 | HETATM 2734 C11 LIG A1101 11.755 -1.890 22.536 0.64 46.89 A C 12 | HETATM 2735 C12 LIG A1101 8.560 -3.621 24.346 0.64 53.22 A C 13 | HETATM 2736 C13 LIG A1101 8.175 -4.483 25.564 0.64 52.78 A C 14 | CONECT 2724 2725 15 | CONECT 2725 2724 2726 2736 16 | CONECT 2726 2725 2727 17 | CONECT 2736 2725 2735 18 | CONECT 2727 2726 2728 19 | CONECT 2728 2727 2729 2735 20 | CONECT 2729 2728 2730 21 | CONECT 2735 2728 2736 22 | CONECT 2730 2729 2731 2734 23 | CONECT 2731 2730 2732 24 | CONECT 2734 2730 2733 25 | CONECT 2732 2731 2733 26 | CONECT 2733 2732 2734 27 | -------------------------------------------------------------------------------- /data/Mpro-x0397_0.mol: -------------------------------------------------------------------------------- 1 | 2 | RDKit 3D 3 | 4 | 15 16 0 0 0 0 0 0 0 0999 V2000 5 | 8.2740 -1.3460 20.7790 C 0 0 0 0 0 0 0 0 0 0 0 0 6 | 7.3000 -2.0220 19.9120 N 0 0 0 0 0 0 0 0 0 0 0 0 7 | 7.2160 -1.6320 18.4930 C 0 0 0 0 0 0 0 0 0 0 0 0 8 | 6.3730 -0.3410 18.4060 C 0 0 0 0 0 0 0 0 0 0 0 0 9 | 5.3930 0.0540 19.3210 C 0 0 0 0 0 0 0 0 0 0 0 0 10 | 4.8870 1.2520 18.8900 C 0 0 0 0 0 0 0 0 0 0 0 0 11 | 3.7870 2.0620 19.5780 C 0 0 0 0 0 0 0 0 0 0 0 0 12 | 5.5230 1.5700 17.7710 O 0 0 0 0 0 0 0 0 0 0 0 0 13 | 6.4450 0.6110 17.4620 N 0 0 0 0 0 0 0 0 0 0 0 0 14 | 6.4090 -3.0460 20.4690 C 0 0 0 0 0 0 0 0 0 0 0 0 15 | 5.6220 -3.5850 19.7570 O 0 0 0 0 0 0 0 0 0 0 0 0 16 | 6.5070 -3.4030 21.8930 N 0 0 0 0 0 0 0 0 0 0 0 0 17 | 5.6450 -4.4230 22.5100 C 0 0 2 0 0 0 0 0 0 0 0 0 18 | 6.4200 -5.6060 23.0980 C 0 0 0 0 0 0 0 0 0 0 0 0 19 | 5.5560 -5.8040 21.8470 C 0 0 0 0 0 0 0 0 0 0 0 0 20 | 1 2 1 0 21 | 2 3 1 0 22 | 2 10 1 0 23 | 3 4 1 0 24 | 10 11 2 0 25 | 10 12 1 0 26 | 4 5 1 0 27 | 4 9 2 0 28 | 5 6 2 0 29 | 9 8 1 0 30 | 6 7 1 0 31 | 6 8 1 0 32 | 13 12 1 6 33 | 13 14 1 0 34 | 13 15 1 0 35 | 14 15 1 0 36 | M END 37 | -------------------------------------------------------------------------------- /data/Mpro-x0874_0.mol: -------------------------------------------------------------------------------- 1 | 2 | RDKit 3D 3 | 4 | 13 14 0 0 0 0 0 0 0 0999 V2000 5 | 10.3170 3.0110 22.2490 N 0 0 0 0 0 0 0 0 0 0 0 0 6 | 9.5290 1.7710 22.2430 C 0 0 0 0 0 0 0 0 0 0 0 0 7 | 9.1930 1.2720 21.1890 O 0 0 0 0 0 0 0 0 0 0 0 0 8 | 9.1480 1.1080 23.6100 C 0 0 1 0 0 0 0 0 0 0 0 0 9 | 7.8240 1.0110 23.7650 C 0 0 0 0 0 0 0 0 0 0 0 0 10 | 7.4350 -0.4970 23.3480 C 0 0 0 0 0 0 0 0 0 0 0 0 11 | 8.8030 -1.1880 23.0060 C 0 0 0 0 0 0 0 0 0 0 0 0 12 | 9.7260 -0.4540 23.6850 C 0 0 1 0 0 0 0 0 0 0 0 0 13 | 11.1510 -0.5360 23.0850 C 0 0 0 0 0 0 0 0 0 0 0 0 14 | 12.1570 0.3870 23.4630 C 0 0 0 0 0 0 0 0 0 0 0 0 15 | 13.4110 0.1800 22.8360 C 0 0 0 0 0 0 0 0 0 0 0 0 16 | 13.2160 -1.2120 21.7920 S 0 0 0 0 0 0 0 0 0 0 0 0 17 | 11.5290 -1.5290 22.1590 C 0 0 0 0 0 0 0 0 0 0 0 0 18 | 1 2 1 0 19 | 2 3 2 0 20 | 4 2 1 6 21 | 4 5 1 0 22 | 4 8 1 0 23 | 5 6 1 0 24 | 8 7 1 0 25 | 8 9 1 6 26 | 6 7 1 0 27 | 9 10 1 0 28 | 9 13 2 0 29 | 10 11 2 0 30 | 13 12 1 0 31 | 11 12 1 0 32 | M END 33 | -------------------------------------------------------------------------------- /data/Mpro-x1093_0.mol: -------------------------------------------------------------------------------- 1 | 2 | RDKit 3D 3 | 4 | 19 21 0 0 0 0 0 0 0 0999 V2000 5 | 13.9130 -1.2590 23.2230 C 0 0 0 0 0 0 0 0 0 0 0 0 6 | 12.5110 -0.9700 23.5310 N 0 0 0 0 0 0 0 0 0 0 0 0 7 | 9.1000 1.2550 21.1530 O 0 0 0 0 0 0 0 0 0 0 0 0 8 | 11.6150 -1.9330 22.8770 C 0 0 0 0 0 0 0 0 0 0 0 0 9 | 9.9240 -0.2280 22.6330 N 0 0 0 0 0 0 0 0 0 0 0 0 10 | 10.1650 -1.5840 23.1190 C 0 0 0 0 0 0 0 0 0 0 0 0 11 | 6.7830 0.2880 17.8390 N 0 0 0 0 0 0 0 0 0 0 0 0 12 | 10.7420 0.7360 23.3590 C 0 0 0 0 0 0 0 0 0 0 0 0 13 | 4.7860 1.4690 18.5050 N 0 0 0 0 0 0 0 0 0 0 0 0 14 | 12.1970 0.4050 23.1350 C 0 0 0 0 0 0 0 0 0 0 0 0 15 | 9.1590 0.1020 21.5780 C 0 0 0 0 0 0 0 0 0 0 0 0 16 | 8.3780 -1.0090 20.8940 C 0 0 0 0 0 0 0 0 0 0 0 0 17 | 7.5260 -0.4030 19.8320 C 0 0 0 0 0 0 0 0 0 0 0 0 18 | 7.7680 -0.4040 18.4910 C 0 0 0 0 0 0 0 0 0 0 0 0 19 | 5.8860 0.7470 18.7550 C 0 0 0 0 0 0 0 0 0 0 0 0 20 | 4.0720 1.8050 19.5870 C 0 0 0 0 0 0 0 0 0 0 0 0 21 | 4.4050 1.4630 20.8980 C 0 0 0 0 0 0 0 0 0 0 0 0 22 | 5.5480 0.7300 21.1350 C 0 0 0 0 0 0 0 0 0 0 0 0 23 | 6.3230 0.3480 20.0330 C 0 0 0 0 0 0 0 0 0 0 0 0 24 | 1 2 1 0 25 | 2 4 1 0 26 | 2 10 1 0 27 | 4 6 1 0 28 | 10 8 1 0 29 | 3 11 2 0 30 | 11 5 1 0 31 | 11 12 1 0 32 | 6 5 1 0 33 | 5 8 1 0 34 | 7 14 1 0 35 | 7 15 1 0 36 | 14 13 2 0 37 | 15 9 2 0 38 | 15 19 1 0 39 | 9 16 1 0 40 | 16 17 2 0 41 | 12 13 1 0 42 | 13 19 1 0 43 | 19 18 2 0 44 | 17 18 1 0 45 | M END 46 | -------------------------------------------------------------------------------- /environment.yml: -------------------------------------------------------------------------------- 1 | name: openmm-77 2 | channels: 3 | - plotly 4 | - conda-forge 5 | dependencies: 6 | - python=3.10 7 | - openmm=7.7 8 | - openmmforcefields=0.11.2 9 | - openff-forcefields=2023.08.0 10 | - openff-toolkit=0.14.3 11 | - parmed=4.1 12 | - pdbfixer=1.8 13 | - rdkit=2023.03 14 | - mdtraj=1.9.9 15 | - plotly=4.9.0 16 | - python-kaleido=0.2.1 17 | -------------------------------------------------------------------------------- /ligand1.mol: -------------------------------------------------------------------------------- 1 | CCOC(=O)c1ccccc1 2 | OpenBabel05162016413D 3 | 4 | 21 21 0 0 0 0 0 0 0 0999 V2000 5 | 9.2039 -3.6943 28.3629 C 0 0 0 0 0 0 0 0 0 0 0 0 6 | 9.6379 -2.2769 28.0269 C 0 0 0 0 0 0 0 0 0 0 0 0 7 | 10.1800 -2.2831 26.6999 O 0 0 0 0 0 0 0 0 0 0 0 0 8 | 10.6561 -1.0913 26.2619 C 0 0 0 0 0 0 0 0 0 0 0 0 9 | 10.7039 -0.0547 26.9050 O 0 0 0 0 0 0 0 0 0 0 0 0 10 | 11.2422 -1.2462 24.9061 C 0 0 0 0 0 0 0 0 0 0 0 0 11 | 11.9678 -0.1683 24.3776 C 0 0 0 0 0 0 0 0 0 0 0 0 12 | 12.6091 -0.2918 23.1448 C 0 0 0 0 0 0 0 0 0 0 0 0 13 | 12.5254 -1.4869 22.4308 C 0 0 0 0 0 0 0 0 0 0 0 0 14 | 11.7963 -2.5600 22.9432 C 0 0 0 0 0 0 0 0 0 0 0 0 15 | 11.1527 -2.4413 24.1771 C 0 0 0 0 0 0 0 0 0 0 0 0 16 | 8.7944 -3.7171 29.3512 H 0 0 0 0 0 0 0 0 0 0 0 0 17 | 10.0496 -4.3478 28.3121 H 0 0 0 0 0 0 0 0 0 0 0 0 18 | 8.4621 -4.0158 27.6620 H 0 0 0 0 0 0 0 0 0 0 0 0 19 | 10.3830 -1.9507 28.7221 H 0 0 0 0 0 0 0 0 0 0 0 0 20 | 8.8040 -1.6089 28.0849 H 0 0 0 0 0 0 0 0 0 0 0 0 21 | 12.0274 0.7185 24.9021 H 0 0 0 0 0 0 0 0 0 0 0 0 22 | 13.1458 0.5017 22.7609 H 0 0 0 0 0 0 0 0 0 0 0 0 23 | 13.0041 -1.5775 21.5210 H 0 0 0 0 0 0 0 0 0 0 0 0 24 | 11.7324 -3.4416 22.4106 H 0 0 0 0 0 0 0 0 0 0 0 0 25 | 10.6102 -3.2345 24.5533 H 0 0 0 0 0 0 0 0 0 0 0 0 26 | 1 2 1 0 0 0 0 27 | 1 12 1 0 0 0 0 28 | 1 13 1 0 0 0 0 29 | 1 14 1 0 0 0 0 30 | 2 3 1 0 0 0 0 31 | 2 15 1 0 0 0 0 32 | 2 16 1 0 0 0 0 33 | 3 4 1 0 0 0 0 34 | 4 5 2 0 0 0 0 35 | 4 6 1 0 0 0 0 36 | 6 7 2 0 0 0 0 37 | 6 11 1 0 0 0 0 38 | 7 8 1 0 0 0 0 39 | 7 17 1 0 0 0 0 40 | 8 9 2 0 0 0 0 41 | 8 18 1 0 0 0 0 42 | 9 10 1 0 0 0 0 43 | 9 19 1 0 0 0 0 44 | 10 11 2 0 0 0 0 45 | 10 20 1 0 0 0 0 46 | 11 21 1 0 0 0 0 47 | M END 48 | -------------------------------------------------------------------------------- /ligand1.pdb: -------------------------------------------------------------------------------- 1 | COMPND CCOC(=O)c1ccccc1 2 | AUTHOR GENERATED BY OPEN BABEL 3.1.0 3 | HETATM 1 C UNL 1 9.204 -3.694 28.363 1.00 0.00 C 4 | HETATM 2 C UNL 1 9.638 -2.277 28.027 1.00 0.00 C 5 | HETATM 3 O UNL 1 10.180 -2.283 26.700 1.00 0.00 O 6 | HETATM 4 C UNL 1 10.656 -1.091 26.262 1.00 0.00 C 7 | HETATM 5 O UNL 1 10.704 -0.055 26.905 1.00 0.00 O 8 | HETATM 6 C UNL 1 11.242 -1.246 24.906 1.00 0.00 C 9 | HETATM 7 C UNL 1 11.968 -0.168 24.378 1.00 0.00 C 10 | HETATM 8 C UNL 1 12.609 -0.292 23.145 1.00 0.00 C 11 | HETATM 9 C UNL 1 12.525 -1.487 22.431 1.00 0.00 C 12 | HETATM 10 C UNL 1 11.796 -2.560 22.943 1.00 0.00 C 13 | HETATM 11 C UNL 1 11.153 -2.441 24.177 1.00 0.00 C 14 | HETATM 12 H UNL 1 8.794 -3.717 29.351 1.00 0.00 H 15 | HETATM 13 H UNL 1 10.050 -4.348 28.312 1.00 0.00 H 16 | HETATM 14 H UNL 1 8.462 -4.016 27.662 1.00 0.00 H 17 | HETATM 15 H UNL 1 10.383 -1.951 28.722 1.00 0.00 H 18 | HETATM 16 H UNL 1 8.804 -1.609 28.085 1.00 0.00 H 19 | HETATM 17 H UNL 1 12.027 0.719 24.902 1.00 0.00 H 20 | HETATM 18 H UNL 1 13.146 0.502 22.761 1.00 0.00 H 21 | HETATM 19 H UNL 1 13.004 -1.577 21.521 1.00 0.00 H 22 | HETATM 20 H UNL 1 11.732 -3.442 22.411 1.00 0.00 H 23 | HETATM 21 H UNL 1 10.610 -3.235 24.553 1.00 0.00 H 24 | CONECT 1 2 12 13 14 25 | CONECT 2 1 3 15 16 26 | CONECT 3 2 4 27 | CONECT 4 3 5 5 6 28 | CONECT 5 4 4 29 | CONECT 6 4 7 7 11 30 | CONECT 7 6 6 8 17 31 | CONECT 8 7 9 9 18 32 | CONECT 9 8 8 10 19 33 | CONECT 10 9 11 11 20 34 | CONECT 11 6 10 10 21 35 | CONECT 12 1 36 | CONECT 13 1 37 | CONECT 14 1 38 | CONECT 15 2 39 | CONECT 16 2 40 | CONECT 17 7 41 | CONECT 18 8 42 | CONECT 19 9 43 | CONECT 20 10 44 | CONECT 21 11 45 | MASTER 0 0 0 0 0 0 0 0 21 0 21 0 46 | END 47 | -------------------------------------------------------------------------------- /ligand1.sdf: -------------------------------------------------------------------------------- 1 | CCOC(=O)c1ccccc1 2 | OpenBabel05162016413D 3 | 4 | 21 21 0 0 0 0 0 0 0 0999 V2000 5 | 9.2039 -3.6943 28.3629 C 0 0 0 0 0 0 0 0 0 0 0 0 6 | 9.6379 -2.2769 28.0269 C 0 0 0 0 0 0 0 0 0 0 0 0 7 | 10.1800 -2.2831 26.6999 O 0 0 0 0 0 0 0 0 0 0 0 0 8 | 10.6561 -1.0913 26.2619 C 0 0 0 0 0 0 0 0 0 0 0 0 9 | 10.7039 -0.0547 26.9050 O 0 0 0 0 0 0 0 0 0 0 0 0 10 | 11.2422 -1.2462 24.9061 C 0 0 0 0 0 0 0 0 0 0 0 0 11 | 11.9678 -0.1683 24.3776 C 0 0 0 0 0 0 0 0 0 0 0 0 12 | 12.6091 -0.2918 23.1448 C 0 0 0 0 0 0 0 0 0 0 0 0 13 | 12.5254 -1.4869 22.4308 C 0 0 0 0 0 0 0 0 0 0 0 0 14 | 11.7963 -2.5600 22.9432 C 0 0 0 0 0 0 0 0 0 0 0 0 15 | 11.1527 -2.4413 24.1771 C 0 0 0 0 0 0 0 0 0 0 0 0 16 | 8.7944 -3.7171 29.3512 H 0 0 0 0 0 0 0 0 0 0 0 0 17 | 10.0496 -4.3478 28.3121 H 0 0 0 0 0 0 0 0 0 0 0 0 18 | 8.4621 -4.0158 27.6620 H 0 0 0 0 0 0 0 0 0 0 0 0 19 | 10.3830 -1.9507 28.7221 H 0 0 0 0 0 0 0 0 0 0 0 0 20 | 8.8040 -1.6089 28.0849 H 0 0 0 0 0 0 0 0 0 0 0 0 21 | 12.0274 0.7185 24.9021 H 0 0 0 0 0 0 0 0 0 0 0 0 22 | 13.1458 0.5017 22.7609 H 0 0 0 0 0 0 0 0 0 0 0 0 23 | 13.0041 -1.5775 21.5210 H 0 0 0 0 0 0 0 0 0 0 0 0 24 | 11.7324 -3.4416 22.4106 H 0 0 0 0 0 0 0 0 0 0 0 0 25 | 10.6102 -3.2345 24.5533 H 0 0 0 0 0 0 0 0 0 0 0 0 26 | 1 2 1 0 0 0 0 27 | 1 12 1 0 0 0 0 28 | 1 13 1 0 0 0 0 29 | 1 14 1 0 0 0 0 30 | 2 3 1 0 0 0 0 31 | 2 15 1 0 0 0 0 32 | 2 16 1 0 0 0 0 33 | 3 4 1 0 0 0 0 34 | 4 5 2 0 0 0 0 35 | 4 6 1 0 0 0 0 36 | 6 7 2 0 0 0 0 37 | 6 11 1 0 0 0 0 38 | 7 8 1 0 0 0 0 39 | 7 17 1 0 0 0 0 40 | 8 9 2 0 0 0 0 41 | 8 18 1 0 0 0 0 42 | 9 10 1 0 0 0 0 43 | 9 19 1 0 0 0 0 44 | 10 11 2 0 0 0 0 45 | 10 20 1 0 0 0 0 46 | 11 21 1 0 0 0 0 47 | M END 48 | $$$$ 49 | -------------------------------------------------------------------------------- /prepareComplex.py: -------------------------------------------------------------------------------- 1 | from simtk.openmm.app import * 2 | from simtk.openmm import * 3 | from pdbfixer import PDBFixer 4 | 5 | if len(sys.argv) != 4: 6 | print('Usage: python prepareComplex.py input.pdb ligand.mol system') 7 | exit(1) 8 | 9 | pdb_in = sys.argv[1] 10 | ligand_in = sys.argv[2] 11 | outname = sys.argv[3] 12 | 13 | # This PDB file contains: 14 | # - the protein (single chain) 15 | # - a ligand 16 | # - 3 DMSO molecules 17 | # - A number of waters 18 | # No hydrogens are present. 19 | # The C-teminal THR residue is missing an oxygen atom. 20 | fixer = PDBFixer(filename=pdb_in) 21 | fixer.findMissingResidues() 22 | fixer.findMissingAtoms() 23 | fixer.findNonstandardResidues() 24 | print('Residues:', fixer.missingResidues) 25 | print('Atoms:', fixer.missingAtoms) 26 | print('Terminals:', fixer.missingTerminals) 27 | print('Non-standard:', fixer.nonstandardResidues) 28 | 29 | fixer.addMissingAtoms() 30 | fixer.addMissingHydrogens(7.4) 31 | 32 | # The following removes the DMS components and retains the ligand and waters. 33 | # If instead we want to remove the ligand it will be easier to use: 34 | # fixer.removeHeterogens(True) or fixer.removeHeterogens(False) 35 | # True keeps the waters, False removes them leaving only the protein. 36 | # See prepareProtein.py for this in action. 37 | modeller = Modeller(fixer.topology, fixer.positions) 38 | toDelete = [] 39 | for res in modeller.topology.residues(): 40 | if res.name == 'DMS': 41 | toDelete.append(res) 42 | print('Deleting', res) 43 | modeller.delete(toDelete) 44 | 45 | 46 | with open(outname + '_receptor.pdb', 'w') as outfile: 47 | PDBFile.writeFile(modeller.topology, modeller.positions, file=outfile, keepIds=True) 48 | 49 | 50 | # Now the ligand. We write it to a file in PDB format. It would be good to write to Molfile format but seems that 51 | # OpenMM does not support that. 52 | # Note: this may not be the best way to do this. Other toolkits might be better. 53 | # Note: your ligand may not be named 'LIG' 54 | # Note: modeller.addHydrogens() does not work for ligands. We'll need to use another toolkit such as OpenBabel or RDKit to do this. 55 | modeller = Modeller(fixer.topology, fixer.positions) 56 | toDelete = [] 57 | for res in modeller.topology.residues(): 58 | if res.name != 'LIG': 59 | toDelete.append(res) 60 | modeller.delete(toDelete) 61 | 62 | with open(outname + '_ligand.pdb', 'w') as outfile: 63 | PDBFile.writeFile(modeller.topology, modeller.positions, file=outfile, keepIds=True) 64 | 65 | print('Done') -------------------------------------------------------------------------------- /prepareProtein.py: -------------------------------------------------------------------------------- 1 | import sys 2 | from simtk.openmm.app import * 3 | from simtk.openmm import * 4 | from simtk import unit 5 | from pdbfixer import PDBFixer 6 | from openmmforcefields.generators import SystemGenerator 7 | 8 | if len(sys.argv) != 3: 9 | print('Usage: python prepareProtein.py input.pdb output') 10 | print('Creates outputs named output_fixed.pdb and output_minimised.pdb') 11 | exit(1) 12 | 13 | pdb_in = sys.argv[1] 14 | pdb_out = sys.argv[2] 15 | print('Processing', pdb_in, 'to', pdb_out) 16 | 17 | fixer = PDBFixer(filename=pdb_in) 18 | fixer.findMissingResidues() 19 | fixer.findMissingAtoms() 20 | fixer.findNonstandardResidues() 21 | print('Residues:', fixer.missingResidues) 22 | print('Atoms:', fixer.missingAtoms) 23 | print('Terminals:', fixer.missingTerminals) 24 | print('Non-standard:', fixer.nonstandardResidues) 25 | 26 | fixer.addMissingAtoms() 27 | fixer.addMissingHydrogens(7.4) 28 | fixer.removeHeterogens(False) 29 | 30 | with open(pdb_out + '_fixed.pdb', 'w') as outfile: 31 | PDBFile.writeFile(fixer.topology, fixer.positions, file=outfile, keepIds=True) 32 | 33 | system_generator = SystemGenerator(forcefields=['amber/ff14SB.xml']) 34 | system = system_generator.create_system(fixer.topology) 35 | integrator = LangevinIntegrator(300 * unit.kelvin, 1 / unit.picosecond, 0.002 * unit.picoseconds) 36 | simulation = Simulation(fixer.topology, system, integrator) 37 | simulation.context.setPositions(fixer.positions) 38 | print('Minimising') 39 | simulation.minimizeEnergy() 40 | 41 | # write out the minimised PDB 42 | with open(pdb_out + '_minimised.pdb', 'w') as outfile: 43 | PDBFile.writeFile(fixer.topology, simulation.context.getState(getPositions=True, enforcePeriodicBox=False).getPositions(), file=outfile, keepIds=True) 44 | 45 | print('Done') -------------------------------------------------------------------------------- /simulateComplex.py: -------------------------------------------------------------------------------- 1 | """ 2 | Run a MD simulation for a complex, optionally adding a solvent box 3 | """ 4 | 5 | import sys, time, argparse 6 | 7 | from openff.toolkit import Molecule 8 | from openmmforcefields.generators import SystemGenerator 9 | import openmm 10 | from openmm import app, unit, LangevinIntegrator, Vec3 11 | from openmm.app import PDBFile, Simulation, Modeller, PDBReporter, StateDataReporter, DCDReporter 12 | 13 | import utils 14 | 15 | t0 = time.time() 16 | 17 | 18 | parser = argparse.ArgumentParser(description="simulateComplex") 19 | 20 | parser.add_argument("-p", "--protein", required=True, help="Protein PDB file") 21 | parser.add_argument("-l", "--ligand", required=True, help="Ligand molfile") 22 | parser.add_argument("-o", "--output", default='output', help="Base name for output files") 23 | parser.add_argument("-s", "--steps", type=int, default=5000, help="Number of steps") 24 | parser.add_argument("-z", "--step-size", type=float, default=0.002, help="Step size (ps") 25 | parser.add_argument("-f", "--friction-coeff", type=float, default=1, help="Friction coefficient (ps)") 26 | parser.add_argument("-i", "--interval", type=int, default=1000, help="Reporting interval") 27 | parser.add_argument("-t", "--temperature", type=int, default=300, help="Temperature (K)") 28 | parser.add_argument("--solvate", action='store_true', help="Add solvent box") 29 | parser.add_argument("--padding", type=float, default=10, help="Padding for solvent box (A)") 30 | parser.add_argument("--water-model", default="tip3p", 31 | choices=["tip3p", "spce", "tip4pew", "tip5p", "swm4ndp"], 32 | help="Water model for solvation") 33 | parser.add_argument("--positive-ion", default="Na+", help="Positive ion for solvation") 34 | parser.add_argument("--negative-ion", default="Cl-", help="Negative ion for solvation") 35 | parser.add_argument("--ionic-strength", type=float, default="0", help="Ionic strength for solvation") 36 | parser.add_argument("--no-neutralize", action='store_true', help="Don't add ions to neutralize") 37 | parser.add_argument("-e", "--equilibration-steps", type=int, default=200, help="Number of equilibration steps") 38 | parser.add_argument("--protein-force-field", default='amber/ff14SB.xml', help="Protein force field") 39 | parser.add_argument("--ligand-force-field", default='gaff-2.11', help="Ligand force field") 40 | parser.add_argument("--water-force-field", default='amber/tip3p_standard.xml', help="Ligand force field") 41 | 42 | args = parser.parse_args() 43 | print("simulateComplex: ", args) 44 | 45 | pdb_in = args.protein 46 | mol_in = args.ligand 47 | output_base = args.output 48 | output_complex = output_base + '_complex.pdb' 49 | output_traj_dcd = output_base + '_traj.dcd' 50 | output_min = output_base + '_minimised.pdb' 51 | num_steps = args.steps 52 | reporting_interval = args.interval 53 | temperature = args.temperature * unit.kelvin 54 | equilibration_steps = args.equilibration_steps 55 | print('Processing', pdb_in, 'and', mol_in, 'with', num_steps, 'steps generating outputs', 56 | output_complex, output_min, output_traj_dcd) 57 | 58 | # get the chosen or fastest platform 59 | platform = utils.get_platform() 60 | 61 | print('Reading ligand') 62 | ligand_mol = Molecule.from_file(mol_in) 63 | 64 | print('Preparing system') 65 | # Initialize a SystemGenerator using the GAFF for the ligand and tip3p for the water. 66 | forcefield_kwargs = {'constraints': app.HBonds, 'rigidWater': True, 'removeCMMotion': False, 'hydrogenMass': 4*unit.amu } 67 | system_generator = SystemGenerator( 68 | forcefields=[args.protein_force_field, args.water_force_field], 69 | small_molecule_forcefield=args.ligand_force_field, 70 | molecules=[ligand_mol], 71 | forcefield_kwargs=forcefield_kwargs) 72 | 73 | # Use Modeller to combine the protein and ligand into a complex 74 | print('Reading protein') 75 | protein_pdb = PDBFile(pdb_in) 76 | 77 | print('Preparing complex') 78 | # The topology is described in the openforcefield API 79 | 80 | modeller = Modeller(protein_pdb.topology, protein_pdb.positions) 81 | print('System has %d atoms' % modeller.topology.getNumAtoms()) 82 | 83 | # The topology is described in the openforcefield API 84 | print('Adding ligand...') 85 | lig_top = ligand_mol.to_topology() 86 | modeller.add(lig_top.to_openmm(), lig_top.get_positions().to_openmm()) 87 | print('System has %d atoms' % modeller.topology.getNumAtoms()) 88 | 89 | # Solvate 90 | if args.solvate: 91 | print('Adding solvent...') 92 | # we use the 'padding' option to define the periodic box. 93 | # we just create a box that has a 10A (default) padding around the complex. 94 | modeller.addSolvent(system_generator.forcefield, model=args.water_model, padding=args.padding * unit.angstroms, 95 | positiveIon=args.positive_ion, negativeIon=args.negative_ion, 96 | ionicStrength=args.ionic_strength * unit.molar, neutralize=not args.no_neutralize) 97 | print('System has %d atoms' % modeller.topology.getNumAtoms()) 98 | 99 | with open(output_complex, 'w') as outfile: 100 | PDBFile.writeFile(modeller.topology, modeller.positions, outfile) 101 | 102 | # Create the system using the SystemGenerator 103 | system = system_generator.create_system(modeller.topology, molecules=ligand_mol) 104 | 105 | friction_coeff = args.friction_coeff / unit.picosecond 106 | step_size = args.step_size * unit.picoseconds 107 | duration = (step_size * num_steps).value_in_unit(unit.nanoseconds) 108 | print('Simulating for {} ns'.format(duration)) 109 | 110 | integrator = LangevinIntegrator(temperature, friction_coeff, step_size) 111 | if args.solvate: 112 | system.addForce(openmm.MonteCarloBarostat(1 * unit.atmospheres, temperature, 25)) 113 | 114 | if system.usesPeriodicBoundaryConditions(): 115 | print('Default Periodic box: {}'.format(system.getDefaultPeriodicBoxVectors())) 116 | else: 117 | print('No Periodic Box') 118 | 119 | simulation = Simulation(modeller.topology, system, integrator, platform=platform) 120 | context = simulation.context 121 | context.setPositions(modeller.positions) 122 | 123 | print('Minimising ...') 124 | simulation.minimizeEnergy() 125 | 126 | # Write out the minimised PDB. 127 | with open(output_min, 'w') as outfile: 128 | PDBFile.writeFile(modeller.topology, context.getState(getPositions=True, enforcePeriodicBox=True).getPositions(), file=outfile, keepIds=True) 129 | 130 | # equilibrate 131 | simulation.context.setVelocitiesToTemperature(temperature) 132 | print('Equilibrating ...') 133 | simulation.step(equilibration_steps) 134 | 135 | # Run the simulation. 136 | simulation.reporters.append(DCDReporter(output_traj_dcd, reporting_interval, enforcePeriodicBox=True)) 137 | simulation.reporters.append(StateDataReporter(sys.stdout, reporting_interval * 5, step=True, potentialEnergy=True, temperature=True)) 138 | print('Starting simulation with', num_steps, 'steps ...') 139 | t1 = time.time() 140 | simulation.step(num_steps) 141 | t2 = time.time() 142 | print('Simulation complete in {} mins at {}. Total wall clock time was {} mins'.format( 143 | round((t2 - t1) / 60, 3), temperature, round((t2 - t0) / 60, 3))) 144 | print('Simulation time was', round(duration, 3), 'ns') 145 | -------------------------------------------------------------------------------- /simulateProtein.py: -------------------------------------------------------------------------------- 1 | from openmmforcefields.generators import SystemGenerator 2 | from openmm import unit 3 | from openmm.app import PDBFile, Simulation 4 | from openmm import * 5 | 6 | protein_pdb = PDBFile('protein.pdb') 7 | 8 | print('Preparing system') 9 | # Initialize a SystemGenerator 10 | system_generator = SystemGenerator(forcefields=['amber/ff14SB.xml']) 11 | 12 | system = system_generator.create_system(protein_pdb.topology) 13 | integrator = LangevinIntegrator(300 * unit.kelvin, 1 / unit.picosecond, 0.002 * unit.picoseconds) 14 | simulation = Simulation(protein_pdb.topology, system, integrator) 15 | simulation.context.setPositions(protein_pdb.positions) 16 | print('Minimising') 17 | simulation.minimizeEnergy() 18 | 19 | # write out the minimised PDB 20 | with open('minimised1.pdb', 'w') as outfile: 21 | PDBFile.writeFile(protein_pdb.topology, 22 | simulation.context.getState(getPositions=True, enforcePeriodicBox=True).getPositions(), 23 | file=outfile, keepIds=True) 24 | 25 | print('Done') 26 | -------------------------------------------------------------------------------- /utils.py: -------------------------------------------------------------------------------- 1 | import os 2 | from openmm import Platform 3 | 4 | def get_platform(): 5 | os_platform = os.getenv('PLATFORM') 6 | if os_platform: 7 | platform = Platform.getPlatformByName(os_platform) 8 | else: 9 | # work out the fastest platform 10 | speed = 0 11 | for i in range(Platform.getNumPlatforms()): 12 | p = Platform.getPlatform(i) 13 | # print(p.getName(), p.getSpeed()) 14 | if p.getSpeed() > speed: 15 | platform = p 16 | speed = p.getSpeed() 17 | 18 | print('Using platform', platform.getName()) 19 | 20 | # if it's GPU platform set the precision to mixed 21 | if platform.getName() == 'CUDA' or platform.getName() == 'OpenCL': 22 | platform.setPropertyDefaultValue('Precision', 'mixed') 23 | print('Set precision for platform', platform.getName(), 'to mixed') 24 | 25 | return platform 26 | --------------------------------------------------------------------------------