Пример #1
0
def run_ff_cationic(mol: Molecule, anchor: Atom, s: Settings) -> None:
    r"""Assign neutral parameters to a cationic species (*e.g.* ammonium).

    Consists of 3 distinct steps:

    * **mol** is converted into two neutral fragments,
      *e.g.* ammonium is converted into two amines:
      :math:`N^+(R)_4 \rightarrow N(R)_3 + RN(H)_2`.
    * Parameters are guessed for both fragments (using MATCH_) and then recombined into **mol**.
    * The atomic charge of **anchor** is adjusted such that
      the total moleculair charge becomes zero.

    Performs an inplace update of **mol**.

    .. _MATCH: http://brooks.chem.lsa.umich.edu/index.php?page=match&subdir=articles/resources/software

    Parameters
    ----------
    mol : :class:`Molecule<scm.plams.mol.molecule.Molecule>`
        A cationic molecule.

    anchor : :class:`Atom<scm.plams.mol.atom.Atom>`
        The atom in **mol** with the formal positive charge.

    s : :class:`Settings<scm.plams.core.settings.Settings>`
        The job Settings to-be passed to :class:`MATCHJob<nanoCAT.ff.match_job.MATCHJob>`.

    See Also
    --------
    :func:`run_match_job()<nanoCAT.ff.ff_assignment.run_match_job>`
        Assign atom types and charges to **mol** based on the results of MATCH_.

    :func:`run_ff_anionic()<nanoCAT.ff.ff_anionic.run_ff_anionic>`
        Assign neutral parameters to an anionic species (*e.g.* carboxylate).

    """  # noqa
    if anchor not in mol:
        raise MoleculeError("Passed 'anchor' is not part of 'mol'")
    anchor.properties.charge = 0

    # Find the first bond attached to the anchor atom which is not part of a ring
    for bond in anchor.bonds:
        if not mol.in_ring(bond):
            break
    else:
        raise MoleculeError(
            "All bonds attached to 'anchor' are part of a ring system")

    with SplitMol(mol, bond) as (frag1, frag2):
        # Identify the amine and the alkylic fragment
        if anchor in frag1:
            amine = frag1
            alkyl = frag2
        else:
            amine = frag2
            alkyl = frag1
        amine.delete_atom(anchor.bonds[-1].other_end(anchor))

        # Change the capping hydrogen into X
        # X is the same atom type as **anchor**
        alkyl_cap = alkyl[-1]
        alkyl_cap.atnum = anchor.atnum
        cap_bond = alkyl_cap.bonds[0]
        bond_length = alkyl_cap.radius + cap_bond.other_end(alkyl_cap).radius
        cap_bond.resize(alkyl_cap, bond_length)

        # Change X into XH_n
        alkyl_with_h = add_Hs(alkyl)
        properties = mol[1].properties
        for at in alkyl_with_h.atoms[len(alkyl):]:
            cap_h = Atom(atnum=at.atnum,
                         coords=at.coords,
                         mol=alkyl,
                         settings=properties.copy())
            cap_h.properties.pdb_info.IsHeteroAtom = False
            cap_h.properties.pdb_info.Name = 'Hxx'
            alkyl.add_atom(cap_h)
            alkyl.add_bond(Bond(alkyl_cap, cap_h, mol=alkyl))

        # Get the match parameters
        run_match_job(amine, s)
        run_match_job(alkyl, s)

    # Set the total charge of the system to 0
    anchor.properties.charge_float -= sum(at.properties.charge_float
                                          for at in mol)
    return None
Пример #2
0
def split_mol(plams_mol: Molecule, anchor: Atom) -> List[Bond]:
    """Split a molecule into multiple smaller fragments; returning the bonds that have to be broken.

    One fragment is created for every branch within **plams_mol**.

    Parameters
    ----------
    plams_mol : |plams.Molecule|
        The input molecule.

    anchor : |plams.Atom|
        An anchor atom which will be stored in the largest to-be returned fragment.

    Returns
    -------
    :class:`list` [|plams.Bond|]
        A list of plams bonds.

    """
    def _in_ring(bond: Bond) -> bool:
        """Check if **bond** is part of a ring system."""
        return plams_mol.in_ring(bond)

    def _besides_ring(atom: Atom) -> bool:
        """Check if any neighboring atoms are part of a ring system."""
        return any(plams_mol.in_ring(at) for at in atom.neighbors())

    def _is_valid_bond(bond: Bond) -> bool:
        """Check if one atom in **bond** has at least 3 neighbours and the other at least 2."""
        n1, n2 = plams_mol.neighbors(bond.atom1), plams_mol.neighbors(
            bond.atom2)
        return (len(n1) >= 3 and len(n2) >= 2) or (len(n1) >= 2
                                                   and len(n2) >= 3)

    def _get_frag_size(bond: Bond) -> int:
        """Return the size of the largest fragment were **plams_mol** to be split along **bond**."""
        if getattr(bond, '_besides_ring', False):
            del bond._besides_ring
            return np.inf
        return plams_mol.get_frag_size(bond, anchor)

    # Temporary remove hydrogen atoms
    atom_gen = (at for at in plams_mol if at.atnum == 1)
    with RemoveAtoms(plams_mol, atom_gen):
        # Remove undesired bonds
        bond_list = [bond for bond in plams_mol.bonds if not _in_ring(bond)]

        # Remove even more undesired bonds
        for bond in reversed(bond_list):
            if not _is_valid_bond(bond):
                bond_list.remove(bond)

    atom_list = list(
        itertools.chain.from_iterable(
            (bond.atom1, bond.atom2) for bond in bond_list))
    atom_set = {atom for atom in atom_list if atom_list.count(atom) >= 3}
    atom_dict = {
        atom: [bond for bond in atom.bonds if bond in bond_list]
        for atom in atom_set
    }
    for b in bond_list:
        if plams_mol.in_ring(b.atom1):
            key = b.atom1
        elif plams_mol.in_ring(b.atom2):
            key = b.atom2
        else:
            continue

        b._besides_ring = True
        if key not in atom_dict:
            atom_dict[key] = 3 * [b]
        else:
            atom_dict[key] += (3 - len(atom_dict[key])) * [b]

    # Fragment the molecule such that the anchor on the largest fragment
    ret = []
    for at, bond_list in atom_dict.items():
        # Can't directly iterate over bond_list as its size is modified
        iterator = range(len(bond_list[2:]))
        for _ in iterator:
            frag_size = [_get_frag_size(bond) for bond in bond_list]
            idx = np.argmax(frag_size)  # The index of the largest fragment
            bond = bond_list.pop(idx)
            ret.append(bond)
    return ret