Example #1
0
def buildPDBEnsemble(atomics,
                     ref=None,
                     title='Unknown',
                     labels=None,
                     atommaps=None,
                     unmapped=None,
                     **kwargs):
    """Builds a :class:`.PDBEnsemble` from a given reference structure and a list of structures 
    (:class:`.Atomic` instances). Note that the reference should be included in the list as well.

    :arg atomics: a list of :class:`.Atomic` instances
    :type atomics: list

    :arg ref: reference structure or the index to the reference in *atomics*. If **None**,
        then the first item in *atomics* will be considered as the reference. If it is a 
        :class:`.PDBEnsemble` instance, then *atomics* will be appended to the existing ensemble.
        Default is **None**
    :type ref: int, :class:`.Chain`, :class:`.Selection`, or :class:`.AtomGroup`

    :arg title: the title of the ensemble
    :type title: str

    :arg labels: labels of the conformations
    :type labels: list

    :arg degeneracy: whether only the active coordinate set (**True**) or all the coordinate sets 
        (**False**) of each structure should be added to the ensemble. Default is **True**
    :type degeneracy: bool

    :arg occupancy: minimal occupancy of columns (range from 0 to 1). Columns whose occupancy
        is below this value will be trimmed
    :type occupancy: float

    :arg atommaps: labels of *atomics* that were mapped and added into the ensemble. This is an 
        output argument
    :type atommaps: list

    :arg unmapped: labels of *atomics* that cannot be included in the ensemble. This is an 
        output argument
    :type unmapped: list

    :arg subset: a subset for selecting particular atoms from the input structures.
        Default is ``"all"``
    :type subset: str

    :arg superpose: if set to ``'iter'``, :func:`.PDBEnsemble.iterpose` will be used to 
        superpose the structures, otherwise conformations will be superposed with respect 
        to the reference specified by *ref* unless set to ``False``. Default is ``'iter'``
    :type superpose: str, bool
    """

    occupancy = kwargs.pop('occupancy', None)
    degeneracy = kwargs.pop('degeneracy', True)
    subset = str(kwargs.get('subset', 'all')).lower()
    superpose = kwargs.pop('superpose', 'iter')
    superpose = kwargs.pop('iterpose', superpose)
    debug = kwargs.pop('debug', {})

    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'
        )
    start = time.time()

    if not isListLike(atomics):
        raise TypeError('atomics should be list-like')

    if len(atomics) == 1 and degeneracy is True:
        raise ValueError('atomics should have at least two items')

    if labels is not None:
        if len(labels) != len(atomics):
            raise TypeError('Labels and atomics must have the same lengths.')
    else:
        labels = []

        for atoms in atomics:
            if atoms is None:
                labels.append(None)
            else:
                labels.append(atoms.getTitle())

    if ref is None:
        target = atomics[0]
    elif isinstance(ref, Integral):
        target = atomics[ref]
    elif isinstance(ref, PDBEnsemble):
        target = ref._atoms
    else:
        target = ref

    # initialize a PDBEnsemble with reference atoms and coordinates
    isrefset = False
    if isinstance(ref, PDBEnsemble):
        ensemble = ref
    else:
        # select the subset of reference beforehand for the sake of efficiency
        if subset != 'all':
            target = target.select(subset)
        ensemble = PDBEnsemble(title)
        if isinstance(target, Atomic):
            ensemble.setAtoms(target)
            ensemble.setCoords(target.getCoords())
            isrefset = True
        else:
            ensemble._n_atoms = len(target)
            isrefset = False

    # build the ensemble
    if unmapped is None: unmapped = []
    if atommaps is None: atommaps = []

    LOGGER.progress('Building the ensemble...', len(atomics),
                    '_prody_buildPDBEnsemble')
    for i, atoms in enumerate(atomics):
        if atoms is None:
            unmapped.append(labels[i])
            continue

        LOGGER.update(i,
                      'Mapping %s to the reference...' % atoms.getTitle(),
                      label='_prody_buildPDBEnsemble')
        try:
            atoms.getHierView()
        except AttributeError:
            raise TypeError(
                'atomics must be a list of instances having the access to getHierView'
            )

        if subset != 'all':
            atoms = atoms.select(subset)

        # find the mapping of chains of atoms to those of target
        debug[labels[i]] = {}
        atommaps_ = alignChains(atoms,
                                target,
                                debug=debug[labels[i]],
                                **kwargs)

        if len(atommaps_) == 0:
            unmapped.append(labels[i])
            continue
        else:
            atommaps.extend(atommaps_)

        # add the atommaps to the ensemble
        for atommap in atommaps_:
            lbl = pystr(labels[i])
            if len(atommaps_) > 1:
                chids = np.unique(atommap.getChids())
                strchids = ''.join(chids)
                lbl += '_%s' % strchids
            ensemble.addCoordset(atommap,
                                 weights=atommap.getFlags('mapped'),
                                 label=lbl,
                                 degeneracy=degeneracy)

            if not isrefset:
                ensemble.setCoords(atommap.getCoords())
                isrefset = True

    LOGGER.finish()

    if occupancy is not None:
        ensemble = trimPDBEnsemble(ensemble, occupancy=occupancy)

    if superpose == 'iter':
        ensemble.iterpose()
    elif superpose is not False:
        ensemble.superpose()

    LOGGER.info('Ensemble ({0} conformations) were built in {1:.2f}s.'.format(
        ensemble.numConfs(),
        time.time() - start))

    if unmapped:
        LOGGER.warn('{0} structures cannot be mapped.'.format(len(unmapped)))
    return ensemble
Example #2
0
def combineEnsembles(target, mobile, **kwargs):
    """Combines two ensembles by mapping the **atoms** of **mobile** 
    to that of **target**."""

    iterpose = kwargs.pop('superpose', True)
    iterpose = kwargs.pop('iterpose', iterpose)

    title = kwargs.pop('title', None)

    def findIndices(A, B):
        """Finds indices of values of A in B."""
        B = np.asarray(B)
        ret = np.zeros_like(A)
        for i, a in enumerate(A):
            indices = np.where(B == a)[0]
            if len(indices):
                index = indices[0]
            else:
                index = -1
            ret[i] = index
        return ret

    ens0, ens1 = target, mobile
    atoms0 = ens0.getAtoms()
    atoms1 = ens1.getAtoms()
    if atoms0 is None:
        raise ValueError('target must have associated atoms')
    if atoms1 is None:
        raise ValueError('mobile must have associated atoms')

    w0 = ens0.getWeights()
    w1 = ens1.getWeights()

    coords0 = ens0.getCoordsets()
    coords1 = ens1.getCoordsets()

    if isinstance(ens0, PDBEnsemble):
        labels0 = ens0.getLabels()
    else:
        labels0 = None

    if isinstance(ens1, PDBEnsemble):
        labels1 = ens1.getLabels()
    else:
        labels1 = None

    # obtain atommaps: atoms1 -> atoms0
    atommaps = alignChains(atoms1, atoms0, **kwargs)

    if len(atommaps) == 0:
        raise ValueError('mobile cannot be mapped onto target. '
                         'Try again with relaxed seqid and/or coverage')

    # combine the atommaps
    atommap = atommaps[0]
    weights = atommap.getFlags('mapped')

    # extract mappings from atommap
    if hasattr(atoms1, 'getIndices'):
        all_indices = atoms1.getIndices()
    else:
        all_indices = np.arange(atoms1.numAtoms())
    I = findIndices(atommap._indices, all_indices)
    J = atommap.getMapping()

    # map the coordinates: ens1 -> ens0
    n_csets = coords1.shape[0]
    n_atoms = atoms0.numAtoms()

    coords2 = np.zeros((n_csets, n_atoms, 3))
    coords2[:, J, :] = coords1[:, I, :]

    if w1 is not None:
        w2 = np.zeros((n_csets, n_atoms, 1))
        w2[:, J, :] = w1[:, I, :]
    else:
        w2 = None

    if w2 is None:
        w2 = weights
    else:
        w2 *= weights

    # build the new ensemble
    if title is None:
        title = '%s + %s' % (target.getTitle(), mobile.getTitle())

    ens = PDBEnsemble(title)

    ens.setAtoms(atoms0)
    ens.setCoords(atoms0.getCoords())
    ens.addCoordset(coords0, weights=w0, label=labels0)
    ens.addCoordset(coords2, weights=w2, label=labels1)

    if iterpose:
        ens.iterpose()
    return ens
Example #3
0
def realignModes(modes, atoms, ref):
    """Align *modes* in the original frame based on *atoms*
    onto another frame based on *ref* using the transformation 
    from alignment of *atoms* to *ref*
    
    :arg modes: multiple 3D modes
    :type modes: :class:`.ModeSet`, :class:`.ANM`, :class:`.PCA`

    :arg atoms: central structure related to *modes* to map onto *ref*
        Inserting *atoms* into an ensemble and projecting onto *modes*
        should give all zeros
    :type atoms: :class:`.Atomic`
    
    :arg ref: reference structure for mapping
    :type ref: :class:`.Atomic`
    """
    if not isinstance(modes, (ModeSet, NMA)):
        raise TypeError('modes should be a ModeSet of NMA instance')

    if not modes.is3d():
        raise ValueError('modes should be 3D for this function to work')

    if not isinstance(atoms, Atomic):
        raise TypeError('atoms should be an Atomic instance')

    if not isinstance(ref, Atomic):
        raise TypeError('ref should be an Atomic instance')

    n_atoms = modes.numAtoms()

    if atoms.numAtoms() != n_atoms:
        raise ValueError(
            'atoms and modes should have the same number of atoms')

    def_coords = np.array(
        [atoms.getCoords() + mode.getArrayNx3() for mode in modes])

    def_ens = PDBEnsemble('applied eigvecs')
    def_ens.setCoords(atoms)
    def_ens.setAtoms(atoms)
    def_ens.addCoordset(atoms)
    def_ens.addCoordset(def_coords)

    if not np.allclose(calcProjection(def_ens[0], modes),
                       np.zeros(modes.numModes())):
        raise ValueError('projection of atoms onto modes (via an ensemble) '
                         'is not all zeros so atoms is not appropriate')

    if ref.numAtoms() != n_atoms:
        ref = alignChains(ref, atoms)[0]

    def_ens.setCoords(ref)
    def_ens.superpose()

    new_vectors = np.array([
        np.array(coords - def_ens.getCoordsets()[0]).flatten()
        for coords in def_ens.getCoordsets()[1:]
    ]).T

    # initialise a new modes object with the same type
    result = type(modes)()

    result.setEigens(new_vectors, modes.getEigvals())
    return result