Commit 175430d3 authored by Alexandre Mestiashvili's avatar Alexandre Mestiashvili
Browse files

New upstream version 1.4.3~b+dfsg

parent a390324d
Loading
Loading
Loading
Loading
+3 −0
Original line number Diff line number Diff line
Changelog
---------
# Next version
* Adds information on covalent linkages to XML files
* __Anaconda package__ (provided by `Ikagami`)

# 1.4.2
* Adds "--name" option to change name of output files
+2 −2
Original line number Diff line number Diff line
@@ -44,7 +44,7 @@ and should be executed with Python 2.7.x.

Example command for Ubuntu using apt-get
```bash
sudo apt-get install pymol openbabel python-openbabel imagemagick
sudo apt-get install pymol openbabel python-openbabel imagemagick swig
```


@@ -67,7 +67,7 @@ You can use any other user directory, but we will use the one given above for th
The package manager `pip` will create symlinks for you so you can call PLIP using `plip` in the command line. If you cloned from Github, use the following command to do the same:

```bash
alias plip='~/pliptool/plip/plipcmd'
alias plip='python ~/pliptool/plip/plipcmd.py'
```

## Analyze a PDB structure with PLIP
+11 −3
Original line number Diff line number Diff line
@@ -18,11 +18,11 @@ git clone https://github.com/ssalentin/plip.git ~/pliptool

##### As a command line tool

Run the `plipcmd` script inside the PLIP folder to detect, report, and visualize interactions. The following example creates a PYMOL visualization for the interactions
Run the `plipcmd.py` script inside the PLIP folder to detect, report, and visualize interactions. The following example creates a PYMOL visualization for the interactions
between the inhibitor NFT and its target protein in the PDB structure 1VSN.

```bash
alias plip='~/pliptool/plip/plipcmd'
alias plip='python ~/pliptool/plip/plipcmd.py'
mkdir /tmp/1vsn && cd /tmp/1vsn
plip -i 1vsn -yv
pymol 1VSN_NFT_A_283.pse
@@ -61,7 +61,7 @@ For a full documentation of running options and output formats, please refear to

## Versions and Branches
For production environments, you should use the latest versioned commit from the stable branch.
Newer commits from the stable and development branch may contains new but untested and not documented features.
Newer commits from the stable and development branch may contain new but untested and not documented features.

## Requirements
Previous to using PLIP, make sure you have the following tools and libraries installed:
@@ -93,3 +93,11 @@ If you are unsure about usage options, don't hesitate to contact me.
If you are using PLIP in your work, please cite
> Salentin,S. et al. PLIP: fully automated protein-ligand interaction profiler.
> Nucl. Acids Res. (1 July 2015) 43 (W1): W443-W447. doi: 10.1093/nar/gkv315

## FAQ
> I try to run PLIP, but I'm getting an error message saying:
> ValueError: [...] is not a recognised Open Babel descriptor type

Make sure Open Babel is correctly installed. This error can occur if the installed Python bindings don't match the OpenBabel version on your machine.
We don't offer technical support for installation of third-party packages.
For an instruction how to install Open Babel, please refer to their [website](https://openbabel.org/docs/dev/Installation/install.html).
+180 −162
Original line number Diff line number Diff line
@@ -17,7 +17,8 @@ def filter_contacts(pairings):
    """Filter interactions by two criteria:
    1. No interactions between the same residue (important for intra mode).
    2. No duplicate interactions (A with B and B with A, also important for intra mode)."""
    if config.INTRA:
    if not config.INTRA:
        return pairings
    filtered1_pairings = [p for p in pairings if (p.resnr, p.reschain) != (p.resnr_l, p.reschain_l)]
    already_considered = []
    filtered2_pairings = []
@@ -30,13 +31,11 @@ def filter_contacts(pairings):
            except AttributeError:
                dist = 'D{}'.format(round(contact.distance_aw,2))
        res1, res2 = ''.join([str(contact.resnr), contact.reschain]), ''.join([str(contact.resnr_l), contact.reschain_l])
            data = set([res1, res2, dist])
        data = {res1, res2, dist}
        if data not in already_considered:
            filtered2_pairings.append(contact)
            already_considered.append(data)
    return filtered2_pairings
    else:
        return pairings


##################################################
@@ -54,7 +53,8 @@ def hydrophobic_interactions(atom_set_a, atom_set_b):
        if a.orig_idx == b.orig_idx:
            continue
        e = euclidean3d(a.atom.coords, b.atom.coords)
        if config.MIN_DIST < e < config.HYDROPH_DIST_MAX:
        if not config.MIN_DIST < e < config.HYDROPH_DIST_MAX:
            continue
        restype, resnr, reschain = whichrestype(a.atom), whichresnumber(a.atom), whichchain(a.atom)
        restype_l, resnr_l, reschain_l = whichrestype(b.orig_atom), whichresnumber(b.orig_atom), whichchain(b.orig_atom)
        contact = data(bsatom=a.atom, bsatom_orig_idx=a.orig_idx, ligatom=b.atom, ligatom_orig_idx=b.orig_idx,
@@ -75,13 +75,17 @@ def hbonds(acceptors, donor_pairs, protisdon, typ):
                               'restype reschain resnr_l restype_l reschain_l sidechain atype dtype')
    pairings = []
    for acc, don in itertools.product(acceptors, donor_pairs):
        if typ == 'strong':  # Regular (strong) hydrogen bonds
        if not typ == 'strong':
            continue
        # Regular (strong) hydrogen bonds
        dist_ah = euclidean3d(acc.a.coords, don.h.coords)
        dist_ad = euclidean3d(acc.a.coords, don.d.coords)
            if config.MIN_DIST < dist_ad < config.HBOND_DIST_MAX:
        if not config.MIN_DIST < dist_ad < config.HBOND_DIST_MAX:
            continue
        vec1, vec2 = vector(don.h.coords, don.d.coords), vector(don.h.coords, acc.a.coords)
        v = vecangle(vec1, vec2)
                if v > config.HBOND_DON_ANGLE_MIN:
        if not v > config.HBOND_DON_ANGLE_MIN:
            continue
        protatom = don.d.OBAtom if protisdon else acc.a.OBAtom
        ligatom = don.d.OBAtom if not protisdon else acc.a.OBAtom
        is_sidechain_hbond = protatom.GetResidue().GetAtomProperty(protatom, 8)  # Check if sidechain atom
@@ -125,7 +129,8 @@ def pistacking(rings_bs, rings_lig):

        # SELECTION BY DISTANCE, ANGLE AND OFFSET
        passed = False
        if config.MIN_DIST < d < config.PISTACK_DIST_MAX:
        if not config.MIN_DIST < d < config.PISTACK_DIST_MAX:
            continue
        if 0 < a < config.PISTACK_ANG_DEV and offset < config.PISTACK_OFFSET_MAX:
            ptype = 'P'
            passed = True
@@ -146,7 +151,8 @@ def pication(rings, pos_charged, protcharged):
    """
    data = namedtuple('pication', 'ring charge distance offset type restype resnr reschain restype_l resnr_l reschain_l protcharged')
    pairings = []
    if not len(rings) == 0 and not len(pos_charged) == 0:
    if len(rings) == 0 or len(pos_charged) == 0:
        return pairings
    for ring in rings:
        c = ring.center
        for p in pos_charged:
@@ -154,7 +160,8 @@ def pication(rings, pos_charged, protcharged):
            # Project the center of charge into the ring and measure distance to ring center
            proj = projection(ring.normal, ring.center, p.center)
            offset = euclidean3d(proj, ring.center)
                if config.MIN_DIST < d < config.PICATION_DIST_MAX and offset < config.PISTACK_OFFSET_MAX:
            if not config.MIN_DIST < d < config.PICATION_DIST_MAX or not offset < config.PISTACK_OFFSET_MAX:
                continue
            if type(p).__name__ == 'lcharge' and p.fgroup == 'tertamine':
                # Special case here if the ligand has a tertiary amine, check an additional angle
                # Otherwise, we might have have a pi-cation interaction 'through' the ligand
@@ -194,7 +201,8 @@ def saltbridge(poscenter, negcenter, protispos):
    data = namedtuple('saltbridge', 'positive negative distance protispos resnr restype reschain resnr_l restype_l reschain_l')
    pairings = []
    for pc, nc in itertools.product(poscenter, negcenter):
        if config.MIN_DIST < euclidean3d(pc.center, nc.center) < config.SALTBRIDGE_DIST_MAX:
        if not config.MIN_DIST < euclidean3d(pc.center, nc.center) < config.SALTBRIDGE_DIST_MAX:
            continue
        resnr = pc.resnr if protispos else nc.resnr
        resnr_l = whichresnumber(nc.orig_atoms[0]) if protispos else whichresnumber(pc.orig_atoms[0])
        restype = pc.restype if protispos else nc.restype
@@ -215,15 +223,18 @@ def halogen(acceptor, donor):
    pairings = []
    for acc, don in itertools.product(acceptor, donor):
        dist = euclidean3d(acc.o.coords, don.x.coords)
        if config.MIN_DIST < dist < config.HALOGEN_DIST_MAX:
        if not config.MIN_DIST < dist < config.HALOGEN_DIST_MAX:
            continue
        vec1, vec2 = vector(acc.o.coords, acc.y.coords), vector(acc.o.coords, don.x.coords)
        vec3, vec4 = vector(don.x.coords, acc.o.coords), vector(don.x.coords, don.c.coords)
        acc_angle, don_angle = vecangle(vec1, vec2), vecangle(vec3, vec4)
        is_sidechain_hal = acc.o.OBAtom.GetResidue().GetAtomProperty(acc.o.OBAtom, 8)  # Check if sidechain atom
            if config.HALOGEN_ACC_ANGLE - config.HALOGEN_ANGLE_DEV < acc_angle \
        if not config.HALOGEN_ACC_ANGLE - config.HALOGEN_ANGLE_DEV < acc_angle \
                < config.HALOGEN_ACC_ANGLE + config.HALOGEN_ANGLE_DEV:
                if config.HALOGEN_DON_ANGLE - config.HALOGEN_ANGLE_DEV < don_angle \
            continue
        if not config.HALOGEN_DON_ANGLE - config.HALOGEN_ANGLE_DEV < don_angle \
                < config.HALOGEN_DON_ANGLE + config.HALOGEN_ANGLE_DEV:
            continue
        restype, reschain, resnr = whichrestype(acc.o), whichchain(acc.o), whichresnumber(acc.o)
        restype_l, reschain_l, resnr_l = whichrestype(don.orig_x), whichchain(don.orig_x), whichresnumber(don.orig_x)
        contact = data(acc=acc, acc_orig_idx=acc.o_orig_idx, don=don, don_orig_idx=don.x_orig_idx,
@@ -269,9 +280,12 @@ def water_bridges(bs_hba, lig_hba, bs_hbd, lig_hbd, water):
    for l, p in itertools.product(lig_aw, prot_hw):
        acc, wl, distance_aw = l
        don, wd, distance_dw, d_angle = p
        if wl.oxy == wd.oxy:  # Same water molecule and angle within omega
        if not wl.oxy == wd.oxy:
            continue
        # Same water molecule and angle within omega
        w_angle = vecangle(vector(acc.a.coords, wl.oxy.coords), vector(wl.oxy.coords, don.h.coords))
            if config.WATER_BRIDGE_OMEGA_MIN < w_angle < config.WATER_BRIDGE_OMEGA_MAX:
        if not config.WATER_BRIDGE_OMEGA_MIN < w_angle < config.WATER_BRIDGE_OMEGA_MAX:
            continue
        resnr, reschain, restype = whichresnumber(don.d), whichchain(don.d), whichrestype(don.d)
        resnr_l, reschain_l, restype_l = whichresnumber(acc.a_orig_atom), whichchain(acc.a_orig_atom), whichrestype(acc.a_orig_atom)
        contact = data(a=acc.a, a_orig_idx=acc.a_orig_idx, atype=acc.a.type, d=don.d, d_orig_idx=don.d_orig_idx,
@@ -283,9 +297,12 @@ def water_bridges(bs_hba, lig_hba, bs_hbd, lig_hbd, water):
    for p, l in itertools.product(prot_aw, lig_dw):
        acc, wl, distance_aw = p
        don, wd, distance_dw, d_angle = l
        if wl.oxy == wd.oxy:  # Same water molecule and angle within omega
        if not wl.oxy == wd.oxy:
            continue
        # Same water molecule and angle within omega
        w_angle = vecangle(vector(acc.a.coords, wl.oxy.coords), vector(wl.oxy.coords, don.h.coords))
            if config.WATER_BRIDGE_OMEGA_MIN < w_angle < config.WATER_BRIDGE_OMEGA_MAX:
        if not config.WATER_BRIDGE_OMEGA_MIN < w_angle < config.WATER_BRIDGE_OMEGA_MAX:
            continue
        resnr, reschain, restype = whichresnumber(acc.a), whichchain(acc.a), whichrestype(acc.a)
        resnr_l, reschain_l, restype_l = whichresnumber(don.d_orig_atom), whichchain(don.d_orig_atom), whichrestype(don.d_orig_atom)
        contact = data(a=acc.a, a_orig_idx=acc.a_orig_idx, atype=acc.a.type, d=don.d, d_orig_idx=don.d_orig_idx,
@@ -310,7 +327,8 @@ def metal_complexation(metals, metal_binding_lig, metal_binding_bs):
    metal_to_orig_atom = {}
    for metal, target in itertools.product(metals, metal_binding_lig + metal_binding_bs):
        distance = euclidean3d(metal.m.coords, target.atom.coords)
        if distance < config.METAL_DIST_MAX:
        if not distance < config.METAL_DIST_MAX:
            continue
        if metal.m not in pairings_dict:
            pairings_dict[metal.m] = [(target, distance), ]
            metal_to_id[metal.m] = metal.m_orig_idx
+3 −3
Original line number Diff line number Diff line
@@ -22,7 +22,7 @@ class XMLStorage:
    def getdata(self, tree, location, force_string=False):
        """Gets XML data from a specific element and handles types."""
        found = tree.xpath('%s/text()' % location)
        if found == []:
        if not found:
            return None
        else:
            data = found[0]
@@ -244,7 +244,7 @@ class BSite(XMLStorage):
        self.halogens = [HalogenBond(x) for x in interactions.xpath('halogen_bonds/halogen_bond')]
        self.metal_complexes = [MetalComplex(x) for x in interactions.xpath('metal_complexes/metal_complex')]
        self.num_contacts = len(self.hydrophobics) + len(self.hbonds) + len(self.wbridges) + len(self.sbridges) + len(self.pi_stacks) + len(self.pi_cations) + len(self.halogens) + len(self.metal_complexes)
        self.has_interactions = True if self.num_contacts > 0 else False
        self.has_interactions = self.num_contacts > 0

        self.get_atom_mapping()
        self.counts = self.get_counts()
@@ -282,7 +282,7 @@ class PLIPXML(XMLStorage):
        self.filetype = self.getdata(self.doc, '/report/filetype')
        self.fixed = self.getdata(self.doc, '/report/pdbfixes/')
        self.filename = self.getdata(self.doc, '/report/filename')
        self.excluded = self.getdata(self.doc, '/report/excluded_ligands')
        self.excluded = self.doc.xpath('/report/excluded_ligands/excluded_ligand/text()')

        # Parse binding site information
        self.bsites = {BSite(bs, self.pdbid).bsid: BSite(bs, self.pdbid) for bs in self.doc.xpath('//bindingsite')}
Loading