[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