diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index dd33301d1..5da7b11be 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,4 +1,4 @@ -This is a first draft, which is based on the ProDy website page http://prody.csb.pitt.edu/manual/devel/develop.html. Please see that page for updates. +This is a first draft, which is based on the ProDy website page http://www.bahargroup.org/prody/manual/devel/develop.html. Please see that page for updates. Install Git and a GUI diff --git a/PKG-INFO b/PKG-INFO index 047dc0d48..bf2690a89 100644 --- a/PKG-INFO +++ b/PKG-INFO @@ -32,23 +32,23 @@ Description: .. image:: https://secure.travis-ci.org/prody/ProDy.png?branch=mast You can run ProDy on all major platforms. For download and installation instructions see: - * http://prody.csb.pitt.edu/downloads + * http://www.bahargroup.org/prody/downloads DOCUMENTATION ------------- - * Homepage: http://prody.csb.pitt.edu/ + * Homepage: http://www.bahargroup.org/prody/ - * Tutorials: http://prody.csb.pitt.edu/tutorials + * Tutorials: http://www.bahargroup.org/prody/tutorials - * Reference: http://prody.csb.pitt.edu/manual + * Reference: http://www.bahargroup.org/prody/manual - * Applications: http://prody.csb.pitt.edu/manual/apps + * Applications: http://www.bahargroup.org/prody/manual/apps - * NMWiz GUI: http://prody.csb.pitt.edu/nmwiz + * NMWiz GUI: http://www.bahargroup.org/prody/nmwiz - * Changes: http://prody.csb.pitt.edu/manual/release + * Changes: http://www.bahargroup.org/prody/manual/release SOURCE CODE diff --git a/docs/apps/prody/index.rst b/docs/apps/prody/index.rst index 477f661b1..32958c34b 100644 --- a/docs/apps/prody/index.rst +++ b/docs/apps/prody/index.rst @@ -40,4 +40,4 @@ the output files are prefixed with :file:`p38_anm`:: The output file :file:`p38_anm.nmd` can be visualized using `NMWiz`_. -.. _NMWiz: http://prody.csb.pitt.edu/nmwiz/ +.. _NMWiz: http://www.bahargroup.org/prody/nmwiz/ diff --git a/docs/conf.py b/docs/conf.py index b5880a85c..4588b0003 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -158,13 +158,13 @@ 'numpy': ('http://docs.scipy.org/doc/numpy/', None), 'scipy': ('http://docs.scipy.org/doc/scipy/reference/', None), 'matplotlib': ('http://matplotlib.sourceforge.net/', None), - 'prodywebsite': ('http://prody.csb.pitt.edu/', None), + 'prodywebsite': ('http://www.bahargroup.org/prody/', None), } rst_epilog = u""" -.. _ProDy: http://prody.csb.pitt.edu -.. _Tutorials: http://prody.csb.pitt.edu/tutorials +.. _ProDy: http://www.bahargroup.org/prody +.. _Tutorials: http://www.bahargroup.org/prody/tutorials .. _NMWiz: http://csb.pitt.edu/NMWiz .. _VMD: http://www.ks.uiuc.edu/Research/vmd .. _PDB: http://www.pdb.org diff --git a/docs/index.rst b/docs/index.rst index 4aad2ed6d..aa56b17e4 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -11,7 +11,7 @@ ProDy Manual .. only:: rtd and html This is a partial copy of ProDy documentation. Please visit - `ProDy Homepage `_ for complete + `ProDy Homepage `_ for complete documentation with tutorials. diff --git a/prody/compounds/pdbligands.py b/prody/compounds/pdbligands.py index 8d2e795c1..31e2f2d90 100644 --- a/prody/compounds/pdbligands.py +++ b/prody/compounds/pdbligands.py @@ -8,6 +8,7 @@ from prody import LOGGER, SETTINGS, getPackagePath, PY3K from prody.atomic import AtomGroup, ATOMIC_FIELDS from prody.utilities import openFile, makePath, openURL +from .ccd import parseCCD __all__ = ['PDBLigandRecord', 'fetchPDBLigand', 'parsePDBLigand'] @@ -18,13 +19,20 @@ def __init__(self, data): self._rawdata = data def getCanonicalSMILES(self): - return self._rawdata['CACTVS_SMILES_CANONICAL'] + canonical = None + for row in self._rawdata[2].data: + if row['_pdbx_chem_comp_descriptor.type'] == 'SMILES_CANONICAL': + canonical = row['_pdbx_chem_comp_descriptor.descriptor'] + return canonical def fetchPDBLigand(cci, filename=None): - """Fetch PDB ligand data from PDB_ for chemical component *cci*. + """Handle PDB ligand data from PDB_ for chemical component *cci*. *cci* may be 3-letter chemical component identifier or a valid XML - filename. If *filename* is given, XML file will be saved with that name. + filename. If *filename* is given, XML file will be saved with that name. + + This function may not work as _PDB is not hosting ligand XML files anymore. + It is kept for use with existing ligand XML files. If you query ligand data frequently, you may configure ProDy to save XML files in your computer. Set ``ligand_xml_save`` option **True**, i.e. @@ -50,6 +58,7 @@ def fetchPDBLigand(cci, filename=None): ideal (energy minimized) coordinate sets: .. ipython:: python + :okexcept: from prody import * ligand_data = fetchPDBLigand('STI') @@ -233,8 +242,8 @@ def fetchPDBLigand(cci, filename=None): return dict_ -def parsePDBLigand(cci, filename=None): +def parsePDBLigand(cci): """See :func:`.fetchPDBLigand`""" - lig_dict = fetchPDBLigand(cci, filename) + lig_dict = parseCCD(cci) return PDBLigandRecord(lig_dict) diff --git a/prody/ensemble/functions.py b/prody/ensemble/functions.py index c09cd844e..be7bd1688 100644 --- a/prody/ensemble/functions.py +++ b/prody/ensemble/functions.py @@ -411,7 +411,7 @@ def buildPDBEnsemble(atomics, ref=None, title='Unknown', labels=None, atommaps=N if 'mapping_func' in kwargs: raise DeprecationWarning('mapping_func is deprecated. Please see release notes for ' - 'more details: http://prody.csb.pitt.edu/manual/release/v1.11_series.html') + 'more details: http://www.bahargroup.org/prody/manual/release/v1.11_series.html') start = time.time() if not isListLike(atomics): diff --git a/prody/proteins/pdbfile.py b/prody/proteins/pdbfile.py index 127aae19a..a8127e72f 100644 --- a/prody/proteins/pdbfile.py +++ b/prody/proteins/pdbfile.py @@ -490,11 +490,10 @@ def _parsePDBLines(atomgroup, lines, split, model, chain, subset, elements = np.zeros(asize, dtype=ATOMIC_FIELDS['element'].dtype) bfactors = np.zeros(asize, dtype=ATOMIC_FIELDS['beta'].dtype) occupancies = np.zeros(asize, dtype=ATOMIC_FIELDS['occupancy'].dtype) - anisou = None siguij = None else: radii = np.zeros(asize, dtype=ATOMIC_FIELDS['radius'].dtype) - + anisou = None asize = 2000 # increase array length by this much when needed start = split @@ -537,19 +536,7 @@ def _parsePDBLines(atomgroup, lines, split, model, chain, subset, dec = True while i < stop: line = lines[i] - if not isPDB: - fields = line.split() - if len(fields) == 10: - fields.insert(4, '') - elif len(fields) != 11: - LOGGER.warn('wrong number of fields for PQR format at line %d'%i) - i += 1 - continue - - if isPDB: - startswith = line[0:6].strip() - else: - startswith = fields[0] + startswith = line[0:6].strip() if startswith == 'ATOM' or startswith == 'HETATM': if isPDB: @@ -560,6 +547,17 @@ def _parsePDBLines(atomgroup, lines, split, model, chain, subset, else: resname = line[17:20].strip() else: + fields = line.split() + if fields[5].find('.') != -1: + # coords too early as no chid + fields.insert(4, '') + if len(fields) != 11: + try: + fields = fields[:6] + [line[30:38].strip(), line[38:46].strip(), line[46:54].strip()] + line[54:].split() + except: + LOGGER.warn('wrong number of fields for PQR format at line %d'%i) + i += 1 + continue atomname= fields[2] resname = fields[3] @@ -1689,7 +1687,10 @@ def writePQRStream(stream, atoms, **kwargs): write = stream.write calphas = atoms.ca - ssa = calphas.getSecstrs() + if calphas is not None: + ssa = calphas.getSecstrs() + else: + ssa = None helix = [] sheet = [] if ssa is not None: diff --git a/prody/proteins/waterbridges.py b/prody/proteins/waterbridges.py index 500d85ce9..a247e07b5 100644 --- a/prody/proteins/waterbridges.py +++ b/prody/proteins/waterbridges.py @@ -638,9 +638,9 @@ def analyseFrame(i, interactions_all): if max_proc == 1: interactions_all = [] - for j0, frame0 in enumerate(traj, start=start_frame): + for i in range(len(atoms.getCoordsets()[start_frame:stop_frame])): interactions_all.append([]) - analyseFrame(j0, start_frame, frame0, interactions_all) + analyseFrame(i, interactions_all) else: with mp.Manager() as manager: interactions_all = manager.list() diff --git a/prody/tests/datafiles/__init__.py b/prody/tests/datafiles/__init__.py index 1f621f95c..54f7c00de 100644 --- a/prody/tests/datafiles/__init__.py +++ b/prody/tests/datafiles/__init__.py @@ -381,6 +381,26 @@ 'file': 'pdb7pbl.pdb', 'atoms': 31396, 'nucleoside': 252 + }, + 'pqrUnknown': { + 'file': 'pqr_snippet1.pqr', + 'atoms': 5, + 'models': 1 + }, + 'pqrTranscomp': { + 'file': 'pqr_snippet2_transcomp.pqr', + 'atoms': 5, + 'models': 1 + }, + 'pqrFpocket': { + 'file': 'pqr_snippet3_fpocket.pqr', + 'atoms': 5, + 'models': 1 + }, + 'pqrPymol': { + 'file': 'pqr_snippet4_pymol.pqr', + 'atoms': 5, + 'models': 1 } } diff --git a/prody/tests/datafiles/pqr_snippet1.pqr b/prody/tests/datafiles/pqr_snippet1.pqr new file mode 100644 index 000000000..15058186a --- /dev/null +++ b/prody/tests/datafiles/pqr_snippet1.pqr @@ -0,0 +1,5 @@ +ATOM 1 C STP 1 -26.417 62.269-123.283 0.00 3.30 +ATOM 2 O STP 1 -30.495 65.669-122.669 0.00 3.08 +ATOM 3 O STP 1 -25.792 61.516-124.085 0.00 3.32 +ATOM 4 O STP 1 -25.262 61.506-124.230 0.00 3.05 +ATOM 5 O STP 1 -30.521 65.070-122.439 0.00 3.05 \ No newline at end of file diff --git a/prody/tests/datafiles/pqr_snippet2_transcomp.pqr b/prody/tests/datafiles/pqr_snippet2_transcomp.pqr new file mode 100644 index 000000000..843ab19d7 --- /dev/null +++ b/prody/tests/datafiles/pqr_snippet2_transcomp.pqr @@ -0,0 +1,5 @@ +ATOM 1 N VAL 1 16.783 48.812 26.447 0.0577 1.550 +ATOM 2 H1 VAL 1 15.848 48.422 26.463 0.2272 1.200 +ATOM 3 H2 VAL 1 16.734 49.803 26.251 0.2272 1.200 +ATOM 4 H3 VAL 1 17.195 48.663 27.359 0.2272 1.200 +ATOM 5 CA VAL 1 17.591 48.101 25.416 -0.0054 1.700 \ No newline at end of file diff --git a/prody/tests/datafiles/pqr_snippet3_fpocket.pqr b/prody/tests/datafiles/pqr_snippet3_fpocket.pqr new file mode 100644 index 000000000..15058186a --- /dev/null +++ b/prody/tests/datafiles/pqr_snippet3_fpocket.pqr @@ -0,0 +1,5 @@ +ATOM 1 C STP 1 -26.417 62.269-123.283 0.00 3.30 +ATOM 2 O STP 1 -30.495 65.669-122.669 0.00 3.08 +ATOM 3 O STP 1 -25.792 61.516-124.085 0.00 3.32 +ATOM 4 O STP 1 -25.262 61.506-124.230 0.00 3.05 +ATOM 5 O STP 1 -30.521 65.070-122.439 0.00 3.05 \ No newline at end of file diff --git a/prody/tests/datafiles/pqr_snippet4_pymol.pqr b/prody/tests/datafiles/pqr_snippet4_pymol.pqr new file mode 100644 index 000000000..7c1719e6f --- /dev/null +++ b/prody/tests/datafiles/pqr_snippet4_pymol.pqr @@ -0,0 +1,5 @@ +ATOM 29 P G 11 -17.189 -6.642 -23.827 0.00000000 0.000 +ATOM 30 C5' G 11 -15.744 -5.986 -25.911 0.00000000 0.000 +ATOM 31 O5' G 11 -16.783 -5.642 -25.005 0.00000000 0.000 +ATOM 32 C4' G 11 -15.722 -5.012 -27.074 0.00000000 0.000 +ATOM 33 O4' G 11 -16.947 -5.133 -27.838 0.00000000 0.000 \ No newline at end of file diff --git a/prody/tests/proteins/test_pdbfile.py b/prody/tests/proteins/test_pdbfile.py index c802dbd66..9d89e2b44 100644 --- a/prody/tests/proteins/test_pdbfile.py +++ b/prody/tests/proteins/test_pdbfile.py @@ -19,7 +19,7 @@ class TestParsePDB(unittest.TestCase): def setUp(self): - """Set PDB file data and parse the PDB file.""" + """Set PDB file data.""" self.pdb = DATA_FILES['multi_model_truncated'] self.one = DATA_FILES['oneatom'] @@ -108,14 +108,22 @@ def testSubsetArgument(self): 'failed to parse correct number of "bb" atoms') def testAgArgument(self): - """Test outcome of valid and invalid *ag* arguments.""" + """Test outcome of 2 invalid and 2 valid *ag* arguments.""" path = pathDatafile(self.pdb['file']) self.assertRaises(TypeError, parsePDB, path, ag='AtomGroup') + ag = prody.AtomGroup('One atom') ag.setCoords(np.array([[0., 0., 0.]])) self.assertRaises(ValueError, parsePDB, path, ag=ag) + + ag = prody.AtomGroup('Test') + self.assertEqual(parsePDB(path, ag=ag).numAtoms(), + self.pdb['atoms'], + 'parsePDB failed to parse correct number of atoms') + ag = prody.AtomGroup('Test') + ag.setCoords(np.array([[0., 0., 0.]]*self.pdb['atoms'])) self.assertEqual(parsePDB(path, ag=ag).numAtoms(), self.pdb['atoms'], 'parsePDB failed to parse correct number of atoms') diff --git a/prody/tests/proteins/test_pqrfile.py b/prody/tests/proteins/test_pqrfile.py new file mode 100644 index 000000000..2e69dfd75 --- /dev/null +++ b/prody/tests/proteins/test_pqrfile.py @@ -0,0 +1,146 @@ +"""This module contains unit tests for :mod:`~prody.proteins`.""" + +import os + +import numpy as np +from numpy.testing import * + +from prody.utilities import importDec +dec = importDec() + +from prody import * +from prody import LOGGER +from prody.utilities import which +from prody.tests import TEMPDIR, unittest +from prody.tests.datafiles import * + +LOGGER.verbosity = 'none' + +class TestParsePRQ(unittest.TestCase): + + def setUp(self): + """Set PQR file data.""" + + self.pqr1 = DATA_FILES['pqrUnknown'] + self.pqr2 = DATA_FILES['pqrTranscomp'] + self.pqr3 = DATA_FILES['pqrFpocket'] + self.pqr4 = DATA_FILES['pqrPymol'] + + def testExamplePQR(self): + """Test the outcome of a simple parsing scenario with example 1 (unnamed).""" + + path = pathDatafile(self.pqr1['file']) + ag = parsePQR(path) + + self.assertIsInstance(ag, prody.AtomGroup, + 'parsePQR failed to return an AtomGroup instance') + + self.assertEqual(ag.numAtoms(), self.pqr1['atoms'], + 'parsePQR failed to parse correct number of atoms') + + self.assertEqual(ag.numCoordsets(), self.pqr1['models'], + 'parsePQR failed to parse correct number of coordinate sets ' + '(models)') + + self.assertEqual(ag.getTitle(), + os.path.splitext(self.pqr1['file'])[0], + 'failed to set AtomGroup title based on filename') + + def testTranscompPQR(self): + """Test the outcome of a simple parsing scenario with transcomp example.""" + + path = pathDatafile(self.pqr2['file']) + ag = parsePQR(path) + + self.assertIsInstance(ag, prody.AtomGroup, + 'parsePQR failed to return an AtomGroup instance') + + self.assertEqual(ag.numAtoms(), self.pqr2['atoms'], + 'parsePQR failed to parse correct number of atoms') + + self.assertEqual(ag.numCoordsets(), self.pqr2['models'], + 'parsePQR failed to parse correct number of coordinate sets ' + '(models)') + + self.assertEqual(ag.getTitle(), + os.path.splitext(self.pqr2['file'])[0], + 'failed to set AtomGroup title based on filename') + + def testFpocketPQR(self): + """Test the outcome of a simple parsing scenario with example 1 (unnamed).""" + + path = pathDatafile(self.pqr3['file']) + ag = parsePQR(path) + + self.assertIsInstance(ag, prody.AtomGroup, + 'parsePQR failed to return an AtomGroup instance') + + self.assertEqual(ag.numAtoms(), self.pqr3['atoms'], + 'parsePQR failed to parse correct number of atoms') + + self.assertEqual(ag.numCoordsets(), self.pqr3['models'], + 'parsePQR failed to parse correct number of coordinate sets ' + '(models)') + + self.assertEqual(ag.getTitle(), + os.path.splitext(self.pqr3['file'])[0], + 'failed to set AtomGroup title based on filename') + + def testPymolPQR(self): + """Test the outcome of a simple parsing scenario with example 1 (unnamed).""" + + path = pathDatafile(self.pqr4['file']) + ag = parsePQR(path) + + self.assertIsInstance(ag, prody.AtomGroup, + 'parsePQR failed to return an AtomGroup instance') + + self.assertEqual(ag.numAtoms(), self.pqr4['atoms'], + 'parsePQR failed to parse correct number of atoms') + + self.assertEqual(ag.numCoordsets(), self.pqr4['models'], + 'parsePQR failed to parse correct number of coordinate sets ' + '(models)') + + self.assertEqual(ag.getTitle(), + os.path.splitext(self.pqr4['file'])[0], + 'failed to set AtomGroup title based on filename') + + def testTitleArgument(self): + """Test outcome of *title* argument.""" + + path = pathDatafile(self.pqr1['file']) + title = 'small protein' + self.assertEqual(parsePQR(path, title=title).getTitle(), + title, 'parsePQR failed to set user given title') + + def testSubsetArgument(self): + """Test outcome of valid and invalid *subset* arguments.""" + + path = pathDatafile(self.pqr2['file']) + self.assertRaises(TypeError, parsePQR, path, subset=['A']) + self.assertEqual(parsePQR(path, subset='ca').numAtoms(), 1, + 'failed to parse correct number of "ca" atoms') + self.assertEqual(parsePQR(path, subset='bb').numAtoms(), 2, + 'failed to parse correct number of "bb" atoms') + + def testAgArgument(self): + """Test outcome of 2 invalid and 2 valid *ag* arguments.""" + + path = pathDatafile(self.pqr1['file']) + self.assertRaises(TypeError, parsePQR, path, ag='AtomGroup') + + ag = prody.AtomGroup('One atom') + ag.setCoords(np.array([[0., 0., 0.]])) + self.assertRaises(ValueError, parsePQR, path, ag=ag) + + ag = prody.AtomGroup('Test') + self.assertEqual(parsePQR(path, ag=ag).numAtoms(), + self.pqr1['atoms'], + 'parsePQR failed to parse correct number of atoms') + + ag = prody.AtomGroup('Test') + ag.setCoords(np.array([[0., 0., 0.]]*5)) + self.assertEqual(parsePQR(path, ag=ag).numAtoms(), + self.pqr1['atoms'], + 'parsePQR failed to parse correct number of atoms') \ No newline at end of file