diff --git a/cookbook/bespoke_parameters_showcase.ipynb b/cookbook/bespoke_parameters_showcase.ipynb index 86a28e8..84b59ea 100644 --- a/cookbook/bespoke_parameters_showcase.ipynb +++ b/cookbook/bespoke_parameters_showcase.ipynb @@ -49,23 +49,31 @@ }, { "cell_type": "code", - "execution_count": 2, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# create the OpenFE RBFE protocol using our bespoke force field\n", + "import copy\n", + "from openff.units import unit\n", "from openfe.protocols.openmm_rfe import RelativeHybridTopologyProtocol\n", "import openfe\n", "\n", - "\n", "# create the default protocol settings\n", - "settings = RelativeHybridTopologyProtocol.default_settings()\n", + "base_settings = RelativeHybridTopologyProtocol.default_settings()\n", + "\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 = bespoke_force_field.to_string()\n", + "base_settings.forcefield_settings.small_molecule_forcefield = bespoke_force_field.to_string()\n", + "\n", + "# we create a copy of the settings for the complex leg so that we can reduce the solvation cutoff\n", + "complex_settings = copy.deepcopy(base_settings)\n", + "complex_settings.solvation_settings.solvent_padding = 1 * unit.nanometer\n", + "\n", + "# create the protocols\n", + "solvent_protocol = RelativeHybridTopologyProtocol(base_settings)\n", + "complex_protocol = RelativeHybridTopologyProtocol(complex_settings)\n", "\n", - "# create the protocol\n", - "protocol = RelativeHybridTopologyProtocol(settings)\n", "\n", "# create the solvent and protein components\n", "solvent = openfe.SolventComponent()\n", @@ -82,8 +90,11 @@ " 'solvent': solvent}\n", "\n", " if leg == 'complex':\n", + " protocol = complex_protocol\n", " sysA_dict['protein'] = protein\n", " sysB_dict['protein'] = protein\n", + " else:\n", + " protocol = solvent_protocol\n", "\n", " # we don't have to name objects, but it can make things (like filenames) more convenient\n", " sysA = openfe.ChemicalSystem(sysA_dict, name=f\"{mapping.componentA.name}_{leg}\")\n", @@ -94,7 +105,7 @@ " transformation = openfe.Transformation(\n", " stateA=sysA,\n", " stateB=sysB,\n", - " mapping={'ligand': mapping},\n", + " mapping=mapping,\n", " protocol=protocol, # use protocol created above\n", " name=f\"{prefix}{sysA.name}_{sysB.name}\"\n", " )\n", @@ -112,7 +123,7 @@ }, { "cell_type": "code", - "execution_count": 3, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ @@ -123,7 +134,7 @@ "\n", "# then we write out each transformation\n", "for transformation in network.edges:\n", - " transformation.dump(transformation_dir / f\"{transformation.name}.json\")" + " transformation.to_json(transformation_dir / f\"{transformation.name}.json\")" ] }, { @@ -132,7 +143,8 @@ "source": [ "# Recap\n", "\n", - "So to recap the workflow can be reduced to the following steps:\n\n", + "So to recap the workflow can be reduced to the following steps:\n", + "\n", "- Plan the RBFE network\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", @@ -144,7 +156,7 @@ ], "metadata": { "kernelspec": { - "display_name": "asapdiscovery", + "display_name": "Python 3 (ipykernel)", "language": "python", "name": "python3" }, @@ -158,9 +170,16 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.11.6" + "version": "3.12.11" + }, + "widgets": { + "application/vnd.jupyter.widget-state+json": { + "state": {}, + "version_major": 2, + "version_minor": 0 + } } }, "nbformat": 4, - "nbformat_minor": 2 + "nbformat_minor": 4 } diff --git a/cookbook/choose_protocol.ipynb b/cookbook/choose_protocol.ipynb index 7c9a26a..65b5df6 100644 --- a/cookbook/choose_protocol.ipynb +++ b/cookbook/choose_protocol.ipynb @@ -178,64 +178,82 @@ "text": [ "{'alchemical_settings': {'endstate_dispersion_correction': False,\n", " 'explicit_charge_correction': False,\n", - " 'explicit_charge_correction_cutoff': ,\n", + " 'explicit_charge_correction_cutoff': {'unit': 'nanometer',\n", + " 'val': 0.8},\n", " 'softcore_LJ': 'gapsys',\n", " 'softcore_alpha': 0.85,\n", " 'turn_off_core_unique_exceptions': False,\n", " 'use_dispersion_correction': False},\n", - " 'engine_settings': {'compute_platform': None, 'gpu_device_index': None},\n", + " 'engine_settings': {'compute_platform': 'cuda', 'gpu_device_index': None},\n", " 'forcefield_settings': {'constraints': 'hbonds',\n", " 'forcefields': ['amber/ff14SB.xml',\n", " 'amber/tip3p_standard.xml',\n", " 'amber/tip3p_HFE_multivalent.xml',\n", " 'amber/phosaa10.xml'],\n", " 'hydrogen_mass': 3.0,\n", - " 'nonbonded_cutoff': ,\n", + " 'nonbonded_cutoff': {'unit': 'nanometer', 'val': 0.9},\n", " 'nonbonded_method': 'PME',\n", " 'rigid_water': True,\n", - " 'small_molecule_forcefield': 'openff-2.1.1'},\n", - " 'integrator_settings': {'barostat_frequency': ,\n", + " 'small_molecule_forcefield': 'openff-2.2.1'},\n", + " 'integrator_settings': {'barostat_frequency': {'unit': 'timestep',\n", + " 'val': 25.0},\n", " 'constraint_tolerance': 1e-06,\n", - " 'langevin_collision_rate': ,\n", + " 'langevin_collision_rate': {'unit': '1 / picosecond',\n", + " 'val': 1.0},\n", " 'n_restart_attempts': 20,\n", " 'reassign_velocities': False,\n", " 'remove_com': False,\n", - " 'timestep': },\n", + " 'timestep': {'unit': 'femtosecond', 'val': 4.0}},\n", " 'lambda_settings': {'lambda_functions': 'default', 'lambda_windows': 11},\n", - " 'output_settings': {'checkpoint_interval': ,\n", + " 'output_settings': {'checkpoint_interval': {'unit': 'nanosecond', 'val': 1.0},\n", " 'checkpoint_storage_filename': 'checkpoint.chk',\n", " 'forcefield_cache': 'db.json',\n", " 'output_filename': 'simulation.nc',\n", " 'output_indices': 'not water',\n", " 'output_structure': 'hybrid_system.pdb',\n", - " 'positions_write_frequency': ,\n", + " 'positions_write_frequency': {'unit': 'picosecond',\n", + " 'val': 100.0},\n", " 'velocities_write_frequency': None},\n", " 'partial_charge_settings': {'nagl_model': None,\n", " 'number_of_conformers': None,\n", " 'off_toolkit_backend': 'ambertools',\n", " 'partial_charge_method': 'am1bcc'},\n", " 'protocol_repeats': 3,\n", - " 'simulation_settings': {'early_termination_target_error': ,\n", - " 'equilibration_length': ,\n", + " 'simulation_settings': {'early_termination_target_error': {'unit': 'kilocalorie_per_mole',\n", + " 'val': 0.0},\n", + " 'equilibration_length': {'unit': 'nanosecond',\n", + " 'val': 1.0},\n", " 'minimization_steps': 5000,\n", " 'n_replicas': 11,\n", - " 'production_length': ,\n", - " 'real_time_analysis_interval': ,\n", - " 'real_time_analysis_minimum_time': ,\n", + " 'production_length': {'unit': 'nanosecond',\n", + " 'val': 5.0},\n", + " 'real_time_analysis_interval': {'unit': 'picosecond',\n", + " 'val': 250.0},\n", + " 'real_time_analysis_minimum_time': {'unit': 'picosecond',\n", + " 'val': 500.0},\n", " 'sampler_method': 'repex',\n", " 'sams_flatness_criteria': 'logZ-flatness',\n", " 'sams_gamma0': 1.0,\n", - " 'time_per_iteration': },\n", - " 'solvation_settings': {'box_shape': 'cube',\n", + " 'time_per_iteration': {'unit': 'picosecond',\n", + " 'val': 2.5}},\n", + " 'solvation_settings': {'box_shape': 'dodecahedron',\n", " 'box_size': None,\n", " 'box_vectors': None,\n", " 'number_of_solvent_molecules': None,\n", " 'solvent_model': 'tip3p',\n", - " 'solvent_padding': },\n", + " 'solvent_padding': {'unit': 'nanometer', 'val': 1.5}},\n", " 'thermo_settings': {'ph': None,\n", - " 'pressure': ,\n", + " 'pressure': {'unit': 'bar', 'val': 1},\n", " 'redox_potential': None,\n", - " 'temperature': }}\n" + " 'temperature': {'unit': 'kelvin', 'val': 298.15}}}\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "/Users/atravitz/micromamba/envs/openfe-conda/lib/python3.11/site-packages/gufe/settings/models.py:30: PydanticDeprecatedSince20: The `dict` method is deprecated; use `model_dump` instead. Deprecated in Pydantic V2.0 to be removed in V3.0. See Pydantic V2 Migration Guide at https://errors.pydantic.dev/2.11/migration/\n", + " pprint.pprint(self.dict())\n" ] } ], @@ -318,11 +336,11 @@ " ],\n", " \n", " # Small molecule force field to use with OpenMM template generator:\n", - " small_molecule_forcefield='openff-2.1.1',\n", + " small_molecule_forcefield='openff-2.2.1',\n", " \n", " # Nonbonded settings\n", " nonbonded_method='PME', # Particle Mesh Ewald for long range electrostatics\n", - " nonbonded_cutoff=1.0 * unit.nm, # Cut off Lennard-Jones interactions beyond 1 nm\n", + " nonbonded_cutoff=0.9 * unit.nm, # Cut off Lennard-Jones interactions beyond 0.9 nm\n", " ),\n", " thermo_settings=equil_rfe_settings.ThermoSettings(\n", " temperature=298.15 * unit.kelvin, # Set thermostat temperature\n", @@ -332,8 +350,8 @@ " ),\n", " solvation_settings=equil_rfe_settings.OpenMMSolvationSettings(\n", " solvent_model='tip3p', # Solvent model to generate starting coords\n", - " solvent_padding=1.2 * unit.nm, # Total distance between periodic image starting coords\n", - " box_shape = 'cube', # Cubic water box\n", + " solvent_padding=1.5 * unit.nm, # Minimum padding distance from the solute\n", + " box_shape = 'dodecahedron', # Dodecahedron water box\n", " box_size = None, # Size of the water box\n", " box_vectors = None, # Box vectors\n", " number_of_solvent_molecules = None, # Number of solvent molecules\n", @@ -372,7 +390,7 @@ " # Alchemical Space Sampling settings\n", " n_replicas=11, # Number of replicas sampling alchemical space\n", " sampler_method='repex', # Sample lambda with Hamiltonian Replica Exchange\n", - " time_per_iteration=1*unit.ps, # Time interval between state sampling (MCMC) attempts\n", + " time_per_iteration=2.5*unit.ps, # Time interval between state sampling (MCMC) attempts\n", " \n", " # SAMS sampling settings (used if sampler_method='sams')\n", " sams_flatness_criteria='logZ-flatness', # Criteria for switch to asymptomatically optimal scheme\n", @@ -387,7 +405,7 @@ " early_termination_target_error=0.0*unit.kilocalorie_per_mole,\n", " ),\n", " engine_settings=equil_rfe_settings.OpenMMEngineSettings(\n", - " compute_platform=None, # Let OpenMM choose the best platform for your hardware\n", + " compute_platform=\"cuda\", # Force the usage of a CUDA device for compute\n", " ),\n", " integrator_settings=equil_rfe_settings.IntegratorSettings(\n", " timestep=4 * unit.femtosecond, # Integration timestep\n", @@ -466,6 +484,14 @@ "source": [ "Unlike `ProtocolSettings`, a `Protocol` instance is immutable. The only way to safely change the settings of a `Protocol` is to recreate it from the modified `ProtocolSettings` object." ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "91e223bc-5dc7-4533-ba5d-da112809be9a", + "metadata": {}, + "outputs": [], + "source": [] } ], "metadata": { @@ -484,7 +510,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.12.10" + "version": "3.11.14" }, "widgets": { "application/vnd.jupyter.widget-state+json": { diff --git a/cookbook/create_alchemical_network.ipynb b/cookbook/create_alchemical_network.ipynb index edebab0..ce6c81c 100644 --- a/cookbook/create_alchemical_network.ipynb +++ b/cookbook/create_alchemical_network.ipynb @@ -90,7 +90,13 @@ "metadata": {}, "outputs": [], "source": [ - "protocol = RelativeHybridTopologyProtocol(RelativeHybridTopologyProtocol.default_settings())" + "# Create a protocol for the solvent and complex legs\n", + "solvent_protocol = RelativeHybridTopologyProtocol(RelativeHybridTopologyProtocol.default_settings())\n", + "\n", + "# For the complex, we reduce the solvation padding to avoid having too many waters\n", + "complex_settings = RelativeHybridTopologyProtocol.default_settings()\n", + "complex_settings.solvation_settings.solvent_padding = 1 * unit.nanometer\n", + "complex_protocol = RelativeHybridTopologyProtocol(complex_settings)" ] }, { @@ -131,57 +137,19 @@ "## Create the Alchemical Network" ] }, - { - "cell_type": "markdown", - "id": "8b2f6271-3c89-4cd6-8283-e617d535b910", - "metadata": {}, - "source": [ - "### Automatically" - ] - }, - { - "cell_type": "markdown", - "id": "cc13516d-92b6-44ac-a3eb-20e7f9e00631", - "metadata": {}, - "source": [ - "The `LigandNetwork.to_rbfe_alchemical_network()` method makes constructing alchemical networks for relative binding free energy calculations very simple: " - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "id": "733dee64-d23a-4eb0-87cf-c03065b8d2a6", - "metadata": {}, - "outputs": [], - "source": [ - "alchemical_network_auto = ligand_network.to_rbfe_alchemical_network(\n", - " solvent=solvent,\n", - " protein=protein,\n", - " protocol=protocol,\n", - ")" - ] - }, - { - "cell_type": "markdown", - "id": "35774b4f-83e7-4235-960b-aa591e20e016", - "metadata": {}, - "source": [ - "### Manually" - ] - }, { "cell_type": "markdown", "id": "0da5558b-3894-4c11-aafd-e4478972e161", "metadata": {}, "source": [ - "If your needs are not catered to by the above method, you can instead loop over the `LigandNetwork` edges and manually create the `Transformation` objects for each of them. This gives you full control over the entire network. For more information, see [Under the Hood]:\n", + "To manually create a network, you must loop over the `LigandNetwork` edges and manually create the `Transformation` objects for each of them. This gives you full control over the entire network. For more information, see [Under the Hood]:\n", "\n", "[Under the Hood]: https://docs.openfree.energy/en/stable/cookbook/under_the_hood.html" ] }, { "cell_type": "code", - "execution_count": 6, + "execution_count": 5, "id": "c49f36b1-c3c1-425e-b1de-24da25ed01c3", "metadata": {}, "outputs": [], @@ -219,6 +187,11 @@ " },\n", " name=f\"{mapping.componentB.name}_{leg}\"\n", " )\n", + "\n", + " if leg == \"solvent\":\n", + " protocol = solvent_protocol\n", + " else:\n", + " protocol = complex_protocol\n", " \n", " transformation = openfe.Transformation(\n", " stateA=system_a,\n", @@ -242,7 +215,7 @@ }, { "cell_type": "code", - "execution_count": 7, + "execution_count": 6, "id": "fc6cac68-bdcd-4d51-b4e1-f9bb7169c6f6", "metadata": {}, "outputs": [], @@ -250,33 +223,6 @@ "alchemical_network = openfe.AlchemicalNetwork(transformations)" ] }, - { - "cell_type": "markdown", - "id": "b623d82e-1069-4c69-81af-ccc795ba5690", - "metadata": {}, - "source": [ - "We can confirm that this produces identical results to the previous strategy:" - ] - }, - { - "cell_type": "code", - "execution_count": 8, - "id": "c9baa54c-513a-486a-a06a-fd8fd5d19fa5", - "metadata": { - "slideshow": { - "slide_type": "" - }, - "tags": [] - }, - "outputs": [], - "source": [ - "assert alchemical_network == ligand_network.to_rbfe_alchemical_network(\n", - " solvent=solvent,\n", - " protein=protein,\n", - " protocol=protocol,\n", - ")" - ] - }, { "cell_type": "markdown", "id": "b17669f6-a205-420d-96c3-1a9c92d0e13b", @@ -307,7 +253,7 @@ }, { "cell_type": "code", - "execution_count": 9, + "execution_count": 7, "id": "2da4dafb-20d6-4ce7-ab83-4690528097d6", "metadata": { "slideshow": { @@ -324,8 +270,16 @@ "\n", "for n, transformation in enumerate(alchemical_network.edges):\n", " transformation_name = transformation.name or transformation.key\n", - " transformation.dump(transformations_dir / f\"{n}_{transformation_name}.json\")" + " transformation.to_json(transformations_dir / f\"{n}_{transformation_name}.json\")" ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "86eeead8-7db5-4240-ba30-dbbc218d65b5", + "metadata": {}, + "outputs": [], + "source": [] } ], "metadata": { @@ -344,11 +298,109 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.11.6" + "version": "3.11.14" }, "widgets": { "application/vnd.jupyter.widget-state+json": { - "state": {}, + "state": { + "080168055e8d4fb38744e1655591160f": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "2.0.0", + "model_name": "HBoxModel", + "state": { + "children": [ + "IPY_MODEL_12f05cbed4114aed81798e2ff8aa732c", + "IPY_MODEL_ea486d173f684c82985e5fba88813736", + "IPY_MODEL_0896ab0832af4cdc88b4b041fdb8eb91" + ], + "layout": "IPY_MODEL_d0b9e988953642bf98ec890d68071d08" + } + }, + "0896ab0832af4cdc88b4b041fdb8eb91": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "2.0.0", + "model_name": "HTMLModel", + "state": { + "layout": "IPY_MODEL_483b78826369478293aede7c5835951c", + "style": "IPY_MODEL_e04d82d3520641108da5a753540fa09d", + "value": " 21/21 [00:00<00:00, 307.01it/s]" + } + }, + "12f05cbed4114aed81798e2ff8aa732c": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "2.0.0", + "model_name": "HTMLModel", + "state": { + "layout": "IPY_MODEL_dd33c019e00746a2a2693ae1976e6725", + "style": "IPY_MODEL_ee318b1d4f134ef2b661e761c4794e50", + "value": "Mapping: 100%" + } + }, + "2848e9cf70524c74af6d59c7fbcf04c0": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "2.0.0", + "model_name": "ProgressStyleModel", + "state": { + "description_width": "" + } + }, + "2a7a60e91deb4434ad68a7f1d149f611": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "2.0.0", + "model_name": "LayoutModel", + "state": {} + }, + "483b78826369478293aede7c5835951c": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "2.0.0", + "model_name": "LayoutModel", + "state": {} + }, + "d0b9e988953642bf98ec890d68071d08": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "2.0.0", + "model_name": "LayoutModel", + "state": {} + }, + "dd33c019e00746a2a2693ae1976e6725": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "2.0.0", + "model_name": "LayoutModel", + "state": {} + }, + "e04d82d3520641108da5a753540fa09d": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "2.0.0", + "model_name": "HTMLStyleModel", + "state": { + "description_width": "", + "font_size": null, + "text_color": null + } + }, + "ea486d173f684c82985e5fba88813736": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "2.0.0", + "model_name": "FloatProgressModel", + "state": { + "bar_style": "success", + "layout": "IPY_MODEL_2a7a60e91deb4434ad68a7f1d149f611", + "max": 21, + "style": "IPY_MODEL_2848e9cf70524c74af6d59c7fbcf04c0", + "value": 21 + } + }, + "ee318b1d4f134ef2b661e761c4794e50": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "2.0.0", + "model_name": "HTMLStyleModel", + "state": { + "description_width": "", + "font_size": null, + "text_color": null + } + } + }, "version_major": 2, "version_minor": 0 }