Example #1
0
    def get_parameter_handler_from_forcefield(self, parameter_handler_name,
                                              forcefield):
        """
        It returns a parameter handler from the forcefield based on its
        name.

        Parameters
        ----------
        parameter_handler_name : str
            The name of the parameter handler that is requested
        forcefield : an openforcefield.typing.engines.smirnoff.ForceField
                     object
            The forcefield from which the parameter handler will be obtained

        Returns
        -------
        parameter_handler : an openforcefield.typing.engines.smirnoff.parameters.ParameterHandler
                            object
            The ParameterHandler that was requested
        """
        from openforcefield.typing.engines.smirnoff import ForceField

        if isinstance(forcefield, str):
            forcefield = ForceField(forcefield)
        elif isinstance(forcefield, ForceField):
            pass
        else:
            raise Exception('Invalid forcefield type')

        return forcefield.get_parameter_handler(parameter_handler_name)
Example #2
0
def test_adding_params_parameterize_flag():
    """
    Test adding new smirks patterns with cosmetic attributes.
    """

    ff = ForceFieldEditor(forcefield_name="openff-1.0.0.offxml")
    # add an atom smirks for boron
    boron = AtomSmirks(smirks="[#5:1]", parameterize={"epsilon"}, atoms={(0,)}, epsilon=0.04, rmin_half=3)
    # add boron with the flag
    ff.add_smirks(smirks=[boron, ], parameterize=True)
    with temp_directory():
        ff.forcefield.to_file(filename="boron.offxml")

        # this should fail if the flag was added
        with pytest.raises(SMIRNOFFSpecError):
            _ = ForceField("boron.offxml", allow_cosmetic_attributes=False)

        boron_ff = ForceField("boron.offxml", allow_cosmetic_attributes=True)
        # now look for the parameter we added
        boron_param = boron_ff.get_parameter_handler(
            "vdW"
        ).parameters["[#5:1]"]
        # now make sure it has the param flag
        param_dict = boron_param.__dict__
        assert param_dict["_cosmetic_attribs"] == ["parameterize"]
        assert param_dict["_parameterize"] == "epsilon"
Example #3
0
def test_force_field_custom_handler(mock_entry_point_plugins):
    """Tests a force field can make use of a custom parameter handler registered
    through the entrypoint plugin system.
    """

    # Construct a simple FF which only uses the custom handler.
    force_field_contents = "\n".join([
        "<?xml version='1.0' encoding='ASCII'?>",
        "<SMIRNOFF version='0.3' aromaticity_model='OEAroModel_MDL'>",
        "  <CustomHandler version='0.3'></CustomHandler>", "</SMIRNOFF>"
    ])

    # An exception should be raised when plugins aren't allowed.
    with pytest.raises(KeyError) as error_info:
        ForceField(force_field_contents)

    assert (
        "Cannot find a registered parameter handler class for tag 'CustomHandler'"
        in error_info.value.args[0])

    # Otherwise the FF should be created as expected.
    force_field = ForceField(force_field_contents, load_plugins=True)

    parameter_handler = force_field.get_parameter_handler("CustomHandler")
    assert parameter_handler is not None
    assert parameter_handler.__class__.__name__ == "CustomHandler"
Example #4
0
def mock_force_field(parameters: List[SMIRNOFFParameter]):
    """Mock a ForceBalance forcefield directory."""

    # Create the force field directory.
    force_field = ForceField("openff-1.2.0.offxml")

    parameters_to_fit = defaultdict(lambda: defaultdict(list))

    for parameter in parameters:
        parameters_to_fit[parameter.handler][parameter.smirks].append(
            parameter.attribute)

    for handler_name in parameters_to_fit:

        handler = force_field.get_parameter_handler(handler_name)

        for smirks in parameters_to_fit[handler_name]:
            openff_parameter = handler.parameters[smirks]
            attributes_string = ", ".join(
                parameters_to_fit[handler_name][smirks])

            openff_parameter.add_cosmetic_attribute("parameterize",
                                                    attributes_string)

    os.makedirs("forcefield", exist_ok=True)
    force_field.to_file(os.path.join("forcefield", "forcefield.offxml"))
Example #5
0
def test_update_parameters(smirks_data):
    """
    Test updating the smirks parameters using openff types.
    """
    smirk, result = smirks_data
    # load a forcefield
    ff = ForceField("openff-1.0.0.offxml")
    # all input smirks have parameters set to 0
    off_parameter = ff.get_parameter_handler(
        smirk.type).parameters[smirk.smirks]
    smirk.update_parameters(off_smirk=off_parameter)
    for param, value in result.items():
        assert getattr(smirk, param) == value
Example #6
0
def test_adding_new_smirks_types(smirks):
    """
    Test adding new smirks to a forcefield with and without the parameterize flag.
    """

    param, param_id = smirks
    ff = ForceFieldEditor("openff-1.0.0.offxml")
    # now make some new smirks pattern
    ff.add_smirks(smirks=[param, ], parameterize=True)
    # now make sure it was added under the correct parameter handler
    with temp_directory():
        ff.forcefield.to_file(filename="bespoke.offxml")

        new_ff = ForceField("bespoke.offxml", allow_cosmetic_attributes=True)
        parameter = new_ff.get_parameter_handler(param.type.value).parameters[param.smirks]
        param_dict = parameter.__dict__
        assert param_dict["_cosmetic_attribs"] == ["parameterize"]
        assert set(param_dict["_parameterize"].split()) == param.parameterize
        # make sure the id is correct
        assert param_id in parameter.id
Example #7
0
def generateSMIRNOFFStructure(oemol):
    """
    Given an OpenEye molecule (oechem.OEMol), create an OpenMM System and use to
    generate a ParmEd structure using the SMIRNOFF forcefield parameters.

    Parameters
    ----------
    oemol : openeye.oechem.OEMol
        OpenEye molecule

    Returns
    -------
    molecule_structure : parmed.Structure
        The resulting Structure

    """
    warnings.warn(DEPRECATION_WARNING_TEXT, PendingDeprecationWarning)
    from openforcefield.topology import Molecule, Topology
    from openforcefield.typing.engines.smirnoff import ForceField

    off_mol = Molecule.from_openeye(oemol)
    off_top = Topology.from_molecules([off_mol])
    mol_ff = ForceField('test_forcefields/smirnoff99Frosst.offxml')

    # Create OpenMM System and Topology.
    omm_top = generateTopologyFromOEMol(oemol)

    # If it's a nonperiodic box, then we can't use default (PME) settings
    if omm_top.getPeriodicBoxVectors() is None:
        mol_ff.get_parameter_handler("Electrostatics", {})._method = 'Coulomb'

    system = mol_ff.create_openmm_system(off_top)

    # Convert to ParmEd structure.
    import parmed
    xyz = extractPositionsFromOEMol(oemol)
    molecule_structure = parmed.openmm.load_topology(omm_top, system, xyz=xyz)

    return molecule_structure
Example #8
0
    def serialise_system(self):
        """Create the OpenMM system; parametrise using frost; serialise the system."""

        # Create an openFF molecule from the rdkit molecule
        off_molecule = Molecule.from_rdkit(self.molecule.rdkit_mol,
                                           allow_undefined_stereo=True)

        # Make the OpenMM system
        off_topology = off_molecule.to_topology()

        forcefield = ForceField("openff_unconstrained-1.0.0.offxml")

        try:
            # Parametrise the topology and create an OpenMM System.
            system = forcefield.create_openmm_system(off_topology)
        except (
                UnassignedValenceParameterException,
                UnassignedBondParameterException,
                UnassignedProperTorsionParameterException,
                UnassignedAngleParameterException,
                UnassignedMoleculeChargeException,
                TypeError,
        ):
            # If this does not work then we have a molecule that is not in SMIRNOFF so we must add generics
            # and remove the charge handler to get some basic parameters for the moleucle
            new_bond = BondHandler.BondType(
                smirks="[*:1]~[*:2]",
                length="0 * angstrom",
                k="0.0 * angstrom**-2 * mole**-1 * kilocalorie",
            )
            new_angle = AngleHandler.AngleType(
                smirks="[*:1]~[*:2]~[*:3]",
                angle="0.0 * degree",
                k="0.0 * mole**-1 * radian**-2 * kilocalorie",
            )
            new_torsion = ProperTorsionHandler.ProperTorsionType(
                smirks="[*:1]~[*:2]~[*:3]~[*:4]",
                periodicity1="1",
                phase1="0.0 * degree",
                k1="0.0 * mole**-1 * kilocalorie",
                periodicity2="2",
                phase2="180.0 * degree",
                k2="0.0 * mole**-1 * kilocalorie",
                periodicity3="3",
                phase3="0.0 * degree",
                k3="0.0 * mole**-1 * kilocalorie",
                periodicity4="4",
                phase4="180.0 * degree",
                k4="0.0 * mole**-1 * kilocalorie",
                idivf1="1.0",
                idivf2="1.0",
                idivf3="1.0",
                idivf4="1.0",
            )
            new_vdw = vdWHandler.vdWType(
                smirks="[*:1]",
                epsilon=0 * unit.kilocalories_per_mole,
                sigma=0 * unit.angstroms,
            )
            new_generics = {
                "Bonds": new_bond,
                "Angles": new_angle,
                "ProperTorsions": new_torsion,
                "vdW": new_vdw,
            }
            for key, val in new_generics.items():
                forcefield.get_parameter_handler(key).parameters.insert(0, val)
            # This has to be removed as sqm will fail with unknown elements
            del forcefield._parameter_handlers["ToolkitAM1BCC"]
            del forcefield._parameter_handlers["Electrostatics"]
            # Parametrize the topology and create an OpenMM System.
            system = forcefield.create_openmm_system(off_topology)
            # This will tag the molecule so run.py knows that generics have been used.
            self.fftype = "generics"
        # Serialise the OpenMM system into the xml file
        with open("serialised.xml", "w+") as out:
            out.write(XmlSerializer.serializeSystem(system))
def plot_parameter_changes(
    original_parameter_path,
    optimized_parameter_directory,
    study_names,
    parameter_smirks,
    output_directory,
):

    from simtk import unit as simtk_unit

    parameter_attributes = ["epsilon", "rmin_half"]
    default_units = {
        "epsilon": simtk_unit.kilocalories_per_mole,
        "rmin_half": simtk_unit.angstrom,
    }

    # Find the values of the original and optimized parameters.
    data_rows = []

    for study_name in study_names:

        original_force_field = ForceField(
            original_parameter_path,
            allow_cosmetic_attributes=True,
        )
        optimized_force_field = ForceField(
            os.path.join(optimized_parameter_directory,
                         f"{study_name}.offxml"),
            allow_cosmetic_attributes=True,
        )

        original_handler = original_force_field.get_parameter_handler("vdW")
        optimized_handler = optimized_force_field.get_parameter_handler("vdW")

        for parameter in original_handler.parameters:

            if parameter.smirks not in parameter_smirks:
                continue

            for attribute_type in parameter_attributes:

                original_value = getattr(parameter, attribute_type)
                optimized_value = getattr(
                    optimized_handler.parameters[parameter.smirks],
                    attribute_type)

                percentage_change = optimized_value - original_value

                data_row = {
                    "Study":
                    study_name,
                    "Smirks":
                    parameter.smirks,
                    "Attribute":
                    f"{attribute_type} ({default_units[attribute_type]})",
                    "Delta":
                    percentage_change.value_in_unit(
                        default_units[attribute_type]),
                }

                data_rows.append(data_row)

    parameter_data = pandas.DataFrame(data_rows)

    palette = seaborn.color_palette(n_colors=len(study_names))

    plot = seaborn.FacetGrid(parameter_data,
                             row="Attribute",
                             height=4.0,
                             aspect=2.0,
                             sharey=False)
    plot.map_dataframe(plot_categories,
                       "Smirks",
                       "Delta",
                       "Study",
                       color=palette)

    plot.add_legend()

    plot.savefig(os.path.join(output_directory, f"parameter_changes.png"))
class ForceFieldEditor:
    def __init__(self, forcefield_name: str):
        """
        Gather the forcefield ready for manipulation.

        Parameters
        ----------
        forcefield_name: str
            The string of the target forcefield path.

        Notes
        ------
            This will always try to strip the constraints parameter handler as the FF should be unconstrained for fitting.
        """
        self.forcefield = ForceField(forcefield_name,
                                     allow_cosmetic_attributes=True)

        # try and strip a constraint handler
        try:
            del self.forcefield._parameter_handlers["Constraints"]
        except KeyError:
            pass

    def add_smirks(
        self,
        smirks: List[Union[AtomSmirks, AngleSmirks, BondSmirks,
                           TorsionSmirks]],
        parameterize: bool = True,
    ) -> None:
        """
        Work out which type of smirks this is and add it to the forcefield, if this is not a bespoke parameter update the value in the forcefield.
        """

        _smirks_conversion = {
            SmirksType.Bonds: BondHandler.BondType,
            SmirksType.Angles: AngleHandler.AngleType,
            SmirksType.ProperTorsions: ProperTorsionHandler.ProperTorsionType,
            SmirksType.Vdw: vdWHandler.vdWType,
        }
        _smirks_ids = {
            SmirksType.Bonds: "b",
            SmirksType.Angles: "a",
            SmirksType.ProperTorsions: "t",
            SmirksType.Vdw: "n",
        }
        new_params = {}
        for smirk in smirks:
            if smirk.type not in new_params:
                new_params[smirk.type] = [
                    smirk,
                ]
            else:
                if smirk not in new_params[smirk.type]:
                    new_params[smirk.type].append(smirk)

        for smirk_type, parameters in new_params.items():
            current_params = self.forcefield.get_parameter_handler(
                smirk_type).parameters
            no_params = len(current_params)
            for i, parameter in enumerate(parameters, start=2):
                smirk_data = parameter.to_off_smirks()
                if not parameterize:
                    del smirk_data["parameterize"]
                # check if the parameter is new
                try:
                    current_param = current_params[parameter.smirks]
                    smirk_data["id"] = current_param.id
                    # update the parameter using the init to get around conditional assigment
                    current_param.__init__(**smirk_data)
                except IndexError:
                    smirk_data["id"] = _smirks_ids[smirk_type] + str(
                        no_params + i)
                    current_params.append(
                        _smirks_conversion[smirk_type](**smirk_data))

    def label_molecule(self, molecule: off.Molecule) -> Dict[str, str]:
        """
        Type the molecule with the forcefield and return a molecule parameter dictionary.

        Parameters
        ----------
        molecule: off.Molecule
            The openforcefield.topology.Molecule that should be labeled by the forcefield.

        Returns
        -------
        Dict[str, str]
            A dictionary of each parameter assigned to molecule organised by parameter handler type.
        """
        return self.forcefield.label_molecules(molecule.to_topology())[0]

    def get_smirks_parameters(
        self, molecule: off.Molecule, atoms: List[Tuple[int, ...]]
    ) -> List[Union[AtomSmirks, AngleSmirks, BondSmirks, TorsionSmirks]]:
        """
        For a given molecule label it and get back the smirks patterns and parameters for the requested atoms.
        """
        _atoms_to_params = {
            1: SmirksType.Vdw,
            2: SmirksType.Bonds,
            3: SmirksType.Angles,
            4: SmirksType.ProperTorsions,
        }
        smirks = []
        labels = self.label_molecule(molecule=molecule)
        for atom_ids in atoms:
            # work out the parameter type from the length of the tuple
            smirk_class = _atoms_to_params[len(atom_ids)]
            # now we can get the handler type using the smirk type
            off_param = labels[smirk_class.value][atom_ids]
            smirk = smirks_from_off(off_smirks=off_param)
            smirk.atoms.add(atom_ids)
            if smirk not in smirks:
                smirks.append(smirk)
            else:
                # update the covered atoms
                index = smirks.index(smirk)
                smirks[index].atoms.add(atom_ids)
        return smirks

    def update_smirks_parameters(
        self,
        smirks: Iterable[Union[AtomSmirks, AngleSmirks, BondSmirks,
                               TorsionSmirks]],
    ) -> None:
        """
        Take a list of input smirks parameters and update the values of the parameters using the given forcefield in place.

        Parameters
        ----------
        smirks : Iterable[Union[AtomSmirks, AngleSmirks, BondSmirks, TorsionSmirks]]
            An iterable containing smirks schemas that are to be updated.

        """

        for smirk in smirks:
            new_parameter = self.forcefield.get_parameter_handler(
                smirk.type).parameters[smirk.smirks]
            # now we just need to update the smirks with the new values
            smirk.update_parameters(off_smirk=new_parameter)

    def get_initial_parameters(
        self,
        molecule: off.Molecule,
        smirks: List[Union[AtomSmirks, AngleSmirks, BondSmirks,
                           TorsionSmirks]],
        clear_existing: bool = True,
    ) -> List[Union[AtomSmirks, AngleSmirks, BondSmirks, TorsionSmirks]]:
        """
        Find the initial parameters assigned to the atoms in the given smirks pattern and update the values to match the forcefield.
        """
        labels = self.label_molecule(molecule=molecule)
        # now find the atoms
        for smirk in smirks:
            parameters = labels[smirk.type]
            if smirk.type == SmirksType.ProperTorsions:
                # here we can combine multiple parameter types
                # TODO is this needed?
                openff_params = []
                for atoms in smirk.atoms:
                    param = parameters[atoms]
                    openff_params.append(param)

                # now check if they are different types
                types = set([param.id for param in openff_params])

                # now update the parameter
                smirk.update_parameters(off_smirk=openff_params[0],
                                        clear_existing=clear_existing)
                # if there is more than expand the k terms
                if len(types) > 1:
                    for param in openff_params[1:]:
                        smirk.update_parameters(param, clear_existing=False)
            else:
                atoms = list(smirk.atoms)[0]
                param = parameters[atoms]
                smirk.update_parameters(off_smirk=param, clear_existing=True)

        return smirks
Example #11
0
    def _build_reduced_system(self, original_force_field, topology, scale_amount=None):
        """Produces an OpenMM system containing only forces for the specified parameter,
         optionally perturbed by the amount specified by `scale_amount`.

        Parameters
        ----------
        original_force_field: openforcefield.typing.engines.smirnoff.ForceField
            The force field to create the system from (and optionally perturb).
        topology: openforcefield.topology.Topology
            The topology of the system to apply the force field to.
        scale_amount: float, optional
            The optional amount to perturb the parameter by.

        Returns
        -------
        simtk.openmm.System
            The created system.
        simtk.unit.Quantity
            The new value of the perturbed parameter.
        """
        # As this method deals mainly with the toolkit, we stick to
        # simtk units here.
        from openforcefield.typing.engines.smirnoff import ForceField

        parameter_tag = self.parameter_key.tag
        parameter_smirks = self.parameter_key.smirks
        parameter_attribute = self.parameter_key.attribute

        original_handler = original_force_field.get_parameter_handler(parameter_tag)
        original_parameter = original_handler.parameters[parameter_smirks]

        if self.use_subset_of_force_field:

            force_field = ForceField()
            handler = copy.deepcopy(original_force_field.get_parameter_handler(parameter_tag))
            force_field.register_parameter_handler(handler)

        else:

            force_field = copy.deepcopy(original_force_field)
            handler = force_field.get_parameter_handler(parameter_tag)

        parameter_index = None
        value_list = None

        if hasattr(original_parameter, parameter_attribute):
            parameter_value = getattr(original_parameter, parameter_attribute)
        else:
            attribute_split = re.split(r'(\d+)', parameter_attribute)

            assert len(parameter_attribute) == 2
            assert hasattr(original_parameter, attribute_split[0])

            parameter_attribute = attribute_split[0]
            parameter_index = int(attribute_split[1]) - 1

            value_list = getattr(original_parameter, parameter_attribute)
            parameter_value = value_list[parameter_index]

        if scale_amount is not None:

            existing_parameter = handler.parameters[parameter_smirks]

            if np.isclose(parameter_value.value_in_unit(parameter_value.unit), 0.0):
                # Careful thought needs to be given to this. Consider cases such as
                # epsilon or sigma where negative values are not allowed.
                parameter_value = (scale_amount if scale_amount > 0.0 else 0.0) * parameter_value.unit
            else:
                parameter_value *= (1.0 + scale_amount)

            if value_list is None:
                setattr(existing_parameter, parameter_attribute, parameter_value)
            else:
                value_list[parameter_index] = parameter_value
                setattr(existing_parameter, parameter_attribute, value_list)

        system = force_field.create_openmm_system(topology)

        if not self.enable_pbc:
            disable_pbc(system)

        return system, parameter_value
Example #12
0
    def serialise_system(self):
        """Create the OpenMM system; parametrise using frost; serialise the system."""

        # Create an openFF molecule from the rdkit molecule
        off_molecule = Molecule.from_rdkit(self.molecule.rdkit_mol,
                                           allow_undefined_stereo=True)

        # Make the OpenMM system
        off_topology = off_molecule.to_topology()

        # Load the smirnoff99Frosst force field.
        forcefield = ForceField('test_forcefields/smirnoff99Frosst.offxml')

        try:
            # Parametrize the topology and create an OpenMM System.
            system = forcefield.create_openmm_system(off_topology)
        except (UnassignedValenceParameterException, TypeError,
                UnassignedValenceParameterException):
            # If this does not work then we have a moleucle that is not in SMIRNOFF so we must add generics
            # and remove the charge handler to get some basic parameters for the moleucle
            new_bond = BondHandler.BondType(
                smirks='[*:1]~[*:2]',
                length="0 * angstrom",
                k="0.0 * angstrom**-2 * mole**-1 * kilocalorie")
            new_angle = AngleHandler.AngleType(
                smirks='[*:1]~[*:2]~[*:3]',
                angle="0.0 * degree",
                k="0.0 * mole**-1 * radian**-2 * kilocalorie")
            new_torsion = ProperTorsionHandler.ProperTorsionType(
                smirks='[*:1]~[*:2]~[*:3]~[*:4]',
                periodicity1="1",
                phase1="0.0 * degree",
                k1="0.0 * mole**-1 * kilocalorie",
                periodicity2="2",
                phase2="180.0 * degree",
                k2="0.0 * mole**-1 * kilocalorie",
                periodicity3="3",
                phase3="0.0 * degree",
                k3="0.0 * mole**-1 * kilocalorie",
                periodicity4="4",
                phase4="180.0 * degree",
                k4="0.0 * mole**-1 * kilocalorie",
                idivf1="1.0",
                idivf2="1.0",
                idivf3="1.0",
                idivf4="1.0")

            new_vdw = vdWHandler.vdWType(smirks='[*:1]',
                                         epsilon=0 *
                                         unit.kilocalories_per_mole,
                                         sigma=0 * unit.angstroms)
            new_generics = {
                'Bonds': new_bond,
                'Angles': new_angle,
                'ProperTorsions': new_torsion,
                'vdW': new_vdw
            }
            for key, val in new_generics.items():
                forcefield.get_parameter_handler(key).parameters.insert(0, val)

            # This has to be removed as sqm will fail with unknown elements
            del forcefield._parameter_handlers['ToolkitAM1BCC']

            # Parametrize the topology and create an OpenMM System.
            system = forcefield.create_openmm_system(off_topology)
            # This will tag the molecule so run.py knows that generics have been used.
            self.fftype = 'generics'

        # Serialise the OpenMM system into the xml file
        with open('serialised.xml', 'w+') as out:
            out.write(XmlSerializer.serializeSystem(system))
# Initialize an empty force field
from openforcefield.typing.engines.smirnoff import ForceField
ff = ForceField()
ff._set_aromaticity_model('OEAroModel_MDL')

# Loop over the parameters to convert and their corresponding SMIRNOFF header tags 
for smirnoff_tag, param_dicts in { "Bonds": bond_dicts, "Angles": angle_dicts,
                                   "ProperTorsions": proper_dicts,
                                   "AmberImproperTorsions": improper_dicts,
                                   #'LibraryCharges': charge_dicts,
                                   #'vdW': nonbond_dicts
                                  }.items():
  # Get the parameter handler for the given tag. The
  # "get_parameter_handler" method will initialize an
  # empty handler if none exists yet (as is the case here). 
  handler = ff.get_parameter_handler(smirnoff_tag)

  # Loop over the list of parameter dictionaries, using each one as an input to
  # handler.add_parameter.  This mimics deserializing an OFFXML into a ForceField
  # object, and will do any sanitization that we might otherwise miss
  from openforcefield.typing.engines.smirnoff.parameters import DuplicateParameterError
  for param_dict in param_dicts:
    try:
      handler.add_parameter(param_dict)
    except DuplicateParameterError:
      continue
      

# Add the ElectrostaticsHandler, with the proper 1-4 scaling factors
handler = ff.get_parameter_handler('Electrostatics')                
# Write the now-populated forcefield out to OFFXML
#!/usr/bin/env python
"""
Generate a PDF of all small molecules in the JSON dataset.
"""

import gzip
import json
import cmiles
from openeye import oechem
from openforcefield.typing.engines.smirnoff import ForceField

forcefield = ForceField('param_valence.offxml', allow_cosmetic_attributes=True)
ff_torsion_param_list = forcefield.get_parameter_handler(
    'ProperTorsions').parameters


def get_torsion_definition(ff_torsion_param_list, tid):
    for torsion_param in ff_torsion_param_list:
        if torsion_param.id == tid:
            answer = torsion_param
    return answer


# Highlight element of interest
#subs = oechem.OESubSearch("[#6]") # carbon
subs = None


# Read the input SMILES
def read_molecules(input_json):
    """
Example #15
0
def find_smirks_parameters(parameter_tag='vdW', *smiles_patterns):
    """Finds those force field parameters with a given tag which
    would be assigned to a specified set of molecules defined by
    the their smiles patterns.

    Parameters
    ----------
    parameter_tag: str
        The tag of the force field parameters to find.
    smiles_patterns: str
        The smiles patterns to assign the force field parameters
        to.

    Returns
    -------
    dict of str and list of str
        A dictionary with keys of parameter smirks patterns, and
        values of lists of smiles patterns which would utilize
        those parameters.
    """

    stdout_ = sys.stdout  # Keep track of the previous value.
    stderr_ = sys.stderr  # Keep track of the previous value.

    stream = StringIO()
    sys.stdout = stream
    sys.stderr = stream
    force_field = ForceField('smirnoff99Frosst-1.1.0.offxml')
    sys.stdout = stdout_  # restore the previous stdout.
    sys.stderr = stderr_

    parameter_handler = force_field.get_parameter_handler(parameter_tag)

    smiles_by_parameter_smirks = {}

    # Initialize the array with all possible smirks pattern
    # to make it easier to identify which are missing.
    for parameter in parameter_handler.parameters:

        if parameter.smirks in smiles_by_parameter_smirks:
            continue

        smiles_by_parameter_smirks[parameter.smirks] = set()

    # Populate the dictionary using the open force field toolkit.
    for smiles in smiles_patterns:

        if smiles not in cached_smirks_parameters or parameter_tag not in cached_smirks_parameters[smiles]:

            try:
                molecule = Molecule.from_smiles(smiles)
            except UndefinedStereochemistryError:
                # Skip molecules with undefined stereochemistry.
                continue

            topology = Topology.from_molecules([molecule])

            if smiles not in cached_smirks_parameters:
                cached_smirks_parameters[smiles] = {}

            if parameter_tag not in cached_smirks_parameters[smiles]:
                cached_smirks_parameters[smiles][parameter_tag] = []

            cached_smirks_parameters[smiles][parameter_tag] = [
                parameter.smirks for parameter in force_field.label_molecules(topology)[0][parameter_tag].values()
            ]

        parameters_with_tag = cached_smirks_parameters[smiles][parameter_tag]

        for smirks in parameters_with_tag:
            smiles_by_parameter_smirks[smirks].add(smiles)

    return smiles_by_parameter_smirks