-
Notifications
You must be signed in to change notification settings - Fork 54
Let the force field resolve the partial charge assignment #2152
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from 8 commits
cd183a8
d1ecb40
46c5bbf
f372a16
7c0575e
00692ec
ebf4e78
3eb3600
d700702
5083dd0
38ff7c6
5fbd1ce
aec1d35
5616dc1
b9a332f
8c4421f
76924a4
5610478
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,23 @@ | ||
| **Added:** | ||
|
|
||
| * The ``assign_offmol_partial_charges`` and ``bulk_assign_partial_charges`` functions can assign charges from a list of OpenFF SMIRNOFF style force fields. Set method=``forcefield`` and provide a list of force field files via the new keyword argument ``forcefields``. This is also supported in the ``charge-molecules`` CLI command and is set by using a yaml settings file. | ||
|
|
||
| **Changed:** | ||
|
|
||
| * <news item> | ||
|
|
||
| **Deprecated:** | ||
|
|
||
| * <news item> | ||
|
|
||
| **Removed:** | ||
|
|
||
| * <news item> | ||
|
|
||
| **Fixed:** | ||
|
|
||
| * <news item> | ||
|
|
||
| **Security:** | ||
|
|
||
| * <news item> |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -11,6 +11,7 @@ | |
|
|
||
| import numpy as np | ||
| from gufe import SmallMoleculeComponent | ||
| from openff.toolkit import ForceField | ||
| from openff.toolkit import Molecule as OFFMol | ||
| from openff.toolkit.utils.base_wrapper import ToolkitWrapper | ||
| from openff.toolkit.utils.toolkit_registry import ToolkitRegistry | ||
|
|
@@ -286,10 +287,11 @@ def _generate_offmol_conformers( | |
| def assign_offmol_partial_charges( | ||
| offmol: OFFMol, | ||
| overwrite: bool, | ||
| method: Literal["am1bcc", "am1bccelf10", "nagl", "espaloma"], | ||
| method: Literal["am1bcc", "am1bccelf10", "nagl", "espaloma", "forcefield"], | ||
| toolkit_backend: Literal["ambertools", "openeye", "rdkit"], | ||
| generate_n_conformers: int | None, | ||
| nagl_model: str | None, | ||
| forcefields: list[str] | None = None, | ||
| ) -> OFFMol: | ||
| """ | ||
| Assign partial charges to an OpenFF Molecule based on a selected method. | ||
|
|
@@ -299,11 +301,11 @@ def assign_offmol_partial_charges( | |
| offmol : openff.toolkit.Molecule | ||
| The Molecule to assign partial charges to. | ||
| overwrite : bool | ||
| Whether or not to overwrite any existing non-zero partial charges. | ||
| Whether to overwrite any existing non-zero partial charges. | ||
| Note that zeroed charges will always be overwritten. | ||
| method : Literal['am1bcc', 'am1bccelf10', 'nagl', 'espaloma'] | ||
| method : Literal['am1bcc', 'am1bccelf10', 'nagl', 'espaloma', 'forcefield'] | ||
| Partial charge assignment method. | ||
| Supported methods include; am1bcc, am1bccelf10, nagl, and espaloma. | ||
| Supported methods include; am1bcc, am1bccelf10, nagl, espaloma and forcefield. | ||
| toolkit_backend : Literal['ambertools', 'openeye', 'rdkit'] | ||
| OpenFF toolkit backend employed for charge generation. | ||
| Supported options: | ||
|
|
@@ -319,6 +321,15 @@ def assign_offmol_partial_charges( | |
| nagl_model : str | None | ||
| The NAGL model to use for charge assignment if method is ``nagl``. | ||
| If ``None``, the latest am1bcc NAGL charge model is used. | ||
| forcefields : list[str] | None, default None | ||
| An optional list of SMIRNOFF style force field offxml paths or strings which should be used to assign partial charges. | ||
|
|
||
| Notes | ||
| ----- | ||
| Charges are applied based on the following source preferences: | ||
| - Charges already present on the ligand are retained if overwrite is ``False`` | ||
| - Charges are applied using the input method and settings | ||
| - the forcefield option will apply the default charges as intended by the force field. | ||
|
IAlibay marked this conversation as resolved.
Outdated
|
||
|
|
||
| Raises | ||
| ------ | ||
|
|
@@ -339,6 +350,25 @@ def assign_offmol_partial_charges( | |
| if not overwrite: | ||
| return offmol | ||
|
|
||
| if method.lower() == "forcefield": | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can we have a check for the other way around too? I'm thinking new users might not easily know you need to set both - especially via the CLI.
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Add the reverse check and test. |
||
| if forcefields is None: | ||
| errmsg = ( | ||
| "The forcefield method requires a force field or list of force fields' to be provided " | ||
| "via `forcefields`." | ||
| ) | ||
| raise ValueError(errmsg) | ||
|
|
||
| if isinstance(forcefields, str): | ||
| forcefields = [forcefields] | ||
|
|
||
| # this expects the full file name of the force field offxml file, e.g. "openff-2.0.0.offxml" | ||
| # which is different to how settings work which can leave off the .offxml extension | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think now we can use Also what happens if this encounters a non
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Updated to accept both and added a test, and yes maybe the default should now have the extension as well.
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can you open a PR on gufe to update the default please? |
||
| ff = ForceField(*forcefields) | ||
| # let the force field resolve the partial charge assignment | ||
| charges = ff.get_partial_charges(offmol) | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I believe this needs to get wrapped around
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Probably but which registry would we use as it could influence the charge method used? I think the default is openeye and am1bccelf10 and then fall back to AmberToolsam1bcc?
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Just use what the user has in the settings for the toolkit_backend and make it clear in the docs that the backend is always followed when a charge is generated.
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ah this doesn't work if the force field uses nagl charges, as our default is ambertools, we might need to use a different method to make the registry. Maybe something like:
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Sorry I don't understand why it's not working. The "AmberTools" backend is AmberTools + RDKit, that should be enough for NAGL to work no?
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ah ok - the issue is that the NAGL registry isn't in there?
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think it might be ok to just add the NAGLToolkitWrapper to both AmberTools & OpenEye backend lists - please double check but I think it will still do the "protection" that we're trying to do (i.e. it will block you from doing am1bcc with openeye if you don't want it).
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Add the wrapper to all backends if nagl is available which I think is what we want? |
||
| offmol.partial_charges = charges | ||
| return offmol | ||
|
|
||
| # Dictionary for each available charge method | ||
| # The idea of this pattern is to allow for maximum flexibility by | ||
| # allowing for swapping out method calls as necessary. | ||
|
|
@@ -441,11 +471,12 @@ def assign_offmol_partial_charges( | |
| def bulk_assign_partial_charges( | ||
| molecules: list[SmallMoleculeComponent], | ||
| overwrite: bool, | ||
| method: Literal["am1bcc", "am1bccelf10", "nagl", "espaloma"], | ||
| method: Literal["am1bcc", "am1bccelf10", "nagl", "espaloma", "forcefield"], | ||
| toolkit_backend: Literal["ambertools", "openeye", "rdkit"], | ||
| generate_n_conformers: int | None, | ||
| nagl_model: str | None, | ||
| processors: int = 1, | ||
| forcefields: list[str] | None = None, | ||
| ) -> list[SmallMoleculeComponent]: | ||
| """ | ||
| Assign partial charges to a list of SmallMoleculeComponents using multiprocessing. | ||
|
|
@@ -457,7 +488,7 @@ def bulk_assign_partial_charges( | |
| overwrite : bool | ||
| Whether or not to overwrite any existing non-zero partial charges. | ||
| Note that zeroed charges will always be overwritten. | ||
| method : Literal['am1bcc', 'am1bccelf10', 'nagl', 'espaloma'] | ||
| method : Literal['am1bcc', 'am1bccelf10', 'nagl', 'espaloma', 'forcefield] | ||
| Partial charge assignment method. | ||
| Supported methods include; am1bcc, am1bccelf10, nagl, and espaloma. | ||
| toolkit_backend : Literal['ambertools', 'openeye', 'rdkit'] | ||
|
|
@@ -477,6 +508,8 @@ def bulk_assign_partial_charges( | |
| If ``None``, the latest am1bcc NAGL charge model is used. | ||
| processors: int, default 1 | ||
| The number of processors which should be used to generate the charges. | ||
| forcefields : list[str] | None, default None | ||
| An optional list of SMIRNOFF style force field offxml paths or strings which should be used to assign partial charges. | ||
|
|
||
| Raises | ||
| ------ | ||
|
|
@@ -499,6 +532,7 @@ def bulk_assign_partial_charges( | |
| "toolkit_backend": toolkit_backend, | ||
| "generate_n_conformers": generate_n_conformers, | ||
| "nagl_model": nagl_model, | ||
| "forcefields": forcefields, | ||
| } | ||
|
|
||
| if processors > 1: | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -223,6 +223,7 @@ def load_yaml_planner_options(path: Optional[str], context) -> PlanNetworkOption | |
| off_toolkit_backend: ambertools | ||
| number_of_conformers: None | ||
| nagl_model: None | ||
| forcefields: None | ||
|
IAlibay marked this conversation as resolved.
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. How about including an example of this under the settings help section?
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. That section is already getting quite big, I think it would be better to point to the docs and have small examples there that users can copy for some different options, this would simplify the CLI help message as well! |
||
| """ | ||
|
|
||
| _yaml_help = """ | ||
|
|
@@ -245,6 +246,7 @@ def load_yaml_planner_options(path: Optional[str], context) -> PlanNetworkOption | |
| - ``am1bccelf10`` (only possible if ``off_toolkit_backend`` is ``openeye``) | ||
| - ``nagl`` (must have openff-nagl installed) | ||
| - ``espaloma`` (must have espaloma_charge installed) | ||
| - ``forcefield`` (must supply the chosen force field files via the ``forcefields`` keyword argument. This is useful to get the correct AshGC model or LibraryCharges for a OpenFF force field.) | ||
|
|
||
| ``settings:`` allows for passing in any keyword arguments of the method's corresponding Python API. | ||
|
|
||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.