736 lines (736 with data), 53.2 kB
{
"nbformat": 4,
"nbformat_minor": 0,
"metadata": {
"colab": {
"provenance": [],
"authorship_tag": "ABX9TyN7soXHJFokcQtIXbExp0Cb",
"include_colab_link": true
},
"kernelspec": {
"name": "python3",
"display_name": "Python 3"
},
"language_info": {
"name": "python"
}
},
"cells": [
{
"cell_type": "markdown",
"metadata": {
"id": "view-in-github",
"colab_type": "text"
},
"source": [
"<a href=\"https://colab.research.google.com/github/francescopatane96/Computer_aided_drug_discovery_kit/blob/main/19_molecular_dynamics.ipynb\" target=\"_parent\"><img src=\"https://colab.research.google.com/assets/colab-badge.svg\" alt=\"Open In Colab\"/></a>"
]
},
{
"cell_type": "code",
"execution_count": 1,
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/"
},
"id": "AQ0bcJn30a0a",
"outputId": "e491477d-f182-45d3-8cea-86ebcfea5155"
},
"outputs": [
{
"output_type": "stream",
"name": "stdout",
"text": [
"Collecting condacolab\n",
" Downloading condacolab-0.1.7-py3-none-any.whl (7.2 kB)\n",
"Installing collected packages: condacolab\n",
"Successfully installed condacolab-0.1.7\n",
"⏬ Downloading https://github.com/conda-forge/miniforge/releases/download/23.1.0-1/Mambaforge-23.1.0-1-Linux-x86_64.sh...\n",
"📦 Installing...\n",
"📌 Adjusting configuration...\n",
"🩹 Patching environment...\n",
"⏲ Done in 0:00:17\n",
"🔁 Restarting kernel...\n"
]
}
],
"source": [
"try:\n",
" import google.colab\n",
"\n",
" !pip install condacolab\n",
" import condacolab\n",
"\n",
" condacolab.install()\n",
"except ModuleNotFoundError:\n",
" pass"
]
},
{
"cell_type": "code",
"source": [
"try:\n",
" import condacolab\n",
" from google.colab import files\n",
" from IPython.display import clear_output\n",
"\n",
" condacolab.check()\n",
" !conda install -q -y -c conda-forge mdtraj openmm openmmforcefields openff-toolkit pdbfixer pypdb rdkit\n",
"except ModuleNotFoundError:\n",
" on_colab = False\n",
"else:\n",
" # check if installation was succesful\n",
" try:\n",
" import rdkit\n",
"\n",
" on_colab = True\n",
" clear_output() # clear the excessive installation outputs\n",
" print(\"Dependencies successfully installed!\")\n",
" except ModuleNotFoundError:\n",
" print(\"Error while installing dependencies!\")"
],
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/"
},
"id": "49Zn15tn3TW2",
"outputId": "74750be4-39f0-48f1-94c9-d98b17f1cb16"
},
"execution_count": 2,
"outputs": [
{
"output_type": "stream",
"name": "stdout",
"text": [
"Dependencies successfully installed!\n"
]
}
]
},
{
"cell_type": "code",
"source": [
"import sys\n",
"\n",
"if not on_colab and sys.platform.startswith((\"linux\", \"darwin\")):\n",
" !mamba install -q -y -c conda-forge openmmforcefields\n",
" # Notes:\n",
" # - If you do not have mamba installed, install it or use conda instead\n",
" # - Under MacOS with an M1 chip you may need to use\n",
" # CONDA_SUBDIR=osx-64 in front of the above command"
],
"metadata": {
"id": "GMnfN9Lj3sYu"
},
"execution_count": 3,
"outputs": []
},
{
"cell_type": "code",
"source": [
"import copy\n",
"from pathlib import Path\n",
"\n",
"import requests\n",
"from IPython.display import display\n",
"import numpy as np\n",
"from rdkit import Chem\n",
"from rdkit.Chem import Draw\n",
"from rdkit.Chem import AllChem\n",
"import mdtraj as md\n",
"import pdbfixer\n",
"import openmm as mm\n",
"import openmm.app as app\n",
"from openmm import unit\n",
"from openff.toolkit.topology import Molecule, Topology\n",
"from openmmforcefields.generators import GAFFTemplateGenerator"
],
"metadata": {
"id": "gDQMA8HG34p0"
},
"execution_count": 4,
"outputs": []
},
{
"cell_type": "code",
"source": [
"pdbid = \"3POZ\"\n",
"ligand_name = \"03P\"\n",
"pdb_path = f\"{pdbid}.pdb\"\n",
"pdb_url = f\"https://files.rcsb.org/download/{pdbid}.pdb\""
],
"metadata": {
"id": "VMUN_T4w4CMA"
},
"execution_count": 30,
"outputs": []
},
{
"cell_type": "code",
"source": [
"r = requests.get(pdb_url)\n",
"r.raise_for_status()\n",
"with open(pdb_path, \"wb\") as f:\n",
" f.write(r.content)"
],
"metadata": {
"id": "n262tgb24D0h"
},
"execution_count": 31,
"outputs": []
},
{
"cell_type": "markdown",
"source": [
"prepare protein"
],
"metadata": {
"id": "mSd6JJSW6hNK"
}
},
{
"cell_type": "code",
"source": [
"def prepare_protein(\n",
" pdb_file, ignore_missing_residues=True, ignore_terminal_missing_residues=True, ph=7.0\n",
"):\n",
" \"\"\"\n",
" Use pdbfixer to prepare the protein from a PDB file. Hetero atoms such as ligands are\n",
" removed and non-standard residues replaced. Missing atoms to existing residues are added.\n",
" Missing residues are ignored by default, but can be included.\n",
"\n",
" Parameters\n",
" ----------\n",
" pdb_file: pathlib.Path or str\n",
" PDB file containing the system to simulate.\n",
" ignore_missing_residues: bool, optional\n",
" If missing residues should be ignored or built.\n",
" ignore_terminal_missing_residues: bool, optional\n",
" If missing residues at the beginning and the end of a chain should be ignored or built.\n",
" ph: float, optional\n",
" pH value used to determine protonation state of residues\n",
"\n",
" Returns\n",
" -------\n",
" fixer: pdbfixer.pdbfixer.PDBFixer\n",
" Prepared protein system.\n",
" \"\"\"\n",
" fixer = pdbfixer.PDBFixer(str(pdb_file))\n",
" fixer.removeHeterogens() # co-crystallized ligands are unknown to PDBFixer\n",
" fixer.findMissingResidues() # identify missing residues, needed for identification of missing atoms\n",
"\n",
" # if missing terminal residues shall be ignored, remove them from the dictionary\n",
" if ignore_terminal_missing_residues:\n",
" chains = list(fixer.topology.chains())\n",
" keys = fixer.missingResidues.keys()\n",
" for key in list(keys):\n",
" chain = chains[key[0]]\n",
" if key[1] == 0 or key[1] == len(list(chain.residues())):\n",
" del fixer.missingResidues[key]\n",
"\n",
" # if all missing residues shall be ignored ignored, clear the dictionary\n",
" if ignore_missing_residues:\n",
" fixer.missingResidues = {}\n",
"\n",
" fixer.findNonstandardResidues() # find non-standard residue\n",
" fixer.replaceNonstandardResidues() # replace non-standard residues with standard one\n",
" fixer.findMissingAtoms() # find missing heavy atoms\n",
" fixer.addMissingAtoms() # add missing atoms and residues\n",
" fixer.addMissingHydrogens(ph) # add missing hydrogens\n",
" return fixer"
],
"metadata": {
"id": "heG0ehyB4hvm"
},
"execution_count": 32,
"outputs": []
},
{
"cell_type": "code",
"source": [
"# prepare protein and build only missing non-terminal residues\n",
"\n",
"prepared_protein = prepare_protein(pdb_path, ignore_missing_residues=False)"
],
"metadata": {
"id": "zt5VDKGl6Y1W"
},
"execution_count": 33,
"outputs": []
},
{
"cell_type": "markdown",
"source": [
"prepare ligand"
],
"metadata": {
"id": "K9rPZl1z6eVx"
}
},
{
"cell_type": "code",
"source": [
"def prepare_ligand(pdb_file, resname, smiles, depict=True):\n",
" \"\"\"\n",
" Prepare a ligand from a PDB file via adding hydrogens and assigning bond orders. A depiction\n",
" of the ligand before and after preparation is rendered in 2D to allow an inspection of the\n",
" results. Huge thanks to @j-wags for the suggestion.\n",
"\n",
" Parameters\n",
" ----------\n",
" pdb_file: pathlib.PosixPath\n",
" PDB file containing the ligand of interest.\n",
" resname: str\n",
" Three character residue name of the ligand.\n",
" smiles : str\n",
" SMILES string of the ligand informing about correct protonation and bond orders.\n",
" depict: bool, optional\n",
" show a 2D representation of the ligand\n",
"\n",
" Returns\n",
" -------\n",
" prepared_ligand: rdkit.Chem.rdchem.Mol\n",
" Prepared ligand.\n",
" \"\"\"\n",
" # split molecule\n",
" rdkit_mol = Chem.MolFromPDBFile(str(pdb_file))\n",
" rdkit_mol_split = Chem.rdmolops.SplitMolByPDBResidues(rdkit_mol)\n",
"\n",
" # extract the ligand and remove any already present hydrogens\n",
" ligand = rdkit_mol_split[resname]\n",
" ligand = Chem.RemoveHs(ligand)\n",
"\n",
" # assign bond orders from template\n",
" reference_mol = Chem.MolFromSmiles(smiles)\n",
" prepared_ligand = AllChem.AssignBondOrdersFromTemplate(reference_mol, ligand)\n",
" prepared_ligand.AddConformer(ligand.GetConformer(0))\n",
"\n",
" # protonate ligand\n",
" prepared_ligand = Chem.rdmolops.AddHs(prepared_ligand, addCoords=True)\n",
" prepared_ligand = Chem.MolFromMolBlock(Chem.MolToMolBlock(prepared_ligand))\n",
"\n",
" # 2D depiction\n",
" if depict:\n",
" ligand_2d = copy.deepcopy(ligand)\n",
" prepared_ligand_2d = copy.deepcopy(prepared_ligand)\n",
" AllChem.Compute2DCoords(ligand_2d)\n",
" AllChem.Compute2DCoords(prepared_ligand_2d)\n",
" display(\n",
" Draw.MolsToGridImage(\n",
" [ligand_2d, prepared_ligand_2d], molsPerRow=2, legends=[\"original\", \"prepared\"]\n",
" )\n",
" )\n",
"\n",
" # return ligand\n",
" return prepared_ligand"
],
"metadata": {
"id": "E_TWK1no4t0u"
},
"execution_count": 34,
"outputs": []
},
{
"cell_type": "code",
"source": [
"smiles = \"CC(C)(O)CC(=O)NCCn1ccc2ncnc(Nc3ccc(Oc4cccc(c4)C(F)(F)F)c(Cl)c3)c12\"\n",
"rdkit_ligand = prepare_ligand(pdb_path, ligand_name, smiles)"
],
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/",
"height": 252
},
"id": "pB-3RiPj400o",
"outputId": "068c34aa-ca4e-42fd-81a5-f507d2b40329"
},
"execution_count": 35,
"outputs": [
{
"output_type": "stream",
"name": "stderr",
"text": [
"[17:28:45] WARNING: More than one matching pattern found - picking one\n",
"\n"
]
},
{
"output_type": "display_data",
"data": {
"image/png": "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\n",
"text/plain": [
"<IPython.core.display.Image object>"
]
},
"metadata": {}
}
]
},
{
"cell_type": "code",
"source": [
"def rdkit_to_openmm(rdkit_mol, name=\"LIG\"):\n",
" \"\"\"\n",
" Convert an RDKit molecule to an OpenMM molecule.\n",
" Inspired by @hannahbrucemcdonald and @glass-w.\n",
"\n",
" Parameters\n",
" ----------\n",
" rdkit_mol: rdkit.Chem.rdchem.Mol\n",
" RDKit molecule to convert.\n",
" name: str\n",
" Molecule name.\n",
"\n",
" Returns\n",
" -------\n",
" omm_molecule: openmm.app.Modeller\n",
" OpenMM modeller object holding the molecule of interest.\n",
" \"\"\"\n",
" # convert RDKit to OpenFF\n",
" off_mol = Molecule.from_rdkit(rdkit_mol)\n",
"\n",
" # add name for molecule\n",
" off_mol.name = name\n",
"\n",
" # add names for atoms\n",
" element_counter_dict = {}\n",
" for off_atom, rdkit_atom in zip(off_mol.atoms, rdkit_mol.GetAtoms()):\n",
" element = rdkit_atom.GetSymbol()\n",
" if element in element_counter_dict.keys():\n",
" element_counter_dict[element] += 1\n",
" else:\n",
" element_counter_dict[element] = 1\n",
" off_atom.name = element + str(element_counter_dict[element])\n",
"\n",
" # convert from OpenFF to OpenMM\n",
" off_mol_topology = off_mol.to_topology()\n",
" mol_topology = off_mol_topology.to_openmm()\n",
" mol_positions = off_mol.conformers[0]\n",
"\n",
" # convert units from Ångström to nanometers\n",
" # since OpenMM works in nm\n",
" mol_positions = mol_positions.to(\"nanometers\")\n",
"\n",
" # combine topology and positions in modeller object\n",
" omm_mol = app.Modeller(mol_topology, mol_positions)\n",
"\n",
" return omm_mol"
],
"metadata": {
"id": "70Ua-pHD5R_T"
},
"execution_count": 36,
"outputs": []
},
{
"cell_type": "code",
"source": [
"omm_ligand = rdkit_to_openmm(rdkit_ligand, ligand_name)"
],
"metadata": {
"id": "xzSL0hJ05TEn"
},
"execution_count": 37,
"outputs": []
},
{
"cell_type": "code",
"source": [
"def merge_protein_and_ligand(protein, ligand):\n",
" \"\"\"\n",
" Merge two OpenMM objects.\n",
"\n",
" Parameters\n",
" ----------\n",
" protein: pdbfixer.pdbfixer.PDBFixer\n",
" Protein to merge.\n",
" ligand: openmm.app.Modeller\n",
" Ligand to merge.\n",
"\n",
" Returns\n",
" -------\n",
" complex_topology: openmm.app.topology.Topology\n",
" The merged topology.\n",
" complex_positions: openmm.unit.quantity.Quantity\n",
" The merged positions.\n",
" \"\"\"\n",
" # combine topologies\n",
" md_protein_topology = md.Topology.from_openmm(protein.topology) # using mdtraj for protein top\n",
" md_ligand_topology = md.Topology.from_openmm(ligand.topology) # using mdtraj for ligand top\n",
" md_complex_topology = md_protein_topology.join(md_ligand_topology) # add them together\n",
" complex_topology = md_complex_topology.to_openmm()\n",
"\n",
" # combine positions\n",
" total_atoms = len(protein.positions) + len(ligand.positions)\n",
"\n",
" # create an array for storing all atom positions as tupels containing a value and a unit\n",
" # called OpenMM Quantities\n",
" complex_positions = unit.Quantity(np.zeros([total_atoms, 3]), unit=unit.nanometers)\n",
" complex_positions[: len(protein.positions)] = protein.positions # add protein positions\n",
" complex_positions[len(protein.positions) :] = ligand.positions # add ligand positions\n",
"\n",
" return complex_topology, complex_positions"
],
"metadata": {
"id": "yxnPCLmL5uIT"
},
"execution_count": 38,
"outputs": []
},
{
"cell_type": "code",
"source": [
"complex_topology, complex_positions = merge_protein_and_ligand(prepared_protein, omm_ligand)"
],
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/"
},
"id": "GASjYvZZ5wOi",
"outputId": "0ac6827d-f8a7-4e50-8d99-e83c07c971d8"
},
"execution_count": 39,
"outputs": [
{
"output_type": "stream",
"name": "stderr",
"text": [
"/usr/local/lib/python3.10/site-packages/openmm/unit/quantity.py:750: UnitStrippedWarning: The unit of the quantity is stripped when downcasting to ndarray.\n",
" self._value[key] = value / self.unit\n"
]
}
]
},
{
"cell_type": "code",
"source": [
"print(\"Complex topology has\", complex_topology.getNumAtoms(), \"atoms.\")\n",
"# NBVAL_CHECK_OUTPUT"
],
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/"
},
"id": "DlNviihM647L",
"outputId": "2f8001e5-3c54-4f49-b581-68cd03126c65"
},
"execution_count": 58,
"outputs": [
{
"output_type": "stream",
"name": "stdout",
"text": [
"Complex topology has 5561 atoms.\n"
]
}
]
},
{
"cell_type": "code",
"source": [
"def generate_forcefield(\n",
" rdkit_mol=None, protein_ff=\"amber14-all.xml\", solvent_ff=\"amber14/tip3pfb.xml\"\n",
"):\n",
" \"\"\"\n",
" Generate an OpenMM Forcefield object and register a small molecule.\n",
"\n",
" Parameters\n",
" ----------\n",
" rdkit_mol: rdkit.Chem.rdchem.Mol\n",
" Small molecule to register in the force field.\n",
" protein_ff: string\n",
" Name of the force field.\n",
" solvent_ff: string\n",
" Name of the solvent force field.\n",
"\n",
" Returns\n",
" -------\n",
" forcefield: openmm.app.Forcefield\n",
" Forcefield with registered small molecule.\n",
" \"\"\"\n",
" forcefield = app.ForceField(protein_ff, solvent_ff)\n",
"\n",
" if rdkit_mol is not None:\n",
" gaff = GAFFTemplateGenerator(\n",
" molecules=Molecule.from_rdkit(rdkit_mol, allow_undefined_stereo=True)\n",
" )\n",
" forcefield.registerTemplateGenerator(gaff.generator)\n",
"\n",
" return forcefield"
],
"metadata": {
"id": "FS0G30OR7Gq9"
},
"execution_count": 41,
"outputs": []
},
{
"cell_type": "code",
"source": [
"forcefield = generate_forcefield(rdkit_ligand)"
],
"metadata": {
"id": "nGROPvgg7JBF"
},
"execution_count": 42,
"outputs": []
},
{
"cell_type": "code",
"source": [
"modeller = app.Modeller(complex_topology, complex_positions)\n",
"modeller.addSolvent(forcefield, padding=1.0 * unit.nanometers, ionicStrength=0.15 * unit.molar)"
],
"metadata": {
"id": "Nq3HtWHb7Sce"
},
"execution_count": 43,
"outputs": []
},
{
"cell_type": "code",
"source": [
"system = forcefield.createSystem(modeller.topology, nonbondedMethod=app.PME)\n",
"integrator = mm.LangevinIntegrator(\n",
" 300 * unit.kelvin, 1.0 / unit.picoseconds, 2.0 * unit.femtoseconds\n",
")\n",
"simulation = app.Simulation(modeller.topology, system, integrator)\n",
"simulation.context.setPositions(modeller.positions)"
],
"metadata": {
"id": "17hZr0PQ7gky"
},
"execution_count": 44,
"outputs": []
},
{
"cell_type": "code",
"source": [
"simulation.minimizeEnergy()\n",
"with open(\"topology.pdb\", \"w\") as pdb_file:\n",
" app.PDBFile.writeFile(\n",
" simulation.topology,\n",
" simulation.context.getState(getPositions=True, enforcePeriodicBox=True).getPositions(),\n",
" file=pdb_file,\n",
" keepIds=True,\n",
" )"
],
"metadata": {
"id": "8BblL0fX73kH"
},
"execution_count": 45,
"outputs": []
},
{
"cell_type": "code",
"source": [
"# output settings\n",
"if on_colab:\n",
" steps = 500 # corresponds to 100 ps\n",
" write_interval = 50 # write every 10 ps\n",
" log_interval = 25 # log progress to stdout every 5 ps\n",
"else:\n",
" steps = 100 # corresponds to 20 fs\n",
" write_interval = 10 # write every 2 fs\n",
" log_interval = 10 # log progress to stdout every 2 fs\n",
"simulation.reporters.append(\n",
" md.reporters.XTCReporter(file=str(\"trajectory.xtc\"), reportInterval=write_interval)\n",
")\n",
"simulation.reporters.append(\n",
" app.StateDataReporter(\n",
" sys.stdout,\n",
" log_interval,\n",
" step=True,\n",
" potentialEnergy=True,\n",
" temperature=True,\n",
" progress=True,\n",
" remainingTime=True,\n",
" speed=True,\n",
" totalSteps=steps,\n",
" separator=\"\\t\",\n",
" )\n",
")"
],
"metadata": {
"id": "F6Lcxnf4A604"
},
"execution_count": 55,
"outputs": []
},
{
"cell_type": "code",
"source": [
"simulation.context.setVelocitiesToTemperature(300 * unit.kelvin)\n",
"simulation.step(steps) # perform the simulation"
],
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/"
},
"id": "iJyno0HEE8TS",
"outputId": "239eb3af-c820-4b95-9178-0d7f4eb16239"
},
"execution_count": 56,
"outputs": [
{
"output_type": "stream",
"name": "stdout",
"text": [
"#\"Progress (%)\"\t\"Step\"\t\"Potential Energy (kJ/mole)\"\t\"Temperature (K)\"\t\"Speed (ns/day)\"\t\"Time Remaining\"\n",
"275.0%\t1375\t-820069.5917694856\t300.70801789383444\t0\t--\n",
"280.0%\t1400\t-819983.1464936219\t300.0918382591453\t0.81\t23:56:49\n",
"285.0%\t1425\t-819263.0368271468\t298.8138801747044\t0.866\t23:56:56\n",
"290.0%\t1450\t-818919.0744361738\t298.8305427291525\t0.725\t23:56:14\n",
"295.0%\t1475\t-819748.4363111855\t300.729636904383\t0.647\t23:55:40\n",
"300.0%\t1500\t-819660.1624241127\t299.66761221053065\t0.687\t23:55:49\n",
"305.0%\t1525\t-819323.6770758838\t299.71689710004654\t0.683\t23:55:41\n",
"310.0%\t1550\t-819539.2441378905\t300.3312551362554\t0.657\t23:55:24\n",
"315.0%\t1575\t-819116.2467783571\t299.9009869911623\t0.683\t23:55:29\n",
"320.0%\t1600\t-818403.6001041392\t298.94589120878936\t0.706\t23:55:31\n",
"325.0%\t1625\t-818228.2611690544\t299.15140107215495\t0.68\t23:55:15\n",
"330.0%\t1650\t-818716.549979551\t300.31481806182217\t0.68\t23:55:08\n",
"335.0%\t1675\t-818214.4521257979\t298.8141444495686\t0.697\t23:55:09\n",
"340.0%\t1700\t-818554.5553893867\t299.4424987187032\t0.702\t23:55:05\n",
"345.0%\t1725\t-819182.7033567722\t300.57025441743275\t0.683\t23:54:51\n",
"350.0%\t1750\t-818565.5740845099\t299.88551212408834\t0.694\t23:54:49\n",
"355.0%\t1775\t-819000.0796485256\t301.0857574413104\t0.704\t23:54:48\n",
"360.0%\t1800\t-818265.8023260854\t299.6084377600189\t0.696\t23:54:38\n",
"365.0%\t1825\t-818490.5172593745\t300.6368918015818\t0.69\t23:54:29\n",
"370.0%\t1850\t-818362.2791066584\t300.80767221479937\t0.7\t23:54:27\n"
]
}
]
},
{
"cell_type": "code",
"source": [
"if on_colab:\n",
" files.download(\"topology.pdb\")\n",
" files.download(\"trajectory.xtc\")"
],
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/",
"height": 335
},
"id": "JOR1aLbSFCXh",
"outputId": "3d8b5072-1105-4c95-9c13-8eaa65483588"
},
"execution_count": 57,
"outputs": [
{
"output_type": "error",
"ename": "FileNotFoundError",
"evalue": "ignored",
"traceback": [
"\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
"\u001b[0;31mFileNotFoundError\u001b[0m Traceback (most recent call last)",
"\u001b[0;32m<ipython-input-57-12ff510b6faf>\u001b[0m in \u001b[0;36m<cell line: 1>\u001b[0;34m()\u001b[0m\n\u001b[1;32m 1\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mon_colab\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 2\u001b[0;31m \u001b[0mfiles\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mdownload\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"topology.pdb\"\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 3\u001b[0m \u001b[0mfiles\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mdownload\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"trajectory.xtc\"\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
"\u001b[0;32m/usr/local/lib/python3.10/dist-packages/google/colab/files.py\u001b[0m in \u001b[0;36mdownload\u001b[0;34m(filename)\u001b[0m\n\u001b[1;32m 223\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0;32mnot\u001b[0m \u001b[0m_os\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mpath\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mexists\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mfilename\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 224\u001b[0m \u001b[0mmsg\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;34m'Cannot find file: {}'\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mformat\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mfilename\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 225\u001b[0;31m \u001b[0;32mraise\u001b[0m \u001b[0mFileNotFoundError\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mmsg\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;31m# pylint: disable=undefined-variable\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 226\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 227\u001b[0m \u001b[0mcomm_manager\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0m_IPython\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mget_ipython\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mkernel\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mcomm_manager\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
"\u001b[0;31mFileNotFoundError\u001b[0m: Cannot find file: topology.pdb"
]
}
]
}
]
}