Skip to content

Commit

Permalink
simplify the notebook
Browse files Browse the repository at this point in the history
  • Loading branch information
jthorton committed Oct 21, 2024
1 parent 3f596fe commit 4552a3e
Showing 1 changed file with 6 additions and 147 deletions.
153 changes: 6 additions & 147 deletions bespokefit_tutorial/bespoke_parameters_showcase.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -9,51 +9,14 @@
"This tutorial gives a step-by-step guide on the use of OpenFF-BespokeFit generated force field parameters with OpenFE protocols. Here we will focus on relative\n",
"binding free energy (RBFE) calculations, but this strategy can be used with any OpenFE protocol. \n",
"\n",
"Here we will aussme you have succesfully planned your RBFE campaign using OpenFE by following the [showcase](http://try.openfree.energy/) or [RBFE tutorial](https://docs.openfree.energy/en/stable/tutorials/rbfe_cli_tutorial.html#rbfe-cli-tutorial) and will be using the `TYK2` test system in this example with the planned network provided for you at `inputs/ligand_network.graphml`. "
"Here we will aussme you have succesfully planned your RBFE campaign using OpenFE by following the [showcase](http://try.openfree.energy/) or [RBFE tutorial](https://docs.openfree.energy/en/stable/tutorials/rbfe_cli_tutorial.html#rbfe-cli-tutorial) and will be using the `TYK2` test system in this example with the planned network provided for you at `inputs/ligand_network.graphml`. You should have also already generated a set of bespoke parameters for your ligand series following the [BespokeFit production guide](https://docs.openforcefield.org/projects/bespokefit/en/latest/getting-started/quick-start.html#production-fits) and combined the parameters into a single SMIRNOFF style `offxml` file following the gathering [results guide](https://docs.openforcefield.org/projects/bespokefit/en/latest/users/bespoke-results.html)."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## BespokeFit submission\n",
"\n",
"First we need to build our BespokeFit fitting protocol and submit the ligands for parameterisation. At this point we assume you already have a running bespokefit server which you can interact with using CLI commands such as: `openff-bespoke executor list` which should indicate that the executor can be reached and is ready to take new jobs. If not you can follow the [bespokefit guide](https://docs.openforcefield.org/projects/bespokefit/en/latest/getting-started/quick-start.html#production-fits) to setup the executor. \n",
"\n",
"If you have a prefered BespokeFit workflow you can skip this step and load that."
]
},
{
"cell_type": "code",
"execution_count": 1,
"metadata": {},
"outputs": [],
"source": [
"from openff.bespokefit.workflows import BespokeWorkflowFactory\n",
"from openff.qcsubmit.common_structures import QCSpec\n",
"\n",
"\n",
"# create a fast specification built on AIMNET2\n",
"aimnet2 = QCSpec(\n",
" method=\"wb97m-d3\",\n",
" basis=None,\n",
" program=\"aimnet2\",\n",
" spec_description=\"Fast MLQM method using aimnet2.\"\n",
")\n",
"\n",
"# build the factory using the AIMNET2 specification and set the force field to the newest version of Sage\n",
"bespoke_factory = BespokeWorkflowFactory(default_qc_specs=[aimnet2], initial_force_field=\"openff_unconstrained-2.2.1.offxml\")\n",
"# update the fitting iterations for large molecules with lots of torsions\n",
"bespoke_factory.optimizer.max_iterations = 20\n",
"# save the factory to file for later \n",
"bespoke_factory.to_file(\"aimnet2-bespoke-factory.json\")"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Now we need to load our planned network and submit each ligand to the BespokeFit server using our chosen bespoke protocol, we should also keep track of the bespokefit task ID which will make it easy to gather the results later."
"Before creating the OpenFE simulation inputs we first we need to lead up our planned ligand network of transformations and our bespoke force field file."
]
},
{
Expand All @@ -62,109 +25,14 @@
"metadata": {},
"outputs": [],
"source": [
"from gufe import LigandNetwork, SmallMoleculeComponent, LigandAtomMapping\n",
"from openff.bespokefit.executor import BespokeFitClient\n",
"from openff.bespokefit.executor.executor import Settings\n",
"from gufe import LigandNetwork\n",
"from openff.toolkit import ForceField\n",
"\n",
"# load our network file\n",
"ligand_network = LigandNetwork.from_graphml(\n",
" open(\"inputs/ligand_network.graphml\", \"r\").read()\n",
")\n",
"# create the client from environment variables\n",
"client = BespokeFitClient(settings = Settings())\n",
"\n",
"# for each ligand in the network submit the ligand to bespokefit\n",
"name_to_node = {}\n",
"for ligand in ligand_network.nodes:\n",
" bespoke_job = bespoke_factory.optimization_schema_from_molecule(\n",
" molecule=ligand.to_openff(), index=ligand.name\n",
" )\n",
" # submit the job and save the task id\n",
" response = client.submit_optimization(input_schema=bespoke_job)\n",
" # we need to round trip the ligand to add the molprop\n",
" ligand_data = ligand.to_dict()\n",
" ligand_data[\"molprops\"][\"bespokefit_id\"] = response\n",
" new_ligand = SmallMoleculeComponent.from_dict(ligand_data)\n",
" name_to_node[new_ligand.name] = new_ligand\n",
"\n",
"# create a set of new edges using these updated nodes to ensure they are used in the network\n",
"edges = []\n",
"for edge in ligand_network.edges:\n",
" new_edge = LigandAtomMapping(\n",
" componentA=name_to_node[edge.componentA.name],\n",
" componentB=name_to_node[edge.componentB.name],\n",
" componentA_to_componentB=edge.componentA_to_componentB,\n",
" annotations=edge.annotations\n",
" )\n",
" edges.append(new_edge)\n",
"\n",
"# create a new network from the edges and save to file\n",
"new_network = LigandNetwork(edges=edges)\n",
"with open(\"bespoke_ligand_network.graphml\", \"w\") as output:\n",
" output.write(new_network.to_graphml())\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Now all we need to do is sit back and wait for the BespokeFit jobs to finish, we can use the code here to load in our ligand network and check the status of the jobs"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"import pandas as pd\n",
"bespoke_network = LigandNetwork.from_graphml(\n",
" open(\"bespoke_ligand_network.graphml\", \"r\").read()\n",
")\n",
"molecule_data = []\n",
"for ligand in bespoke_network.nodes:\n",
" # do we have an easier way to access the molprops?\n",
" ligand_data = ligand.to_dict()\n",
" response = client.get_optimization(optimization_id=ligand_data[\"molprops\"][\"bespokefit_id\"])\n",
" molecule_data.append(\n",
" {\"ligand\": ligand.name, \"status\": response.status, \"stage\": response.stages[0].type}\n",
" )\n",
"print(pd.DataFrame(molecule_data))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Gathering results\n",
"\n",
"Once the calculations are finished we can use BespokeFit to build a single force field file which contains all of the bespoke parameters for the ligands in this network which we can use with the RBFE protocol."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# build the command to combine all of the bespoke parameters\n",
"command = \"openff-bespoke combine --output tyk2_bespoke_ff.offxml \"\n",
"# extract the ids of the bespokefit jobs for this network\n",
"for ligand in bespoke_network.nodes:\n",
" ligand_data = ligand.to_dict()\n",
" command += f\"--id {ligand_data['molprops']['bespokefit_id']} \"\n",
"print(command)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# run the command to collect all of the results\n",
"import os\n",
"os.system(command)"
"bespoke_force_field = ForceField(\"my_bespoke_ff.offxml\")"
]
},
{
Expand All @@ -184,17 +52,14 @@
"source": [
"# create the OpenFE RBFE protocol using our bespoke force field\n",
"from openfe.protocols.openmm_rfe import RelativeHybridTopologyProtocol\n",
"from openff.toolkit import ForceField\n",
"import openfe\n",
"\n",
"# load the bespokefit force field\n",
"force_field = ForceField(\"tyk2_bespoke_ff.offxml\")\n",
"\n",
"# create the default protocol settings\n",
"settings = RelativeHybridTopologyProtocol.default_settings()\n",
"# add our new force field as a string\n",
"# this avoids the need to move the file around when executing the transformations\n",
"settings.forcefield_settings.small_molecule_forcefield = force_field.to_string()\n",
"settings.forcefield_settings.small_molecule_forcefield = bespoke_force_field.to_string()\n",
"\n",
"# create the protocol\n",
"protocol = RelativeHybridTopologyProtocol(settings)\n",
Expand Down Expand Up @@ -266,18 +131,12 @@
"\n",
"So to recap the workflow can be reduced to the following steps:\n",
"- Plan the RBFE network\n",
"- Submit each of the ligands in the network for processing by BespokeFit\n",
"- Create a single SMIRNOFF style force field with all of the bespoke parameters for the network using the BespokeFit `combine` CLI\n",
"- Store the force field as a string in the OpenFE protocol under the `settings.forcefield_settings.small_molecule_forcefield` field\n",
"- Use these settings to create the protcol and create the AlchemicalNetwork following the normal steps\n",
"\n",
"Hopefully its clear that this strategy can be applied to any bespoke parameters you wish to add to the force field not just those from BespokeFit, simply edit your base SMIRNOFF style force field using the [OpenFF-Toolkit](https://docs.openforcefield.org/en/latest/examples/openforcefield/openff-toolkit/forcefield_modification/forcefield_modification.html#modifying-a-smirnoff-force-field) set it in the protocol and simulate! \n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": []
}
],
"metadata": {
Expand Down

0 comments on commit 4552a3e

Please sign in to comment.