openfe icon indicating copy to clipboard operation
openfe copied to clipboard

Using OPC water models for RBFE calculations

Open palominohernandez opened this issue 1 year ago • 6 comments
trafficstars

Dear all,

I have been trying to use other water models such as OPC (a 4 point model).

I thought that intuitively it should be as simple as:

rbfe_settings = RelativeHybridTopologyProtocol.default_settings()

rbfe_settings.forcefield_settings.forcefields=["amber/RNA.OL3.xml","amber/opc_standard.xml"]
rbfe_settings.solvation_settings.solvent_model="opc"

However, I get an error that says:

File /data/sw/mamba/envs/openfe_env/lib/python3.12/site-packages/pydantic/v1/main.py:384, in BaseModel.__setattr__(self, name, value)
    [382](https://vscode-remote+ssh-002dremote-002blinux.vscode-resource.vscode-cdn.net/data/sw/mamba/envs/openfe_env/lib/python3.12/site-packages/pydantic/v1/main.py:382) value, error_ = known_field.validate(value, dict_without_original_value, loc=name, cls=self.__class__)
    [383](https://vscode-remote+ssh-002dremote-002blinux.vscode-resource.vscode-cdn.net/data/sw/mamba/envs/openfe_env/lib/python3.12/site-packages/pydantic/v1/main.py:383) if error_:
--> [384](https://vscode-remote+ssh-002dremote-002blinux.vscode-resource.vscode-cdn.net/data/sw/mamba/envs/openfe_env/lib/python3.12/site-packages/pydantic/v1/main.py:384)     raise ValidationError([error_], self.__class__)
    [385](https://vscode-remote+ssh-002dremote-002blinux.vscode-resource.vscode-cdn.net/data/sw/mamba/envs/openfe_env/lib/python3.12/site-packages/pydantic/v1/main.py:385) else:
    [386](https://vscode-remote+ssh-002dremote-002blinux.vscode-resource.vscode-cdn.net/data/sw/mamba/envs/openfe_env/lib/python3.12/site-packages/pydantic/v1/main.py:386)     new_values[name] = value

ValidationError: 1 validation error for OpenMMSolvationSettings
solvent_model
  unexpected value; permitted: 'tip3p', 'spce', 'tip4pew', 'tip5p' (type=value_error.const; given=opc; permitted=('tip3p', 'spce', 'tip4pew', 'tip5p'))

I tried to go deep in the gufe rabbit hole yet it was a bit unclear.

I thought I could just set up another 4 point model such as tip4pew and run the system with it, as the opc parameters should override the tip4pew setup. Doing this, I could write the transformations to disk. However, when running the json files with openfe quickrun, I got the following output:

Done with all simulations! Analyzing the results....
Here is the result:
        dG = None ± None

Error: The protocol unit 'LIG1 to LIG2 repeat 0 generation 0' failed with the error message:
ValueError: Residue template U with the same override level 0 already exists.

Details provided in output.

For reference, the same simulation runs properly when using the default force field parameters.

Thanks, O.

palominohernandez avatar Jun 13 '24 19:06 palominohernandez

Thanks @palominohernandez - unfortunately I don't think we've ever tested out simulations with the opc models.

My initial thought here is that openmm.Modeller.addSolvent just doesn't support it properly yet (hence why you are getting a validation error when trying to set the solvent_model settings to 'opc'.

There is likely a way to fix this, but we'll need to triage exactly what is going wrong & how much work fixing it will take.

IAlibay avatar Jun 18 '24 23:06 IAlibay

I believe that the issue is we only allow a subset of the solvation models that https://github.com/openmm/openmmforcefields supports. Looking at http://docs.openmm.org/latest/api-python/generated/openmm.app.modeller.Modeller.html#openmm.app.modeller.Modeller.addSolvent @IAlibay is correct that addSolvent only supports the models we allow in the settings:

model (str='tip3p') – the water model to use. Supported values are ‘tip3p’, ‘spce’, ‘tip4pew’, ‘tip5p’, and ‘swm4ndp’ (polarizable).

https://github.com/OpenFreeEnergy/openfe/blob/6aaf7e30eea4fd65255fc6660923b33c2156c6e3/openfe/protocols/openmm_utils/omm_settings.py#L46

There are other ways to solvate a system, but our current OpenMM protocols use Modeller.addSolvent AFAIK

mikemhenry avatar Jul 03 '24 14:07 mikemhenry

Thanks! Do I get it from the comments that there is no easy fix at the moment, correct?

palominohernandez avatar Jul 03 '24 17:07 palominohernandez

Just ran into this and I think the method described by @palominohernandez to use a similar supported water model like tip4pew to solvate the system and then the opc parameters to simulate after minimisation should be fine and follows the recommendations in the OpenMM docs:

There exist many different water models, many of which are very similar to each other. This method creates preequilibrated water boxes for a limited set of water models. In most cases, they work equally well for other models that involve the same number of particles. For example, to simulate a box of TIP3P-FB water, use this method to create a box of TIP3P water, construct a System using TIP3P-FB parameters, and perform a local energy minimization to correct for the small differences between the models. Likewise, a box of TIP4P-Ew water can be used for most four site water models.

This works with tip4p-fb in the following script, I think the issue with OPC is in the XML file in openmmtools. I noticed that this error only occurs if I load another force field with a residue named U. The amber/ff14SB.xml contains a residue named U which then clashes with some parameters in the opc ions file.

import string
import click
import tempfile
import pathlib
import gufe
from openff.units import unit
import openfe
from openfe.protocols.openmm_md.plain_md_methods import PlainMDProtocol
from rdkit import Chem


def get_settings():
    """
    Utility method for getting MDProtocol settings.

    These settings mostly follow defaults but use very short
    simulation times to avoid being too much of a burden on users' machines.
    """
    settings = openfe.protocols.openmm_md.plain_md_methods.PlainMDProtocol.default_settings()
    settings.simulation_settings.equilibration_length_nvt = 1 * unit.picosecond
    settings.simulation_settings.equilibration_length = 1 * unit.picosecond
    settings.simulation_settings.production_length = 1 * unit.picosecond
    settings.forcefield_settings.forcefields=[
        "amber/ff14SB.xml",  # ff14SB protein force field
        "amber/tip4pfb_standard.xml",  # TIP4P and recommended monovalent ion parameters
        "amber/phosaa10.xml",  # Handles THE TPO
        ]
    settings.solvation_settings.solvent_model="tip4pew"
    return settings


def run_md(dag):
    """
    Run a DAG and check it was ok.

    Parameters
    ----------
    dag : openfe.ProtocolDAG
      A ProtocolDAG to execute.

    Raises
    ------
    AssertionError
      If any of the simulation Units failed.
    """
    with tempfile.TemporaryDirectory() as tmpdir:
        workdir = pathlib.Path(tmpdir)
        dagres = gufe.protocols.execute_DAG(
            dag,
            shared_basedir=workdir,
            scratch_basedir=workdir,
            keep_shared=False,
            raise_error=True,
            n_retries=0,
        )

    # If everything is good then tell the user
    assert dagres.ok()
    print("SIMULATION COMPLETE")


@click.command
@click.option(
    '--pdb',
    type=click.Path(dir_okay=False, file_okay=True, path_type=pathlib.Path),
    required=True,
    help="Path to the prepared PDB file to validate",
)
@click.option(
    '--cofactors',
    type=click.Path(dir_okay=False, file_okay=True, path_type=pathlib.Path),
    default=None,
    help="Path to the prepared cofactors SDF file (optional)",
)
def run_inputs(pdb, cofactors):
    """
    Validate input files by running a short MD simulation

    Parameters
    ----------
    pdb : pathlib.Path
      A Path to a protein PDB file.
    cofactors : Optional[pathlib.Path]
      A Path to an SDF file containing the system's cofactors.
    """
    # Create the solvent and protein components
    solv = openfe.SolventComponent()
    prot = openfe.ProteinComponent.from_pdb_file(str(pdb))

    # Store there in a components dictionary
    components_dict = {
        'protein': prot,
        'solvent': solv,
    }

    # If we have cofactors, populate them and store them based on
    # an single letter index (we assume no more than len(alphabet) cofactors)
    if cofactors is not None:
        cofactors = [
            openfe.SmallMoleculeComponent(m)
            for m in Chem.SDMolSupplier(str(cofactors), removeHs=False)
        ]

        for cofactor, entry in zip(cofactors, string.ascii_lowercase):
            components_dict[entry] = cofactor

    # Create the ChemicalSystem
    system = openfe.ChemicalSystem(components_dict)

    # Get the settings and create the protocol
    settings = get_settings()
    protocol = PlainMDProtocol(settings=settings)

    # Now create the DAG and run it
    dag = protocol.create(stateA=system, stateB=system, mapping=None)
    run_md(dag)


if __name__ == "__main__":
    run_inputs()

jthorton avatar Jan 31 '25 14:01 jthorton

@jthorton can I take from this that a locally modified version of that xml works?

IAlibay avatar Jan 31 '25 15:01 IAlibay

@jthorton can I take from this that a locally modified version of that xml works?

Yeah that works locally!

jthorton avatar Jan 31 '25 15:01 jthorton