[med-svn] [plip] 01/04: New upstream version 1.3.3+dfsg
Alex Mestiashvili
malex-guest at moszumanska.debian.org
Mon Nov 7 14:43:25 UTC 2016
This is an automated email from the git hooks/post-receive script.
malex-guest pushed a commit to branch master
in repository plip.
commit d6863e894b8e87e12b1ec0cff58c525903c9763e
Author: Alexandre Mestiashvili <alex at biotec.tu-dresden.de>
Date: Mon Nov 7 14:34:45 2016 +0100
New upstream version 1.3.3+dfsg
---
CHANGES.txt | 5 +
DOCUMENTATION.md | 11 ++
README.md | 3 +-
plip/modules/config.py | 3 +-
plip/modules/detection.py | 19 +-
plip/modules/plipremote.py | 1 +
plip/modules/plipxml.py | 316 ++++++++++++++++++++++++++++++
plip/modules/preparation.py | 68 ++++++-
plip/modules/pymolplip.py | 19 +-
plip/modules/report.py | 2 +-
plip/modules/supplemental.py | 2 +-
plip/modules/visualize.py | 23 ++-
plip/plipcmd | 9 +-
plip/test/test_xml_parser.py | 162 ++++++++++++++++
plip/test/xml/1vsn.report.xml | 441 ++++++++++++++++++++++++++++++++++++++++++
setup.py | 2 +-
16 files changed, 1051 insertions(+), 35 deletions(-)
diff --git a/CHANGES.txt b/CHANGES.txt
index 921ce28..121f740 100644
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -1,6 +1,11 @@
Changelog
---------
+# 1.3.3
+* __Adds XML Parser module for PLIP XML files__
+* Detection of intramolecular interactions with --intra
+* improved error correction in PDB files
+
### 1.3.2
* __Processing of protein-peptide interactions via --peptides__
* option to keep modified residues as ligands (--keepmod)
diff --git a/DOCUMENTATION.md b/DOCUMENTATION.md
index 947cc0d..ae28206 100644
--- a/DOCUMENTATION.md
+++ b/DOCUMENTATION.md
@@ -129,6 +129,17 @@ To switch into protein-peptide interaction mode, start PLIP with the option `--p
plip -i 5hi4 --peptides I -vx
```
+### Detection of intra-chain interactions (__beta__)
+Intra-protein interactions are important for the stabilization of a structure and can give valuable insights for protein engineering and drug discovery.
+PLIP now supports detection of interactions within one chain.
+To switch into intra-chain interaction mode, start PLIP with the option `--intra`, followed by the protein chain of interest, e.g.:
+
+```bash
+plip -i 5b2m --intra A -yv
+```
+Please note that detection within a chain takes much longer than detection of protein-ligand interactions,
+especially for large structures.
+
## Changing detection thresholds
The PLIP algorithm uses a rule-based detection to report non-covalent interaction between proteins and their partners.
The current settings are based on literature values and have been refined based on extensive testing with independent cases from mainly crystallography journals, covering a broad range of structure resolutions.
diff --git a/README.md b/README.md
index df1d8cf..89b0cc5 100644
--- a/README.md
+++ b/README.md
@@ -2,8 +2,9 @@
**Protein-Ligand Interaction Profiler (PLIP)**
-
Analyze non-covalent protein-ligand interactions in 3D structures
+
+ <img src="https://github.com/ssalentin/plip/blob/development/pliplogo.png" alt="PLIP Logo" height="100">
## How to use PLIP
diff --git a/plip/modules/config.py b/plip/modules/config.py
index 3f1a8ea..4ae5e4c 100644
--- a/plip/modules/config.py
+++ b/plip/modules/config.py
@@ -30,6 +30,7 @@ ALTLOC = False # Consider alternate locations
PLUGIN_MODE = False # Special mode for PLIP in Plugins (e.g. PyMOL)
NOFIX = False # Turn off fixing of errors in PDB files
PEPTIDES = [] # Definition which chains should be considered as peptide ligands
+INTRA = None
KEEPMOD = False
# Configuration file for Protein-Ligand Interaction Profiler (PLIP)
@@ -38,7 +39,7 @@ KEEPMOD = False
# Thresholds for detection (global variables)
BS_DIST = 7.5 # Determines maximum distance to include binding site residues
AROMATIC_PLANARITY = 5.0 # Determines allowed deviation from planarity in aromatic rings
-
+MIN_DIST = 0.5 # Minimum distance for all distance thresholds
# Some distance thresholds were extended (max. 1.0A) if too restrictive too account for low-quality structures
HYDROPH_DIST_MAX = 4.0 # Distance cutoff for detection of hydrophobic contacts
HBOND_DIST_MAX = 4.1 # Max. distance between hydrogen bond donor and acceptor (Hubbard & Haider, 2001) + 0.6 A
diff --git a/plip/modules/detection.py b/plip/modules/detection.py
index 2087552..6143ea8 100644
--- a/plip/modules/detection.py
+++ b/plip/modules/detection.py
@@ -37,8 +37,10 @@ def hydrophobic_interactions(atom_set_a, atom_set_b):
'distance restype resnr reschain')
pairings = []
for a, b in itertools.product(atom_set_a, atom_set_b):
+ if a.orig_idx == b.orig_idx:
+ continue
e = euclidean3d(a.atom.coords, b.atom.coords)
- if e < config.HYDROPH_DIST_MAX:
+ if config.MIN_DIST < e < config.HYDROPH_DIST_MAX:
contact = data(bsatom=a.atom, bsatom_orig_idx=a.orig_idx, ligatom=b.atom, ligatom_orig_idx=b.orig_idx,
distance=e, restype=whichrestype(a.atom), resnr=whichresnumber(a.atom),
reschain=whichchain(a.atom))
@@ -59,15 +61,20 @@ def hbonds(acceptors, donor_pairs, protisdon, typ):
if typ == 'strong': # Regular (strong) hydrogen bonds
dist_ah = euclidean3d(acc.a.coords, don.h.coords)
dist_ad = euclidean3d(acc.a.coords, don.d.coords)
- if dist_ad < config.HBOND_DIST_MAX:
+ if config.MIN_DIST < dist_ad < config.HBOND_DIST_MAX:
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:
restype = whichrestype(don.d) if protisdon else whichrestype(acc.a)
reschain = whichchain(don.d) if protisdon else whichchain(acc.a)
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
resnr = whichresnumber(don.d)if protisdon else whichresnumber(acc.a)
+ # Next line prevents H-Bonds within amino acids in intermolecular interactions
+ if config.INTRA is not None and whichresnumber(don.d) == whichresnumber(acc.a): continue
+ # Next line prevents backbone-backbone H-Bonds
+ if config.INTRA is not None and protatom.GetResidue().GetAtomProperty(protatom, 8) and ligatom.GetResidue().GetAtomProperty(ligatom, 8): continue
contact = data(a=acc.a, a_orig_idx=acc.a_orig_idx, d=don.d, d_orig_idx=don.d_orig_idx, h=don.h,
distance_ah=dist_ah, distance_ad=dist_ad, angle=v, type=typ, protisdon=protisdon,
resnr=resnr, restype=restype, reschain=reschain, sidechain=is_sidechain_hbond,
@@ -95,7 +102,7 @@ def pistacking(rings_bs, rings_lig):
resnr, restype, reschain = whichresnumber(r.atoms[0]), whichrestype(r.atoms[0]), whichchain(r.atoms[0])
# SELECTION BY DISTANCE, ANGLE AND OFFSET
- if d < config.PISTACK_DIST_MAX:
+ if config.MIN_DIST < d < config.PISTACK_DIST_MAX:
if 0 < a < config.PISTACK_ANG_DEV and offset < config.PISTACK_OFFSET_MAX:
contact = data(proteinring=r, ligandring=l, distance=d, angle=a, offset=offset,
type='P', resnr=resnr, restype=restype, reschain=reschain)
@@ -121,7 +128,7 @@ 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 d < config.PICATION_DIST_MAX and offset < config.PISTACK_OFFSET_MAX:
+ if config.MIN_DIST < d < config.PICATION_DIST_MAX and offset < config.PISTACK_OFFSET_MAX:
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
@@ -153,7 +160,7 @@ def saltbridge(poscenter, negcenter, protispos):
data = namedtuple('saltbridge', 'positive negative distance protispos resnr restype reschain')
pairings = []
for pc, nc in itertools.product(poscenter, negcenter):
- if euclidean3d(pc.center, nc.center) < config.SALTBRIDGE_DIST_MAX:
+ if config.MIN_DIST < euclidean3d(pc.center, nc.center) < config.SALTBRIDGE_DIST_MAX:
resnr = pc.resnr if protispos else nc.resnr
restype = pc.restype if protispos else nc.restype
reschain = pc.reschain if protispos else nc.reschain
@@ -170,7 +177,7 @@ def halogen(acceptor, donor):
pairings = []
for acc, don in itertools.product(acceptor, donor):
dist = euclidean3d(acc.o.coords, don.x.coords)
- if dist < config.HALOGEN_DIST_MAX:
+ if config.MIN_DIST < dist < config.HALOGEN_DIST_MAX:
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)
diff --git a/plip/modules/plipremote.py b/plip/modules/plipremote.py
index f7e5b28..e2fd18f 100644
--- a/plip/modules/plipremote.py
+++ b/plip/modules/plipremote.py
@@ -44,6 +44,7 @@ class VisualizerData:
self.corrected_pdb = pcomp.corrected_pdb
self.pdbid = mol.pymol_name
self.hetid = ligand.hetid
+ self.ligandtype = ligand.type
self.chain = ligand.chain if not ligand.chain == "0" else "" # #@todo Fix this
self.position = str(ligand.position)
self.uid = ":".join([self.hetid, self.chain, self.position])
diff --git a/plip/modules/plipxml.py b/plip/modules/plipxml.py
new file mode 100644
index 0000000..96e7f24
--- /dev/null
+++ b/plip/modules/plipxml.py
@@ -0,0 +1,316 @@
+"""
+Protein-Ligand Interaction Profiler - Analyze and visualize protein-ligand interactions in PDB files.
+plipxml.py - Read in PLIP XML files for further analysis.
+Copyright 2014-2016 Sebastian Salentin
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+"""
+
+# Python standard library
+from collections import defaultdict
+from lxml import etree
+import itertools
+from itertools import groupby
+import sys
+import urllib2
+
+#TODO True/False values for all corresponding attributes
+
+class XMLStorage:
+ """Generic class for storing XML data from PLIP XML files."""
+
+ 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 == []:
+ return None
+ else:
+ data = found[0]
+ if force_string:
+ return data
+ if data == 'True':
+ return True
+ elif data == 'False':
+ return False
+ else:
+ try:
+ return int(data)
+ except ValueError:
+ try:
+ return float(data)
+ except ValueError:
+ # It's a string
+ return data
+
+ def getcoordinates(self, tree, location):
+ """Gets coordinates from a specific element in PLIP XML"""
+ return tuple(float(x) for x in tree.xpath('.//%s/*/text()' % location))
+
+
+class Interaction(XMLStorage):
+ """Stores information on a specific interaction type"""
+
+ def __init__(self, interaction_part):
+ self.id = interaction_part.get('id')
+ self.resnr = self.getdata(interaction_part, 'resnr')
+ self.restype = self.getdata(interaction_part, 'restype')
+ self.reschain = self.getdata(interaction_part, 'reschain')
+ self.ligcoo = self.getcoordinates(interaction_part, 'ligcoo')
+ self.protcoo = self.getcoordinates(interaction_part, 'protcoo')
+
+
+
+class HydrophobicInteraction(Interaction):
+ """Stores information on a hydrophobic interaction"""
+
+ def __init__(self, hydrophobic_part):
+ Interaction.__init__(self, hydrophobic_part)
+ self.dist = self.getdata(hydrophobic_part, 'dist')
+ self.ligcarbonidx = self.getdata(hydrophobic_part, 'ligcarbonidx')
+ self.protcarbonidx = self.getdata(hydrophobic_part, 'protcarbonidx')
+
+
+class HydrogenBond(Interaction):
+ """Stores information on a hydrogen bond interaction"""
+
+ def __init__(self, hbond_part):
+ Interaction.__init__(self, hbond_part)
+ self.sidechain = self.getdata(hbond_part, 'sidechain')
+ self.dist_h_a = self.getdata(hbond_part, 'dist_h-a')
+ self.dist_d_a = self.getdata(hbond_part, 'dist_d-a')
+ self.dist = self.dist_d_a
+
+ self.don_angle = self.getdata(hbond_part, 'don_angle')
+ self.protisdon = self.getdata(hbond_part, 'protisdon')
+ self.donoridx = self.getdata(hbond_part, 'donoridx')
+ self.acceptoridx = self.getdata(hbond_part, 'acceptoridx')
+ self.donortype = self.getdata(hbond_part, 'donortype')
+ self.acceptortype = self.getdata(hbond_part, 'acceptortype')
+
+
+class WaterBridge(Interaction):
+ """Stores information on a water bridge interaction"""
+
+ def __init__(self, wbridge_part):
+ Interaction.__init__(self, wbridge_part)
+ self.dist_a_w = self.getdata(wbridge_part, 'dist_a-w')
+ self.dist_d_w = self.getdata(wbridge_part, 'dist_d-w')
+ self.don_angle = self.getdata(wbridge_part, 'don_angle')
+ self.water_angle = self.getdata(wbridge_part, 'water_angle')
+ self.protisdon = self.getdata(wbridge_part, 'protisdon')
+ self.dist = self.dist_a_w if self.protisdon else self.dist_d_w
+
+ self.donor_idx = self.getdata(wbridge_part, 'donor_idx')
+ self.acceptor_idx = self.getdata(wbridge_part, 'acceptor_idx')
+ self.donortype = self.getdata(wbridge_part, 'donortype')
+ self.acceptortype = self.getdata(wbridge_part, 'acceptortype')
+ self.water_idx = self.getdata(wbridge_part, 'water_idx')
+ self.watercoo = self.getcoordinates(wbridge_part, 'watercoo')
+
+
+class SaltBridge(Interaction):
+ """Stores information on a salt bridge interaction"""
+
+ def __init__(self, sbridge_part):
+ Interaction.__init__(self, sbridge_part)
+ self.dist = self.getdata(sbridge_part, 'dist')
+ self.protispos = self.getdata(sbridge_part, 'protispos')
+ self.lig_group = self.getdata(sbridge_part, 'lig_group')
+ self.lig_idx_list = [int(tagpart.text) for tagpart in
+ sbridge_part.xpath('lig_idx_list/idx')]
+
+
+class PiStacking(Interaction):
+ """Stores information on a pi stacking interaction"""
+
+ def __init__(self, pistack_part):
+ Interaction.__init__(self, pistack_part)
+ self.centdist = self.getdata(pistack_part, 'centdist')
+ self.dist = self.centdist
+ self.angle = self.getdata(pistack_part, 'angle')
+ self.offset = self.getdata(pistack_part, 'offset')
+ self.type = self.getdata(pistack_part, 'type')
+ self.lig_idx_list = [int(tagpart.text) for tagpart in
+ pistack_part.xpath('lig_idx_list/idx')]
+
+
+class PiCation(Interaction):
+ """Stores information on a pi cation interaction"""
+
+ def __init__(self, pication_part):
+ Interaction.__init__(self, pication_part)
+ self.dist = self.getdata(pication_part, 'dist')
+ self.offset = self.getdata(pication_part, 'offset')
+ self.protcharged = self.getdata(pication_part, 'protcharged')
+ self.lig_group = self.getdata(pication_part, 'lig_group')
+ self.lig_idx_list = [int(tag.text) for tag in pication_part.xpath('.//lig_idx_list/idx')]
+
+
+class HalogenBond(Interaction):
+ """Stores information on a halogen bond interaction"""
+
+ def __init__(self, halogen_part):
+ Interaction.__init__(self, halogen_part)
+ self.dist = self.getdata(halogen_part, 'dist')
+ self.don_angle = self.getdata(halogen_part, 'don_angle')
+ self.acc_angle = self.getdata(halogen_part, 'acc_angle')
+ self.donortype = self.getdata(halogen_part, 'donortype')
+ self.acceptortype = self.getdata(halogen_part, 'acceptortype')
+ self.don_idx = self.getdata(halogen_part, 'don_idx')
+ self.acc_idx = self.getdata(halogen_part, 'acc_idx')
+ self.sidechain = self.getdata(halogen_part, 'sidechain')
+
+
+class MetalComplex(Interaction):
+ """Stores information on a metal complexe interaction"""
+
+ def __init__(self, metalcomplex_part):
+ Interaction.__init__(self, metalcomplex_part)
+ self.metal_idx = self.getdata(metalcomplex_part, 'metal_idx')
+ self.metal_type = self.getdata(metalcomplex_part, 'metal_type')
+ self.target_idx = self.getdata(metalcomplex_part, 'target_idx')
+ self.target_type = self.getdata(metalcomplex_part, 'target_type')
+ self.coordination = self.getdata(metalcomplex_part, 'coordination')
+ self.dist = self.getdata(metalcomplex_part, 'dist')
+ self.location = self.getdata(metalcomplex_part, 'location')
+ self.rms = self.getdata(metalcomplex_part, 'rms')
+ self.geometry = self.getdata(metalcomplex_part, 'geometry')
+ self.complexnum = self.getdata(metalcomplex_part, 'complexnum')
+ self.targetcoo = self.getcoordinates(metalcomplex_part, 'targetcoo')
+ self.metalcoo = self.getcoordinates(metalcomplex_part, 'metalcoo')
+
+class BSite(XMLStorage):
+ """Stores all information about an specific binding site."""
+
+ def __init__(self, bindingsite, pdbid):
+ self.bindingsite = bindingsite
+ self.pdbid = pdbid
+ self.bsid = ":".join(bindingsite.xpath('identifiers/*/text()')[2:5])
+ self.uniqueid = ":".join([self.pdbid, self.bsid])
+ self.hetid = self.getdata(bindingsite, 'identifiers/hetid')
+ self.longname = self.getdata(bindingsite, 'identifiers/longname')
+ self.ligtype = self.getdata(bindingsite, 'identifiers/ligtype')
+ self.smiles = self.getdata(bindingsite, 'identifiers/smiles')
+
+ # Information on binding site members
+ self.members = []
+ for member in bindingsite.xpath('identifiers/members/member'):
+ self.members += member.xpath('text()')
+
+ self.composite = self.getdata(bindingsite, 'identifiers/composite')
+
+ # Ligand Properties
+ self.heavy_atoms = self.getdata(bindingsite, 'lig_properties/num_heavy_atoms')
+ self.hbd = self.getdata(bindingsite, 'lig_properties/num_hbd')
+ self.unpaired_hbd = self.getdata(bindingsite, 'lig_properties/num_unpaired_hbd')
+ self.hba = self.getdata(bindingsite, 'lig_properties/num_hba')
+ self.unpaired_hba = self.getdata(bindingsite, 'lig_properties/num_unpaired_hba')
+ self.hal = self.getdata(bindingsite, 'lig_properties/num_hal')
+ self.unpaired_hal = self.getdata(bindingsite, 'lig_properties/num_unpaired_hal')
+ self.molweight = self.getdata(bindingsite, 'lig_properties/molweight')
+ self.logp = self.getdata(bindingsite, 'lig_properties/logp')
+ self.rotatable_bonds = self.getdata(bindingsite, 'lig_properties/num_rotatable_bonds')
+ self.rings = self.getdata(bindingsite, 'lig_properties/num_aromatic_rings')
+
+
+ # Binding Site residues
+ self.bs_res = []
+ for tagpart in bindingsite.xpath('bs_residues/bs_residue'):
+ resnumber, reschain = tagpart.text[:-1], tagpart.text[-1]
+ aa, contact, min_dist = tagpart.get('aa'), tagpart.get('contact'), tagpart.get('min_dist')
+ new_bs_res = {'resnr': int(resnumber), 'reschain': reschain, 'aa': aa, 'contact': True if contact == 'True' else False, 'min_dist': float(min_dist)}
+ self.bs_res.append(new_bs_res)
+
+ # Interacting chains
+ self.interacting_chains = []
+ for chain in bindingsite.xpath('interacting_chains/interacting_chain'):
+ self.interacting_chains += chain.xpath('text()')
+
+ # Interactions
+ interactions = bindingsite.xpath('interactions')[0]
+ self.hydrophobics = [HydrophobicInteraction(x) for x in
+ interactions.xpath('hydrophobic_interactions/hydrophobic_interaction')]
+ self.hbonds = [HydrogenBond(x) for x in interactions.xpath('hydrogen_bonds/hydrogen_bond')]
+ self.wbridges = [WaterBridge(x) for x in interactions.xpath('water_bridges/water_bridge')]
+ self.sbridges = [SaltBridge(x) for x in interactions.xpath('salt_bridges/salt_bridge')]
+ self.pi_stacks = [PiStacking(x) for x in interactions.xpath('pi_stacks/pi_stack')]
+ self.pi_cations = [PiCation(x) for x in interactions.xpath('pi_cation_interactions/pi_cation_interaction')]
+ 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.get_atom_mapping()
+ self.counts = self.get_counts()
+
+ def get_atom_mapping(self):
+ """Parses the ligand atom mapping."""
+ # Atom mappings
+ smiles_to_pdb_mapping = self.bindingsite.xpath('mappings/smiles_to_pdb/text()')
+ if smiles_to_pdb_mapping == []:
+ self.mappings = {'smiles_to_pdb': None, 'pdb_to_smiles': None}
+ else:
+ smiles_to_pdb_mapping = {int(y[0]): int(y[1]) for y in [x.split(':') for x in smiles_to_pdb_mapping[0].split(',')]}
+ self.mappings = {'smiles_to_pdb': smiles_to_pdb_mapping}
+ self.mappings['pdb_to_smiles'] = {v: k for k, v in self.mappings['smiles_to_pdb'].items()}
+
+ def get_counts(self):
+ """counts the interaction types and backbone hydrogen bonding in a binding site"""
+
+ hbondsback = len([hb for hb in self.hbonds if hb.sidechain == 'F'])
+ counts = {'hydrophobics': len(self.hydrophobics), 'hbonds': len(self.hbonds),
+ 'wbridges': len(self.wbridges), 'sbridges': len(self.sbridges), 'pistacks': len(self.pi_stacks),
+ 'pications': len(self.pi_cations), 'halogens': len(self.halogens), 'metal': len(self.metal_complexes),
+ 'hbond_back': hbondsback, 'hbond_nonback': (len(self.hbonds) - hbondsback)}
+ return counts
+
+class PLIPXML(XMLStorage):
+ """Parses and stores all information from a PLIP XML file."""
+ def __init__(self, xmlfile):
+ self.load_data(xmlfile)
+
+ # Parse general information
+ self.version = self.getdata(self.doc, '/report/plipversion/')
+ self.pdbid = self.getdata(self.doc, '/report/pdbid', force_string=True)
+ 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')
+
+ # Parse binding site information
+ self.bsites = {BSite(bs, self.pdbid).bsid: BSite(bs, self.pdbid) for bs in self.doc.xpath('//bindingsite')}
+ self.num_bsites = len(self.bsites)
+
+
+ def load_data(self, xmlfile):
+ """Loads/parses an XML file and saves it as a tree if successful."""
+ try:
+ self.doc = etree.parse(xmlfile)
+ except IOError:
+ sys.__stderr__.write("XML %s file could not be read." % xmlfile)
+ sys.exit(1)
+
+class PLIPXMLREST(PLIPXML):
+ """Parses and stores all from a PLIP XML file from the PLIP REST service"""
+ def __init__(self, pdbid):
+ PLIPXML.__init__(self, pdbid)
+
+ def load_data(self, pdbid):
+ """Loads and parses an XML resource and saves it as a tree if successful"""
+ #TODO Implement error handling
+ f = urllib2.urlopen("http://projects.biotec.tu-dresden.de/plip-rest/pdb/%s?format=xml" % pdbid.lower())
+ try:
+ self.doc = etree.parse(f)
+ except IOError:
+ sys.__stderr__.write("XML file for PDB ID %s could not be read." % pdbid)
+ sys.exit(1)
diff --git a/plip/modules/preparation.py b/plip/modules/preparation.py
index a8b12be..b839caf 100644
--- a/plip/modules/preparation.py
+++ b/plip/modules/preparation.py
@@ -107,8 +107,12 @@ class PDBParser:
def fix_pdbline(self, pdbline, lastnum):
"""Fix a PDB line if information is missing."""
+ pdbqt_conversion = {
+ "HD": "H", "HS": "H", "NA": "N",
+ "NS": "N", "OA": "O", "OS": "O", "SA": "S"}
fixed = False
newnum = 0
+ forbidden_characters = "[^a-zA-Z0-9_]"
pdbline = pdbline.strip('\n')
# Some MD / Docking tools produce empty lines, leading to segfaults
if len(pdbline.strip()) == 0:
@@ -123,6 +127,18 @@ class PDBParser:
if pdbline.startswith('ATOM'):
newnum = lastnum + 1
currentnum = int(pdbline[6:11])
+ resnum = pdbline[22:27].strip()
+ resname = pdbline[17:21].strip()
+ # Invalid residue number
+ try:
+ int(resnum)
+ except ValueError:
+ pdbline = pdbline[:22] + ' 0 ' + pdbline[27:]
+ fixed = True
+ # Invalid characters in residue name
+ if re.match(forbidden_characters, resname.strip()):
+ pdbline = pdbline[:17] + 'UNK ' + pdbline[21:]
+ fixed = True
if lastnum + 1 != currentnum:
pdbline = pdbline[:6] + (5 - len(str(newnum))) * ' ' + str(newnum) + ' ' + pdbline[12:]
fixed = True
@@ -133,14 +149,19 @@ class PDBParser:
if pdbline.endswith('H'):
self.num_fixed_lines += 1
return None, lastnum
+ # Sometimes, converted PDB structures contain PDBQT atom types. Fix that.
+ for pdbqttype in pdbqt_conversion:
+ if pdbline.strip().endswith(pdbqttype):
+ pdbline = pdbline.strip()[:-2] + ' ' + pdbqt_conversion[pdbqttype] + '\n'
+ self.num_fixed_lines += 1
if pdbline.startswith('HETATM'):
newnum = lastnum + 1
currentnum = int(pdbline[6:11])
if lastnum + 1 != currentnum:
pdbline = pdbline[:6] + (5 - len(str(newnum))) * ' ' + str(newnum) + ' ' + pdbline[12:]
fixed = True
- # No chain assigned
- if pdbline[21] == ' ':
+ # No chain assigned or number assigned as chain
+ if pdbline[21] == ' ' or re.match("[0-9]", pdbline[21]):
pdbline = pdbline[:21] + 'Z' + pdbline[22:]
fixed = True
# No residue number assigned
@@ -148,8 +169,11 @@ class PDBParser:
pdbline = pdbline[:23] + '999' + pdbline[26:]
fixed = True
# Non-standard Ligand Names
- ligname = pdbline[17:20]
- if re.match("[^a-zA-Z0-9_]", ligname.strip()):
+ ligname = pdbline[17:21].strip()
+ if len(ligname) > 3:
+ pdbline = pdbline[:17] + ligname[:3] + ' ' + pdbline[21:]
+ fixed = True
+ if re.match(forbidden_characters, ligname.strip()):
pdbline = pdbline[:17] + 'LIG ' + pdbline[21:]
fixed = True
if len(ligname.strip()) == 0:
@@ -158,6 +182,11 @@ class PDBParser:
if pdbline.endswith('H'):
self.num_fixed_lines += 1
return None, lastnum
+ # Sometimes, converted PDB structures contain PDBQT atom types. Fix that.
+ for pdbqttype in pdbqt_conversion:
+ if pdbline.strip().endswith(pdbqttype):
+ pdbline = pdbline.strip()[:-2] + ' ' + pdbqt_conversion[pdbqttype] + ' '
+ self.num_fixed_lines +=1
self.num_fixed_lines += 1 if fixed else 0
return pdbline + '\n', max(newnum, lastnum)
@@ -204,7 +233,7 @@ class LigandFinder:
Returns all non-empty ligands.
"""
- if config.PEPTIDES == []:
+ if config.PEPTIDES == [] and config.INTRA is None:
# Extract small molecule ligands (default)
ligands = []
@@ -227,7 +256,11 @@ class LigandFinder:
else:
# Extract peptides from given chains
self.water = [o for o in pybel.ob.OBResidueIter(self.proteincomplex.OBMol) if o.GetResidueProperty(9)]
- peptide_ligands = [self.getpeptides(chain) for chain in config.PEPTIDES]
+ if config.PEPTIDES != []:
+ peptide_ligands = [self.getpeptides(chain) for chain in config.PEPTIDES]
+ elif config.INTRA is not None:
+ peptide_ligands = [self.getpeptides(config.INTRA), ]
+
ligands = [p for p in peptide_ligands if p is not None ]
self.covalent, self.lignames_kept, self.lignames_all = [], [], set()
@@ -243,11 +276,13 @@ class LigandFinder:
names = [x[0] for x in members]
longname = '-'.join([x[0] for x in members])
- if config.PEPTIDES == []:
+ if config.PEPTIDES != []:
+ ligtype = 'PEPTIDE'
+ elif config.INTRA is not None:
+ ligtype = 'INTRA'
+ else:
# Classify a ligand by its HETID(s)
ligtype = classify_by_name(names)
- else:
- ligtype = 'PEPTIDE'
hetatoms = set()
for obresidue in kmer:
@@ -827,7 +862,10 @@ class BindingSite(Mol):
"""Looks for positive charges in arginine, histidine or lysine, for negative in aspartic and glutamic acid."""
data = namedtuple('pcharge', 'atoms atoms_orig_idx type center restype resnr reschain')
a_set = []
- for res in pybel.ob.OBResidueIter(mol.OBMol):
+ # Iterate through all residue, exclude those in chains defined as peptides
+ for res in [r for r in pybel.ob.OBResidueIter(mol.OBMol) if not r.GetChain() in config.PEPTIDES]:
+ if config.INTRA is not None:
+ if res.GetChain() != config.INTRA: continue
a_contributing = []
a_contributing_orig_idx = []
if res.GetName() in ('ARG', 'HIS', 'LYS'): # Arginine, Histidine or Lysine have charged sidechains
@@ -1283,6 +1321,10 @@ class PDBComplex:
longname = ligand.longname if not len(ligand.longname) > 20 else ligand.longname[:20] + '...'
ligtype = 'Unspecified type' if ligand.type == 'UNSPECIFIED' else ligand.type
ligtext = "\n%s [%s] -- %s" % (longname, ligtype, site)
+ if ligtype == 'PEPTIDE':
+ ligtext = '\n Chain %s [PEPTIDE / INTER-CHAIN]' % ligand.chain
+ if ligtype == 'INTRA':
+ ligtext = "\n Chain %s [INTRA-CHAIN]" % ligand.chain
any_in_biolip = len(set([x[0] for x in ligand.members]).intersection(config.biolip_list)) != 0
write_message(ligtext)
write_message('\n' + '-' * len(ligtext) + '\n')
@@ -1297,6 +1339,12 @@ class PDBComplex:
bs_atoms = [self.atoms[idx] for idx in [i for i in self.atoms.keys()
if self.atoms[i].OBAtom.GetResidue().GetIdx() in bs_res]
if idx in self.Mapper.proteinmap and self.Mapper.mapid(idx, mtype='protein') not in self.altconf]
+ if ligand.type == 'PEPTIDE':
+ # If peptide, don't consider the peptide chain as part of the protein binding site
+ bs_atoms = [a for a in bs_atoms if a.OBAtom.GetResidue().GetChain() != lig_obj.chain]
+ if ligand.type == 'INTRA':
+ # Interactions within the chain
+ bs_atoms = [a for a in bs_atoms if a.OBAtom.GetResidue().GetChain() == lig_obj.chain]
bs_atoms_refined = []
# Create hash with BSRES -> (MINDIST_TO_LIG, AA_TYPE)
diff --git a/plip/modules/pymolplip.py b/plip/modules/pymolplip.py
index 14a9914..166064f 100644
--- a/plip/modules/pymolplip.py
+++ b/plip/modules/pymolplip.py
@@ -28,6 +28,7 @@ class PyMOLVisualizer:
self.plcomplex = plcomplex
self.protname = plcomplex.pdbid # Name of protein with binding site
self.hetid = plcomplex.hetid
+ self.ligandtype = plcomplex.ligandtype
self.ligname = "Ligand_" + self.hetid # Name of ligand
self.metal_ids = plcomplex.metal_ids
@@ -290,7 +291,7 @@ class PyMOLVisualizer:
selections = cmd.get_names("selections")
for selection in selections:
- if len(cmd.get_model(selection).atom) == 0:
+ if cmd.count_atoms(selection) == 0:
cmd.delete(selection)
cmd.deselect()
cmd.delete('tmp*')
@@ -401,6 +402,19 @@ class PyMOLVisualizer:
cmd.set('ray_shadow', 0) # Gives the molecules a flat, modern look
cmd.set('ambient_occlusion_mode', 1)
+ def adapt_for_peptides(self):
+ """Adapt visualization for peptide ligands and interchain contacts"""
+ cmd.hide('sticks', self.ligname)
+ cmd.set('cartoon_color', 'lightorange', self.ligname)
+ cmd.show('cartoon', self.ligname)
+ cmd.show('sticks', "byres *-L")
+ cmd.util.cnc(self.ligname)
+ cmd.remove('%sCartoon and chain %s' % (self.protname, self.plcomplex.chain))
+ cmd.set('cartoon_side_chain_helper', 0)
+
+
+
+
def refinements(self):
"""Refinements for the visualization"""
@@ -439,3 +453,6 @@ class PyMOLVisualizer:
cmd.hide('everything', 'resn HOH &!Water') # Hide all non-interacting water molecules
cmd.hide('sticks', '%s and !%s and !AllBSRes' % (self.protname, self.ligname)) # Hide all non-interacting residues
+
+ if self.ligandtype in ['PEPTIDE', 'INTRA']:
+ self.adapt_for_peptides()
diff --git a/plip/modules/report.py b/plip/modules/report.py
index d8cbeb3..b45b998 100644
--- a/plip/modules/report.py
+++ b/plip/modules/report.py
@@ -26,7 +26,7 @@ import sys
# External libraries
import lxml.etree as et
-__version__ = '1.3.2'
+__version__ = '1.3.3'
class StructureReport:
"""Creates reports (xml or txt) for one structure/"""
diff --git a/plip/modules/supplemental.py b/plip/modules/supplemental.py
index f49c538..be56ccf 100644
--- a/plip/modules/supplemental.py
+++ b/plip/modules/supplemental.py
@@ -483,7 +483,7 @@ def write_message(msg, indent=False, mtype='standard', caption=False):
def message(msg, indent=False, mtype='standard', caption=False):
"""Writes messages in verbose mode"""
if caption:
- msg = msg + '\n' + '-'*len(msg)
+ msg = '\n' + msg + '\n' + '-'*len(msg) + '\n'
if mtype == 'warning':
msg = colorlog('Warning: ' + msg, 'yellow')
if mtype == 'error':
diff --git a/plip/modules/visualize.py b/plip/modules/visualize.py
index 0de5ae7..60858d0 100644
--- a/plip/modules/visualize.py
+++ b/plip/modules/visualize.py
@@ -63,6 +63,8 @@ def visualize_in_pymol(plcomplex):
chain = plcomplex.chain
if config.PEPTIDES != []:
vis.ligname = 'PeptideChain%s' % plcomplex.chain
+ if config.INTRA is not None:
+ vis.ligname = 'Intra%s' % plcomplex.chain
ligname = vis.ligname
hetid = plcomplex.hetid
@@ -112,7 +114,6 @@ def visualize_in_pymol(plcomplex):
vis.make_initial_selections()
-
vis.show_hydrophobic() # Hydrophobic Contacts
vis.show_hbonds() # Hydrogen Bonds
vis.show_halogen() # Halogen Bonds
@@ -130,15 +131,17 @@ def visualize_in_pymol(plcomplex):
vis.selections_cleanup()
vis.selections_group()
vis.additional_cleanup()
- if config.PEPTIDES == []:
- if config.PYMOL:
- vis.save_session(config.OUTPATH)
- if config.PICS:
- filename = '%s_%s' % (pdbid.upper(), "_".join([hetid, plcomplex.chain, plcomplex.position]))
- vis.save_picture(config.OUTPATH, filename)
- else:
+ if config.PEPTIDES != []:
filename = "%s_PeptideChain%s" % (pdbid.upper(), plcomplex.chain)
if config.PYMOL:
vis.save_session(config.OUTPATH, override=filename)
- if config.PICS:
- vis.save_picture(config.OUTPATH, filename)
+ elif config.INTRA is not None:
+ filename = "%s_IntraChain%s" % (pdbid.upper(), plcomplex.chain)
+ if config.PYMOL:
+ vis.save_session(config.OUTPATH, override=filename)
+ else:
+ filename = '%s_%s' % (pdbid.upper(), "_".join([hetid, plcomplex.chain, plcomplex.position]))
+ if config.PYMOL:
+ vis.save_session(config.OUTPATH)
+ if config.PICS:
+ vis.save_picture(config.OUTPATH, filename)
diff --git a/plip/plipcmd b/plip/plipcmd
index e929955..3bfd0f7 100755
--- a/plip/plipcmd
+++ b/plip/plipcmd
@@ -206,9 +206,11 @@ if __name__ == '__main__':
parser.add_argument("--nofix", dest="nofix", default=False,
help="Turns off fixing of PDB files.",
action="store_true")
- parser.add_argument("--peptides", dest="peptides", default=[],
- help="Allows to define one or multiple chains as peptide ligands",
+ ligandtype = parser.add_mutually_exclusive_group() # Either peptide/inter or intra mode
+ ligandtype.add_argument("--peptides", "--inter", dest="peptides", default=[],
+ help="Allows to define one or multiple chains as peptide ligands or to detect inter-chain contacts",
nargs="+")
+ ligandtype.add_argument("--intra", dest="intra", help="Allows to define one chain to analyze intra-chain contacts.")
parser.add_argument("--keepmod", dest="keepmod", default=False,
help="Keep modified residues as ligands",
action="store_true")
@@ -242,7 +244,8 @@ if __name__ == '__main__':
config.BASEPATH = config.OUTPATH # Used for batch processing
config.BREAKCOMPOSITE = arguments.breakcomposite
config.ALTLOC = arguments.altlocation
- config.PEPTIDES =arguments.peptides
+ config.PEPTIDES = arguments.peptides
+ config.INTRA = arguments.intra
config.NOFIX = arguments.nofix
config.KEEPMOD = arguments.keepmod
# Assign values to global thresholds
diff --git a/plip/test/test_xml_parser.py b/plip/test/test_xml_parser.py
new file mode 100644
index 0000000..64bed55
--- /dev/null
+++ b/plip/test/test_xml_parser.py
@@ -0,0 +1,162 @@
+# coding=utf-8
+"""
+Protein-Ligand Interaction Profiler - Analyze and visualize protein-ligand interactions in PDB files.
+test_xml_parser.py - Unit Tests for XML Parser.
+Copyright 2014-2016 Sebastian Salentin
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+"""
+
+
+import unittest
+from plip.modules.plipxml import PLIPXML
+
+
+class XMLParserTest(unittest.TestCase):
+ """Checks if the XML parser is working correctly"""
+
+ def setUp(self):
+ self.px = PLIPXML('./xml/1vsn.report.xml')
+ self.bsite = self.px.bsites['NFT:A:283']
+ self.smiles = 'N=CCNC(=O)[C at H](CC(C)C)N[C at H](C(F)(F)F)c1ccc(cc1)c1ccc(cc1)S(=O)(=O)N'
+
+ def test_general_information(self):
+ """Test if general information is correctly parsed."""
+ self.assertEqual(self.px.version, '1.3.2')
+ self.assertEqual(self.px.pdbid, '1VSN')
+ self.assertEqual(self.px.filetype, 'PDB')
+ self.assertFalse(self.px.fixed)
+ self.assertEqual(self.px.filename, '1vsn.pdb')
+ self.assertEqual(self.px.excluded, None)
+
+ def test_bsite_information(self):
+ """Test if the binding site information is correctly parsed."""
+ self.assertEqual(self.bsite.pdbid, '1VSN')
+ self.assertEqual(self.bsite.uniqueid, '1VSN:NFT:A:283')
+ self.assertEqual(self.bsite.hetid, 'NFT')
+ self.assertEqual(self.bsite.longname, 'NFT')
+ self.assertEqual(self.bsite.ligtype, 'SMALLMOLECULE')
+ self.assertEqual(self.bsite.smiles, self.smiles)
+ self.assertEqual(self.bsite.members, ['NFT:A:283'])
+ self.assertFalse(self.bsite.composite)
+
+ # ligand properties
+ self.assertEqual(self.bsite.heavy_atoms, 33)
+ self.assertEqual(self.bsite.hbd, 5)
+ self.assertEqual(self.bsite.unpaired_hbd, 0)
+ self.assertEqual(self.bsite.hba, 7)
+ self.assertEqual(self.bsite.unpaired_hba, 2)
+ self.assertEqual(self.bsite.hal, 3)
+ self.assertEqual(self.bsite.unpaired_hal, 1)
+ self.assertEqual(self.bsite.rings, 2)
+ self.assertEqual(self.bsite.rotatable_bonds, 12)
+ self.assertEqual(self.bsite.molweight, 484.5349896)
+ self.assertEqual(self.bsite.logp, 6.0371)
+
+ # Atom mappings (non-exhaustive test)
+ lmap = self.bsite.mappings['pdb_to_smiles']
+ self.assertEqual(lmap[1625], 9)
+ self.assertEqual(lmap[1649], 1)
+ self.assertEqual(lmap[1617], 19)
+
+ # Binding site residues
+ self.assertEqual(len(self.bsite.bs_res), 36)
+
+ # Interacting chains
+ self.assertEqual(self.bsite.interacting_chains, ['A'])
+
+ # Has Interactions?
+ self.assertTrue(self.bsite.has_interactions, True)
+
+ def test_interactions(self):
+ """Test if interaction information is correctly parsed."""
+
+ # Hydrophobic Contacts
+ self.assertEqual(len(self.bsite.hydrophobics), 4)
+ hydrophobic1 = self.bsite.hydrophobics[0]
+ self.assertEqual(hydrophobic1.dist, 3.67)
+ self.assertEqual(hydrophobic1.resnr, 61)
+ self.assertEqual(hydrophobic1.restype, 'ASP')
+ self.assertEqual(hydrophobic1.reschain, 'A')
+ self.assertEqual(hydrophobic1.ligcarbonidx, 1639)
+ self.assertEqual(hydrophobic1.protcarbonidx, 448)
+ self.assertEqual(hydrophobic1.ligcoo, (-7.395, 24.225, 6.614))
+ self.assertEqual(hydrophobic1.protcoo, (-6.900, 21.561, 9.090))
+
+
+ # Hydrogen Bonds
+ self.assertEqual(len(self.bsite.hbonds), 7)
+ hbond1 = self.bsite.hbonds[0]
+ self.assertEqual(hbond1.resnr, 19)
+ self.assertEqual(hbond1.restype, 'GLN')
+ self.assertEqual(hbond1.reschain, 'A')
+ self.assertTrue(hbond1.sidechain)
+ self.assertEqual(hbond1.dist_h_a, 2.16)
+ self.assertEqual(hbond1.dist_d_a, 3.11)
+ self.assertEqual(hbond1.don_angle, 160.05)
+ self.assertTrue(hbond1.protisdon)
+ self.assertEqual(hbond1.donoridx, 153)
+ self.assertEqual(hbond1.donortype, 'Nam')
+ self.assertEqual(hbond1.acceptoridx, 1649)
+ self.assertEqual(hbond1.acceptortype, 'N2')
+ self.assertEqual(hbond1.ligcoo, (2.820, 18.145, 6.806))
+ self.assertEqual(hbond1.protcoo, (3.976, 15.409, 7.712))
+
+ # Water Bridges
+ self.assertEqual(len(self.bsite.wbridges), 2)
+ wbridge1 = self.bsite.wbridges[0]
+ self.assertEqual(wbridge1.resnr, 159)
+ self.assertEqual(wbridge1.restype, 'HIS')
+ self.assertEqual(wbridge1.reschain, 'A')
+ self.assertEqual(wbridge1.dist_a_w, 3.67)
+ self.assertEqual(wbridge1.dist_d_w, 3.13)
+ self.assertEqual(wbridge1.don_angle, 126.73)
+ self.assertEqual(wbridge1.water_angle, 116.36)
+ self.assertTrue(wbridge1.protisdon)
+ self.assertEqual(wbridge1.donor_idx, 1210)
+ self.assertEqual(wbridge1.donortype, 'Nar')
+ self.assertEqual(wbridge1.acceptor_idx, 1649)
+ self.assertEqual(wbridge1.acceptortype, 'N2')
+ self.assertEqual(wbridge1.ligcoo, (2.820, 18.145, 6.806))
+ self.assertEqual(wbridge1.protcoo, (6.401, 19.307, 4.971))
+ self.assertEqual(wbridge1.watercoo, (3.860, 18.563, 3.309))
+
+
+ # Salt Bridges
+ self.assertEqual(len(self.bsite.sbridges), 0)
+
+ # Pi stacking
+ self.assertEqual(len(self.bsite.pi_stacks), 0)
+
+ # Pi cation interactions
+ self.assertEqual(len(self.bsite.pi_cations), 0)
+
+ # Halogen Bonds
+ self.assertEqual(len(self.bsite.halogens), 2)
+ hal1 = self.bsite.halogens[0]
+ self.assertEqual(hal1.resnr, 67)
+ self.assertEqual(hal1.restype, 'TYR')
+ self.assertEqual(hal1.reschain, 'A')
+ self.assertTrue(hal1.sidechain)
+ self.assertEqual(hal1.dist, 3.37)
+ self.assertEqual(hal1.don_angle, 156.70)
+ self.assertEqual(hal1.acc_angle, 100.53)
+ self.assertEqual(hal1.don_idx, 1627)
+ self.assertEqual(hal1.donortype, 'F')
+ self.assertEqual(hal1.acc_idx, 485)
+ self.assertEqual(hal1.acceptortype, 'O3')
+ self.assertEqual(hal1.ligcoo, (-1.862, 29.303, 4.507))
+ self.assertEqual(hal1.protcoo, (-1.005, 26.276, 3.287))
+
+ # Metal complexes
+ self.assertEqual(len(self.bsite.metal_complexes), 0)
diff --git a/plip/test/xml/1vsn.report.xml b/plip/test/xml/1vsn.report.xml
new file mode 100644
index 0000000..d697c42
--- /dev/null
+++ b/plip/test/xml/1vsn.report.xml
@@ -0,0 +1,441 @@
+<?xml version='1.0' encoding='ASCII'?>
+<report>
+ <plipversion>1.3.2</plipversion>
+ <bindingsite id="1" has_interactions="True">
+ <identifiers>
+ <longname>NFT</longname>
+ <ligtype>SMALLMOLECULE</ligtype>
+ <hetid>NFT</hetid>
+ <chain>A</chain>
+ <position>283</position>
+ <composite>False</composite>
+ <members>
+ <member id="1">NFT:A:283</member>
+ </members>
+ <smiles>N=CCNC(=O)[C at H](CC(C)C)N[C at H](C(F)(F)F)c1ccc(cc1)c1ccc(cc1)S(=O)(=O)N</smiles>
+ </identifiers>
+ <lig_properties>
+ <num_heavy_atoms>33</num_heavy_atoms>
+ <num_hbd>5</num_hbd>
+ <num_unpaired_hbd>0</num_unpaired_hbd>
+ <num_hba>7</num_hba>
+ <num_unpaired_hba>2</num_unpaired_hba>
+ <num_hal>3</num_hal>
+ <num_unpaired_hal>1</num_unpaired_hal>
+ <num_aromatic_rings>2</num_aromatic_rings>
+ <num_rotatable_bonds>12</num_rotatable_bonds>
+ <molweight>484.5349896</molweight>
+ <logp>6.0371</logp>
+ </lig_properties>
+ <interacting_chains>
+ <interacting_chain id="1">A</interacting_chain>
+ </interacting_chains>
+ <bs_residues>
+ <bs_residue aa="GLY" contact="False" id="1" min_dist="3.5">64A</bs_residue>
+ <bs_residue aa="LEU" contact="True" id="2" min_dist="3.2">157A</bs_residue>
+ <bs_residue aa="SER" contact="False" id="3" min_dist="6.2">176A</bs_residue>
+ <bs_residue aa="MET" contact="False" id="4" min_dist="3.8">68A</bs_residue>
+ <bs_residue aa="SER" contact="False" id="5" min_dist="7.3">29A</bs_residue>
+ <bs_residue aa="ALA" contact="False" id="6" min_dist="4.3">160A</bs_residue>
+ <bs_residue aa="TRP" contact="False" id="7" min_dist="3.5">26A</bs_residue>
+ <bs_residue aa="SER" contact="False" id="8" min_dist="3.7">24A</bs_residue>
+ <bs_residue aa="CYS" contact="False" id="9" min_dist="4.5">22A</bs_residue>
+ <bs_residue aa="GLY" contact="False" id="10" min_dist="7.3">20A</bs_residue>
+ <bs_residue aa="CYS" contact="False" id="11" min_dist="6.4">63A</bs_residue>
+ <bs_residue aa="SER" contact="False" id="12" min_dist="4.4">58A</bs_residue>
+ <bs_residue aa="ASP" contact="True" id="13" min_dist="3.0">61A</bs_residue>
+ <bs_residue aa="ALA" contact="True" id="14" min_dist="4.0">133A</bs_residue>
+ <bs_residue aa="TYR" contact="True" id="15" min_dist="3.4">67A</bs_residue>
+ <bs_residue aa="GLY" contact="False" id="16" min_dist="3.2">65A</bs_residue>
+ <bs_residue aa="PHE" contact="False" id="17" min_dist="7.4">28A</bs_residue>
+ <bs_residue aa="LEU" contact="False" id="18" min_dist="4.2">205A</bs_residue>
+ <bs_residue aa="THR" contact="False" id="19" min_dist="7.3">69A</bs_residue>
+ <bs_residue aa="TRP" contact="True" id="20" min_dist="5.8">177A</bs_residue>
+ <bs_residue aa="HIS" contact="True" id="21" min_dist="4.0">159A</bs_residue>
+ <bs_residue aa="ASN" contact="True" id="22" min_dist="2.9">158A</bs_residue>
+ <bs_residue aa="VAL" contact="False" id="23" min_dist="6.9">161A</bs_residue>
+ <bs_residue aa="ALA" contact="False" id="24" min_dist="6.8">27A</bs_residue>
+ <bs_residue aa="ALA" contact="False" id="25" min_dist="6.8">1156A</bs_residue>
+ <bs_residue aa="CYS" contact="True" id="26" min_dist="1.7">25A</bs_residue>
+ <bs_residue aa="GLY" contact="False" id="27" min_dist="3.4">23A</bs_residue>
+ <bs_residue aa="GLU" contact="False" id="28" min_dist="3.3">59A</bs_residue>
+ <bs_residue aa="GLY" contact="False" id="29" min_dist="5.8">62A</bs_residue>
+ <bs_residue aa="VAL" contact="False" id="30" min_dist="6.0">132A</bs_residue>
+ <bs_residue aa="ASN" contact="False" id="31" min_dist="3.0">60A</bs_residue>
+ <bs_residue aa="ILE" contact="False" id="32" min_dist="5.7">134A</bs_residue>
+ <bs_residue aa="VAL" contact="False" id="33" min_dist="6.3">57A</bs_residue>
+ <bs_residue aa="GLN" contact="True" id="34" min_dist="3.1">19A</bs_residue>
+ <bs_residue aa="ASN" contact="False" id="35" min_dist="5.9">70A</bs_residue>
+ <bs_residue aa="GLY" contact="True" id="36" min_dist="2.9">66A</bs_residue>
+ </bs_residues>
+ <interactions>
+ <hydrophobic_interactions>
+ <hydrophobic_interaction id="1">
+ <resnr>61</resnr>
+ <restype>ASP</restype>
+ <reschain>A</reschain>
+ <dist>3.67</dist>
+ <ligcarbonidx>1639</ligcarbonidx>
+ <protcarbonidx>448</protcarbonidx>
+ <ligcoo>
+ <x>-7.395</x>
+ <y>24.225</y>
+ <z>6.614</z>
+ </ligcoo>
+ <protcoo>
+ <x>-6.900</x>
+ <y>21.561</y>
+ <z>9.090</z>
+ </protcoo>
+ </hydrophobic_interaction>
+ <hydrophobic_interaction id="2">
+ <resnr>67</resnr>
+ <restype>TYR</restype>
+ <reschain>A</reschain>
+ <dist>3.73</dist>
+ <ligcarbonidx>1622</ligcarbonidx>
+ <protcarbonidx>482</protcarbonidx>
+ <ligcoo>
+ <x>-2.692</x>
+ <y>25.499</y>
+ <z>5.958</z>
+ </ligcoo>
+ <protcoo>
+ <x>-2.449</x>
+ <y>29.118</y>
+ <z>6.843</z>
+ </protcoo>
+ </hydrophobic_interaction>
+ <hydrophobic_interaction id="3">
+ <resnr>67</resnr>
+ <restype>TYR</restype>
+ <reschain>A</reschain>
+ <dist>3.84</dist>
+ <ligcarbonidx>1636</ligcarbonidx>
+ <protcarbonidx>481</protcarbonidx>
+ <ligcoo>
+ <x>3.244</x>
+ <y>26.542</y>
+ <z>6.729</z>
+ </ligcoo>
+ <protcoo>
+ <x>0.294</x>
+ <y>28.886</y>
+ <z>7.491</z>
+ </protcoo>
+ </hydrophobic_interaction>
+ <hydrophobic_interaction id="4">
+ <resnr>133</resnr>
+ <restype>ALA</restype>
+ <reschain>A</reschain>
+ <dist>3.97</dist>
+ <ligcarbonidx>1636</ligcarbonidx>
+ <protcarbonidx>994</protcarbonidx>
+ <ligcoo>
+ <x>3.244</x>
+ <y>26.542</y>
+ <z>6.729</z>
+ </ligcoo>
+ <protcoo>
+ <x>6.575</x>
+ <y>27.872</y>
+ <z>5.025</z>
+ </protcoo>
+ </hydrophobic_interaction>
+ </hydrophobic_interactions>
+ <hydrogen_bonds>
+ <hydrogen_bond id="1">
+ <resnr>19</resnr>
+ <restype>GLN</restype>
+ <reschain>A</reschain>
+ <sidechain>True</sidechain>
+ <dist_h-a>2.16</dist_h-a>
+ <dist_d-a>3.11</dist_d-a>
+ <don_angle>160.05</don_angle>
+ <protisdon>True</protisdon>
+ <donoridx>153</donoridx>
+ <donortype>Nam</donortype>
+ <acceptoridx>1649</acceptoridx>
+ <acceptortype>N2</acceptortype>
+ <ligcoo>
+ <x>2.820</x>
+ <y>18.145</y>
+ <z>6.806</z>
+ </ligcoo>
+ <protcoo>
+ <x>3.976</x>
+ <y>15.409</y>
+ <z>7.712</z>
+ </protcoo>
+ </hydrogen_bond>
+ <hydrogen_bond id="2">
+ <resnr>25</resnr>
+ <restype>CYS</restype>
+ <reschain>A</reschain>
+ <sidechain>False</sidechain>
+ <dist_h-a>2.21</dist_h-a>
+ <dist_d-a>3.09</dist_d-a>
+ <don_angle>146.90</don_angle>
+ <protisdon>True</protisdon>
+ <donoridx>183</donoridx>
+ <donortype>Nam</donortype>
+ <acceptoridx>1649</acceptoridx>
+ <acceptortype>N2</acceptortype>
+ <ligcoo>
+ <x>2.820</x>
+ <y>18.145</y>
+ <z>6.806</z>
+ </ligcoo>
+ <protcoo>
+ <x>3.306</x>
+ <y>18.620</y>
+ <z>9.817</z>
+ </protcoo>
+ </hydrogen_bond>
+ <hydrogen_bond id="3">
+ <resnr>61</resnr>
+ <restype>ASP</restype>
+ <reschain>A</reschain>
+ <sidechain>True</sidechain>
+ <dist_h-a>2.26</dist_h-a>
+ <dist_d-a>2.99</dist_d-a>
+ <don_angle>134.61</don_angle>
+ <protisdon>True</protisdon>
+ <donoridx>451</donoridx>
+ <donortype>O3</donortype>
+ <acceptoridx>1645</acceptoridx>
+ <acceptortype>N3</acceptortype>
+ <ligcoo>
+ <x>-9.805</x>
+ <y>23.545</y>
+ <z>10.596</z>
+ </ligcoo>
+ <protcoo>
+ <x>-9.236</x>
+ <y>20.949</y>
+ <z>9.223</z>
+ </protcoo>
+ </hydrogen_bond>
+ <hydrogen_bond id="4">
+ <resnr>61</resnr>
+ <restype>ASP</restype>
+ <reschain>A</reschain>
+ <sidechain>True</sidechain>
+ <dist_h-a>1.98</dist_h-a>
+ <dist_d-a>2.99</dist_d-a>
+ <don_angle>170.22</don_angle>
+ <protisdon>False</protisdon>
+ <donoridx>1645</donoridx>
+ <donortype>N3</donortype>
+ <acceptoridx>451</acceptoridx>
+ <acceptortype>O3</acceptortype>
+ <ligcoo>
+ <x>-9.805</x>
+ <y>23.545</y>
+ <z>10.596</z>
+ </ligcoo>
+ <protcoo>
+ <x>-9.236</x>
+ <y>20.949</y>
+ <z>9.223</z>
+ </protcoo>
+ </hydrogen_bond>
+ <hydrogen_bond id="5">
+ <resnr>66</resnr>
+ <restype>GLY</restype>
+ <reschain>A</reschain>
+ <sidechain>False</sidechain>
+ <dist_h-a>2.33</dist_h-a>
+ <dist_d-a>3.18</dist_d-a>
+ <don_angle>140.42</don_angle>
+ <protisdon>False</protisdon>
+ <donoridx>1631</donoridx>
+ <donortype>N3</donortype>
+ <acceptoridx>473</acceptoridx>
+ <acceptortype>O2</acceptortype>
+ <ligcoo>
+ <x>0.027</x>
+ <y>24.446</y>
+ <z>5.449</z>
+ </ligcoo>
+ <protcoo>
+ <x>0.194</x>
+ <y>25.010</y>
+ <z>8.576</z>
+ </protcoo>
+ </hydrogen_bond>
+ <hydrogen_bond id="6">
+ <resnr>66</resnr>
+ <restype>GLY</restype>
+ <reschain>A</reschain>
+ <sidechain>False</sidechain>
+ <dist_h-a>2.05</dist_h-a>
+ <dist_d-a>2.95</dist_d-a>
+ <don_angle>150.59</don_angle>
+ <protisdon>True</protisdon>
+ <donoridx>470</donoridx>
+ <donortype>Nam</donortype>
+ <acceptoridx>1638</acceptoridx>
+ <acceptortype>O2</acceptortype>
+ <ligcoo>
+ <x>0.422</x>
+ <y>22.065</y>
+ <z>7.006</z>
+ </ligcoo>
+ <protcoo>
+ <x>-1.810</x>
+ <y>23.106</y>
+ <z>8.621</z>
+ </protcoo>
+ </hydrogen_bond>
+ <hydrogen_bond id="7">
+ <resnr>158</resnr>
+ <restype>ASN</restype>
+ <reschain>A</reschain>
+ <sidechain>False</sidechain>
+ <dist_h-a>1.95</dist_h-a>
+ <dist_d-a>2.92</dist_d-a>
+ <don_angle>167.45</don_angle>
+ <protisdon>False</protisdon>
+ <donoridx>1629</donoridx>
+ <donortype>Nam</donortype>
+ <acceptoridx>1199</acceptoridx>
+ <acceptortype>O2</acceptortype>
+ <ligcoo>
+ <x>1.645</x>
+ <y>21.274</y>
+ <z>5.306</z>
+ </ligcoo>
+ <protcoo>
+ <x>3.137</x>
+ <y>21.570</y>
+ <z>2.817</z>
+ </protcoo>
+ </hydrogen_bond>
+ </hydrogen_bonds>
+ <water_bridges>
+ <water_bridge id="1">
+ <resnr>159</resnr>
+ <restype>HIS</restype>
+ <reschain>A</reschain>
+ <dist_a-w>3.67</dist_a-w>
+ <dist_d-w>3.13</dist_d-w>
+ <don_angle>126.73</don_angle>
+ <water_angle>116.36</water_angle>
+ <protisdon>True</protisdon>
+ <donor_idx>1210</donor_idx>
+ <donortype>Nar</donortype>
+ <acceptor_idx>1649</acceptor_idx>
+ <acceptortype>N2</acceptortype>
+ <water_idx>1758</water_idx>
+ <ligcoo>
+ <x>2.820</x>
+ <y>18.145</y>
+ <z>6.806</z>
+ </ligcoo>
+ <protcoo>
+ <x>6.401</x>
+ <y>19.307</y>
+ <z>4.971</z>
+ </protcoo>
+ <watercoo>
+ <x>3.860</x>
+ <y>18.563</y>
+ <z>3.309</z>
+ </watercoo>
+ </water_bridge>
+ <water_bridge id="2">
+ <resnr>177</resnr>
+ <restype>TRP</restype>
+ <reschain>A</reschain>
+ <dist_a-w>3.96</dist_a-w>
+ <dist_d-w>3.04</dist_d-w>
+ <don_angle>125.14</don_angle>
+ <water_angle>84.15</water_angle>
+ <protisdon>True</protisdon>
+ <donor_idx>1380</donor_idx>
+ <donortype>Nar</donortype>
+ <acceptor_idx>1649</acceptor_idx>
+ <acceptortype>N2</acceptortype>
+ <water_idx>1735</water_idx>
+ <ligcoo>
+ <x>2.820</x>
+ <y>18.145</y>
+ <z>6.806</z>
+ </ligcoo>
+ <protcoo>
+ <x>7.577</x>
+ <y>15.116</y>
+ <z>5.463</z>
+ </protcoo>
+ <watercoo>
+ <x>4.665</x>
+ <y>15.397</y>
+ <z>4.635</z>
+ </watercoo>
+ </water_bridge>
+ </water_bridges>
+ <salt_bridges/>
+ <pi_stacks/>
+ <pi_cation_interactions/>
+ <halogen_bonds>
+ <halogen_bond id="1">
+ <resnr>67</resnr>
+ <restype>TYR</restype>
+ <reschain>A</reschain>
+ <sidechain>True</sidechain>
+ <dist>3.37</dist>
+ <don_angle>156.70</don_angle>
+ <acc_angle>100.53</acc_angle>
+ <don_idx>1627</don_idx>
+ <donortype>F</donortype>
+ <acc_idx>485</acc_idx>
+ <acceptortype>O3</acceptortype>
+ <ligcoo>
+ <x>-1.862</x>
+ <y>29.303</y>
+ <z>4.507</z>
+ </ligcoo>
+ <protcoo>
+ <x>-1.005</x>
+ <y>26.276</y>
+ <z>3.287</z>
+ </protcoo>
+ </halogen_bond>
+ <halogen_bond id="2">
+ <resnr>157</resnr>
+ <restype>LEU</restype>
+ <reschain>A</reschain>
+ <sidechain>False</sidechain>
+ <dist>3.24</dist>
+ <don_angle>141.32</don_angle>
+ <acc_angle>129.13</acc_angle>
+ <don_idx>1628</don_idx>
+ <donortype>F</donortype>
+ <acc_idx>1191</acc_idx>
+ <acceptortype>O2</acceptortype>
+ <ligcoo>
+ <x>2.348</x>
+ <y>25.905</y>
+ <z>0.550</z>
+ </ligcoo>
+ <protcoo>
+ <x>0.124</x>
+ <y>24.593</y>
+ <z>2.500</z>
+ </protcoo>
+ </halogen_bond>
+ </halogen_bonds>
+ <metal_complexes/>
+ </interactions>
+ <mappings>
+ <smiles_to_pdb>1:1649,2:1648,3:1630,4:1629,5:1637,6:1632,7:1631,8:1624,9:1625,10:1626,11:1627,12:1628,13:1623,14:1621,15:1620,16:1619,17:1618,18:1622,19:1617,20:1639,21:1640,22:1641,23:1642,24:1643,25:1644,26:1645,27:1646,28:1647,29:1633,30:1634,31:1635,32:1636,33:1638</smiles_to_pdb>
+ </mappings>
+ </bindingsite>
+ <pdbid>1VSN</pdbid>
+ <filetype>PDB</filetype>
+ <pdbfile>1vsn.pdb</pdbfile>
+ <pdbfixes>False</pdbfixes>
+ <filename>1vsn.pdb</filename>
+ <excluded_ligands/>
+</report>
diff --git a/setup.py b/setup.py
index 752d5cd..6d068b6 100644
--- a/setup.py
+++ b/setup.py
@@ -19,7 +19,7 @@ limitations under the License.
from setuptools import setup
setup(name='plip',
- version='1.3.2',
+ version='1.3.3',
description='PLIP - Fully automated protein-ligand interaction profiler',
classifiers=[
'Development Status :: 5 - Production/Stable',
--
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/debian-med/plip.git
More information about the debian-med-commit
mailing list