def __init__(self, filename, **kwargs): self.filename = util.filename(filename, ext=native_str("stream")) self._version = kwargs.get("charmm_version", 41) width = 4 if self._version < 36 else 8 if self._version >= 36: self.fmt = (""" IC EDIT DIST %-{width}s %{width}d %-{width}s %-{width}s %{width}d %-{width}s%{width}.1f END """.format(width=width)) else: self.fmt = (""" IC EDIT DIST BYNUM %{width}d BYNUM %{width}d %{width}.1f END """.format(width=width)) date = time.strftime("%a, %d %b %Y %H:%M:%S", time.localtime()) user = environ["USER"] self._title = kwargs.get( "title", ( "* Created by fluctmatch on {date}".format(date=date), "* User: {user}".format(user=user), )) if not util.iterable(self._title): self._title = util.asiterable(self._title)
def __init__(self, filename, **kwargs): self.filename = util.filename(filename, ext="prm") self._version = kwargs.get("charmm_version", 41) self._nonbonded = kwargs.get("nonbonded", False) date = time.strftime("%a, %d %b %Y %H:%M:%S", time.localtime()) user = environ["USER"] self._title = kwargs.get("title", ( "* Created by fluctmatch on {date}".format(date=date), "* User: {user}".format(user=user), )) if not util.iterable(self._title): self._title = util.asiterable(self._title)
def _prepare(self): self._n_atoms = self.mobile_atoms.n_atoms if not iterable(self.weights) and self.weights == 'mass': self.weights = self.ref_atoms.masses if self.weights is not None: self.weights = np.asarray(self.weights, dtype=np.float64) / np.mean(self.weights) current_frame = self.reference.universe.trajectory.ts.frame try: # Move to the ref_frame # (coordinates MUST be stored in case the ref traj is advanced # elsewhere or if ref == mobile universe) self.reference.universe.trajectory[self.ref_frame] self._ref_com = self.ref_atoms.center(self.weights) # makes a copy self._ref_coordinates = self.ref_atoms.positions - self._ref_com if self._groupselections_atoms: self._groupselections_ref_coords64 = [ (self.reference.select_atoms( *s['reference']).positions.astype(np.float64)) for s in self.groupselections ] finally: # Move back to the original frame self.reference.universe.trajectory[current_frame] self._ref_coordinates64 = self._ref_coordinates.astype(np.float64) if self._groupselections_atoms: # Only carry out a rotation if we want to calculate secondary # RMSDs. # R: rotation matrix that aligns r-r_com, x~-x~com # (x~: selected coordinates, x: all coordinates) # Final transformed traj coordinates: x' = (x-x~_com)*R + ref_com self._rot = np.zeros(9, dtype=np.float64) # allocate space self._R = self._rot.reshape(3, 3) else: self._rot = None self.rmsd = np.zeros( (self.n_frames, 3 + len(self._groupselections_atoms))) self._pm.format = ("RMSD {rmsd:5.2f} A at frame " "{step:5d}/{numsteps} [{percentage:5.1f}%]") self._mobile_coordinates64 = self.mobile_atoms.positions.copy().astype( np.float64)
def __init__(self, filename, **kwargs): self.filename = util.filename(filename, ext="ic") self._intcor = None self._extended = kwargs.get("extended", True) self._resid = kwargs.get("resid", True) self.key = "EXTENDED" if self._extended else "STANDARD" self.key += "_RESID" if self._resid else "" date = time.strftime("%a, %d %b %Y %H:%M:%S", time.localtime()) user = environ["USER"] self._title = kwargs.get( "title", ( "* Created by fluctmatch on {date}".format(date=date), "* User: {user}".format(user=user), )) if not util.iterable(self._title): self._title = util.asiterable(self._title)
def _prepare(self): self._n_atoms = self.mobile_atoms.n_atoms if not iterable(self.weights) and self.weights == 'mass': self.weights = self.ref_atoms.masses if self.weights is not None: self.weights = np.asarray(self.weights, dtype=np.float64) / np.mean(self.weights) current_frame = self.reference.universe.trajectory.ts.frame try: # Move to the ref_frame # (coordinates MUST be stored in case the ref traj is advanced # elsewhere or if ref == mobile universe) self.reference.universe.trajectory[self.ref_frame] self._ref_com = self.ref_atoms.center(self.weights) # makes a copy self._ref_coordinates = self.ref_atoms.positions - self._ref_com if self._groupselections_atoms: self._groupselections_ref_coords64 = [(self.reference. select_atoms(*s['reference']). positions.astype(np.float64)) for s in self.groupselections] finally: # Move back to the original frame self.reference.universe.trajectory[current_frame] self._ref_coordinates64 = self._ref_coordinates.astype(np.float64) if self._groupselections_atoms: # Only carry out a rotation if we want to calculate secondary # RMSDs. # R: rotation matrix that aligns r-r_com, x~-x~com # (x~: selected coordinates, x: all coordinates) # Final transformed traj coordinates: x' = (x-x~_com)*R + ref_com self._rot = np.zeros(9, dtype=np.float64) # allocate space self._R = self._rot.reshape(3, 3) else: self._rot = None self.rmsd = np.zeros((self.n_frames, 3 + len(self._groupselections_atoms))) self._pm.format = ("RMSD {rmsd:5.2f} A at frame " "{step:5d}/{numsteps} [{percentage:5.1f}%]\r") self._mobile_coordinates64 = self.mobile_atoms.positions.copy().astype(np.float64)
def test_iterable(iterable, value): assert util.iterable(iterable) == value
def _prepare(self): self._n_atoms = self.mobile_atoms.n_atoms if not self.weights_groupselections: if not iterable( self.weights): # apply 'mass' or 'None' to groupselections self.weights_groupselections = [self.weights] * len( self.groupselections) else: self.weights_groupselections = [None] * len( self.groupselections) for igroup, (weights, atoms) in enumerate( zip(self.weights_groupselections, self._groupselections_atoms)): if str(weights) == 'mass': self.weights_groupselections[igroup] = atoms['mobile'].masses if weights is not None: self.weights_groupselections[igroup] = np.asarray(self.weights_groupselections[igroup], dtype=np.float64) / \ np.mean(self.weights_groupselections[igroup]) # add the array of weights to weights_select self.weights_select = get_weights(self.mobile_atoms, self.weights) self.weights_ref = get_weights(self.ref_atoms, self.weights) if self.weights_select is not None: self.weights_select = np.asarray(self.weights_select, dtype=np.float64) / \ np.mean(self.weights_select) self.weights_ref = np.asarray(self.weights_ref, dtype=np.float64) / \ np.mean(self.weights_ref) current_frame = self.reference.universe.trajectory.ts.frame try: # Move to the ref_frame # (coordinates MUST be stored in case the ref traj is advanced # elsewhere or if ref == mobile universe) self.reference.universe.trajectory[self.ref_frame] self._ref_com = self.ref_atoms.center(self.weights_ref) # makes a copy self._ref_coordinates = self.ref_atoms.positions - self._ref_com if self._groupselections_atoms: self._groupselections_ref_coords64 = [ (self.reference.select_atoms( *s['reference']).positions.astype(np.float64)) for s in self.groupselections ] finally: # Move back to the original frame self.reference.universe.trajectory[current_frame] self._ref_coordinates64 = self._ref_coordinates.astype(np.float64) if self._groupselections_atoms: # Only carry out a rotation if we want to calculate secondary # RMSDs. # R: rotation matrix that aligns r-r_com, x~-x~com # (x~: selected coordinates, x: all coordinates) # Final transformed traj coordinates: x' = (x-x~_com)*R + ref_com self._rot = np.zeros(9, dtype=np.float64) # allocate space self._R = self._rot.reshape(3, 3) else: self._rot = None self.rmsd = np.zeros( (self.n_frames, 3 + len(self._groupselections_atoms))) self._mobile_coordinates64 = self.mobile_atoms.positions.copy().astype( np.float64)
def test_lists(self): assert_equal(util.iterable([1, 2, 3]), True) assert_equal(util.iterable([]), True)
def test_strings(self): """Test that iterable() works on any string (Fixed bug)""" assert_equal(util.iterable("byte string"), False) assert_equal(util.iterable(u"unicode string"), False)
def test_scalars(self): assert_equal(util.iterable(123), False)
def test_arrays(self): assert_equal(util.iterable(numpy.array([1, 2, 3])), True)
def test_iterator(self): assert_equal(util.iterable(range(3)), True)
def __init__(self, atomgroup, reference=None, select='all', groupselections=None, filename="rmsd.dat", weights=None, tol_mass=0.1, ref_frame=0, **kwargs): # DEPRECATION: remove filename kwarg in 1.0 r"""Parameters ---------- atomgroup : AtomGroup or Universe Group of atoms for which the RMSD is calculated. If a trajectory is associated with the atoms then the computation iterates over the trajectory. reference : AtomGroup or Universe (optional) Group of reference atoms; if ``None`` then the current frame of `atomgroup` is used. select : str or dict or tuple (optional) The selection to operate on; can be one of: 1. any valid selection string for :meth:`~MDAnalysis.core.groups.AtomGroup.select_atoms` that produces identical selections in `atomgroup` and `reference`; or 2. a dictionary ``{'mobile': sel1, 'reference': sel2}`` where *sel1* and *sel2* are valid selection strings that are applied to `atomgroup` and `reference` respectively (the :func:`MDAnalysis.analysis.align.fasta2select` function returns such a dictionary based on a ClustalW_ or STAMP_ sequence alignment); or 3. a tuple ``(sel1, sel2)`` When using 2. or 3. with *sel1* and *sel2* then these selection strings are applied to `atomgroup` and `reference` respectively and should generate *groups of equivalent atoms*. *sel1* and *sel2* can each also be a *list of selection strings* to generate a :class:`~MDAnalysis.core.groups.AtomGroup` with defined atom order as described under :ref:`ordered-selections-label`). groupselections : list (optional) A list of selections as described for `select`, with the difference that these selections are *always applied to the full universes*, i.e., ``atomgroup.universe.select_atoms(sel1)`` and ``reference.universe.select_atoms(sel2)``. Each selection describes additional RMSDs to be computed *after the structures have been superimposed* according to `select`. No additional fitting is performed.The output contains one additional column for each selection. .. Note:: Experimental feature. Only limited error checking implemented. filename : str (optional) write RMSD into file with :meth:`RMSD.save` .. deprecated:; 0.19.0 `filename` will be removed together with :meth:`save` in 1.0. weights : {"mass", ``None``} or array_like (optional) choose weights. With ``"mass"`` uses masses as weights; with ``None`` weigh each atom equally. If a float array of the same length as `atomgroup` is provided, use each element of the `array_like` as a weight for the corresponding atom in `atomgroup`. tol_mass : float (optional) Reject match if the atomic masses for matched atoms differ by more than `tol_mass`. ref_frame : int (optional) frame index to select frame from `reference` verbose : bool (optional) Show detailed progress of the calculation if set to ``True``; the default is ``False``. Raises ------ SelectionError If the selections from `atomgroup` and `reference` do not match. TypeError If `weights` is not of the appropriate type; see also :func:`MDAnalysis.lib.util.get_weights` ValueError If `weights` are not compatible with `atomgroup` (not the same length) or if it is not a 1D array (see :func:`MDAnalysis.lib.util.get_weights`). A :exc:`ValueError` is also raised if `weights` are not compatible with `groupselections`: only equal weights (``weights=None``) or mass-weighted (``weights="mass"``) are supported for additional `groupselections`. Notes ----- The root mean square deviation :math:`\rho(t)` of a group of :math:`N` atoms relative to a reference structure as a function of time is calculated as .. math:: \rho(t) = \sqrt{\frac{1}{N} \sum_{i=1}^N w_i \left(\mathbf{x}_i(t) - \mathbf{x}_i^{\text{ref}}\right)^2} The weights :math:`w_i` are calculated from the input weights `weights` :math:`w'_i` as relative to the mean of the input weights: .. math:: w_i = \frac{w'_i}{\langle w' \rangle} The selected coordinates from `atomgroup` are optimally superimposed (translation and rotation) on the `reference` coordinates at each time step as to minimize the RMSD. Douglas Theobald's fast QCP algorithm [Theobald2005]_ is used for the rotational superposition and to calculate the RMSD (see :mod:`MDAnalysis.lib.qcprot` for implementation details). The class runs various checks on the input to ensure that the two atom groups can be compared. This includes a comparison of atom masses (i.e., only the positions of atoms of the same mass will be considered to be correct for comparison). If masses should not be checked, just set `tol_mass` to a large value such as 1000. .. _ClustalW: http://www.clustal.org/ .. _STAMP: http://www.compbio.dundee.ac.uk/manuals/stamp.4.2/ See Also -------- rmsd .. versionadded:: 0.7.7 .. versionchanged:: 0.8 `groupselections` added .. versionchanged:: 0.16.0 Flexible weighting scheme with new `weights` keyword. .. deprecated:: 0.16.0 Instead of ``mass_weighted=True`` (removal in 0.17.0) use new ``weights='mass'``; refactored to fit with AnalysisBase API .. versionchanged:: 0.17.0 removed deprecated `mass_weighted` keyword; `groupselections` are *not* rotationally superimposed any more. .. deprecated:: 0.19.0 `filename` will be removed in 1.0 """ super(RMSD, self).__init__(atomgroup.universe.trajectory, **kwargs) self.atomgroup = atomgroup self.reference = reference if reference is not None else self.atomgroup select = process_selection(select) self.groupselections = ([process_selection(s) for s in groupselections] if groupselections is not None else []) self.weights = weights self.tol_mass = tol_mass self.ref_frame = ref_frame self.filename = filename # DEPRECATED in 0.19.0, remove in 1.0.0 self.ref_atoms = self.reference.select_atoms(*select['reference']) self.mobile_atoms = self.atomgroup.select_atoms(*select['mobile']) if len(self.ref_atoms) != len(self.mobile_atoms): err = ("Reference and trajectory atom selections do " "not contain the same number of atoms: " "N_ref={0:d}, N_traj={1:d}".format(self.ref_atoms.n_atoms, self.mobile_atoms.n_atoms)) logger.exception(err) raise SelectionError(err) logger.info("RMS calculation " "for {0:d} atoms.".format(len(self.ref_atoms))) mass_mismatches = (np.absolute((self.ref_atoms.masses - self.mobile_atoms.masses)) > self.tol_mass) if np.any(mass_mismatches): # diagnostic output: logger.error("Atoms: reference | mobile") for ar, at in zip(self.ref_atoms, self.mobile_atoms): if ar.name != at.name: logger.error("{0!s:>4} {1:3d} {2!s:>3} {3!s:>3} {4:6.3f}" "| {5!s:>4} {6:3d} {7!s:>3} {8!s:>3}" "{9:6.3f}".format(ar.segid, ar.resid, ar.resname, ar.name, ar.mass, at.segid, at.resid, at.resname, at.name, at.mass)) errmsg = ("Inconsistent selections, masses differ by more than" "{0:f}; mis-matching atoms" "are shown above.".format(self.tol_mass)) logger.error(errmsg) raise SelectionError(errmsg) del mass_mismatches # TODO: # - make a group comparison a class that contains the checks above # - use this class for the *select* group and the additional # *groupselections* groups each a dict with reference/mobile self._groupselections_atoms = [ { 'reference': self.reference.universe.select_atoms(*s['reference']), 'mobile': self.atomgroup.universe.select_atoms(*s['mobile']), } for s in self.groupselections] # sanity check for igroup, (sel, atoms) in enumerate(zip(self.groupselections, self._groupselections_atoms)): if len(atoms['mobile']) != len(atoms['reference']): logger.exception('SelectionError: Group Selection') raise SelectionError( "Group selection {0}: {1} | {2}: Reference and trajectory " "atom selections do not contain the same number of atoms: " "N_ref={3}, N_traj={4}".format( igroup, sel['reference'], sel['mobile'], len(atoms['reference']), len(atoms['mobile']))) # Explicitly check for "mass" because this option CAN # be used with groupselection. (get_weights() returns the mass array # for "mass") if not iterable(self.weights) and self.weights == "mass": pass else: self.weights = get_weights(self.mobile_atoms, self.weights) # cannot use arbitrary weight array (for superposition) with # groupselections because arrays will not match if (len(self.groupselections) > 0 and ( iterable(self.weights) or self.weights not in ("mass", None))): raise ValueError("groupselections can only be combined with " "weights=None or weights='mass', not a weight " "array.") # initialized to note for testing the save function self.rmsd = None
def test_iterator(self): assert_equal(util.iterable(xrange(3)), True)
def test_tuples(self): assert_equal(util.iterable((1, 2, 3)), True) assert_equal(util.iterable(()), True)
def __init__(self, atomgroup, reference=None, select='all', groupselections=None, filename="rmsd.dat", weights=None, tol_mass=0.1, ref_frame=0, **kwargs): # DEPRECATION: remove filename kwarg in 1.0 r"""Parameters ---------- atomgroup : AtomGroup or Universe Group of atoms for which the RMSD is calculated. If a trajectory is associated with the atoms then the computation iterates over the trajectory. reference : AtomGroup or Universe (optional) Group of reference atoms; if ``None`` then the current frame of `atomgroup` is used. select : str or dict or tuple (optional) The selection to operate on; can be one of: 1. any valid selection string for :meth:`~MDAnalysis.core.groups.AtomGroup.select_atoms` that produces identical selections in `atomgroup` and `reference`; or 2. a dictionary ``{'mobile': sel1, 'reference': sel2}`` where *sel1* and *sel2* are valid selection strings that are applied to `atomgroup` and `reference` respectively (the :func:`MDAnalysis.analysis.align.fasta2select` function returns such a dictionary based on a ClustalW_ or STAMP_ sequence alignment); or 3. a tuple ``(sel1, sel2)`` When using 2. or 3. with *sel1* and *sel2* then these selection strings are applied to `atomgroup` and `reference` respectively and should generate *groups of equivalent atoms*. *sel1* and *sel2* can each also be a *list of selection strings* to generate a :class:`~MDAnalysis.core.groups.AtomGroup` with defined atom order as described under :ref:`ordered-selections-label`). groupselections : list (optional) A list of selections as described for `select`, with the difference that these selections are *always applied to the full universes*, i.e., ``atomgroup.universe.select_atoms(sel1)`` and ``reference.universe.select_atoms(sel2)``. Each selection describes additional RMSDs to be computed *after the structures have been superimposed* according to `select`. No additional fitting is performed.The output contains one additional column for each selection. .. Note:: Experimental feature. Only limited error checking implemented. filename : str (optional) write RMSD into file with :meth:`RMSD.save` .. deprecated:; 0.19.0 `filename` will be removed together with :meth:`save` in 1.0. weights : {"mass", ``None``} or array_like (optional) choose weights. With ``"mass"`` uses masses as weights; with ``None`` weigh each atom equally. If a float array of the same length as `atomgroup` is provided, use each element of the `array_like` as a weight for the corresponding atom in `atomgroup`. tol_mass : float (optional) Reject match if the atomic masses for matched atoms differ by more than `tol_mass`. ref_frame : int (optional) frame index to select frame from `reference` verbose : bool (optional) Show detailed progress of the calculation if set to ``True``; the default is ``False``. Raises ------ SelectionError If the selections from `atomgroup` and `reference` do not match. TypeError If `weights` is not of the appropriate type; see also :func:`MDAnalysis.lib.util.get_weights` ValueError If `weights` are not compatible with `atomgroup` (not the same length) or if it is not a 1D array (see :func:`MDAnalysis.lib.util.get_weights`). A :exc:`ValueError` is also raised if `weights` are not compatible with `groupselections`: only equal weights (``weights=None``) or mass-weighted (``weights="mass"``) are supported for additional `groupselections`. Notes ----- The root mean square deviation :math:`\rho(t)` of a group of :math:`N` atoms relative to a reference structure as a function of time is calculated as .. math:: \rho(t) = \sqrt{\frac{1}{N} \sum_{i=1}^N w_i \left(\mathbf{x}_i(t) - \mathbf{x}_i^{\text{ref}}\right)^2} The weights :math:`w_i` are calculated from the input weights `weights` :math:`w'_i` as relative to the mean of the input weights: .. math:: w_i = \frac{w'_i}{\langle w' \rangle} The selected coordinates from `atomgroup` are optimally superimposed (translation and rotation) on the `reference` coordinates at each time step as to minimize the RMSD. Douglas Theobald's fast QCP algorithm [Theobald2005]_ is used for the rotational superposition and to calculate the RMSD (see :mod:`MDAnalysis.lib.qcprot` for implementation details). The class runs various checks on the input to ensure that the two atom groups can be compared. This includes a comparison of atom masses (i.e., only the positions of atoms of the same mass will be considered to be correct for comparison). If masses should not be checked, just set `tol_mass` to a large value such as 1000. .. _ClustalW: http://www.clustal.org/ .. _STAMP: http://www.compbio.dundee.ac.uk/manuals/stamp.4.2/ See Also -------- rmsd .. versionadded:: 0.7.7 .. versionchanged:: 0.8 `groupselections` added .. versionchanged:: 0.16.0 Flexible weighting scheme with new `weights` keyword. .. deprecated:: 0.16.0 Instead of ``mass_weighted=True`` (removal in 0.17.0) use new ``weights='mass'``; refactored to fit with AnalysisBase API .. versionchanged:: 0.17.0 removed deprecated `mass_weighted` keyword; `groupselections` are *not* rotationally superimposed any more. .. deprecated:: 0.19.0 `filename` will be removed in 1.0 """ super(RMSD, self).__init__(atomgroup.universe.trajectory, **kwargs) self.atomgroup = atomgroup self.reference = reference if reference is not None else self.atomgroup select = process_selection(select) self.groupselections = ( [process_selection(s) for s in groupselections] if groupselections is not None else []) self.weights = weights self.tol_mass = tol_mass self.ref_frame = ref_frame self.filename = filename # DEPRECATED in 0.19.0, remove in 1.0.0 self.ref_atoms = self.reference.select_atoms(*select['reference']) self.mobile_atoms = self.atomgroup.select_atoms(*select['mobile']) if len(self.ref_atoms) != len(self.mobile_atoms): err = ("Reference and trajectory atom selections do " "not contain the same number of atoms: " "N_ref={0:d}, N_traj={1:d}".format( self.ref_atoms.n_atoms, self.mobile_atoms.n_atoms)) logger.exception(err) raise SelectionError(err) logger.info("RMS calculation " "for {0:d} atoms.".format(len(self.ref_atoms))) mass_mismatches = (np.absolute( (self.ref_atoms.masses - self.mobile_atoms.masses)) > self.tol_mass) if np.any(mass_mismatches): # diagnostic output: logger.error("Atoms: reference | mobile") for ar, at in zip(self.ref_atoms, self.mobile_atoms): if ar.name != at.name: logger.error("{0!s:>4} {1:3d} {2!s:>3} {3!s:>3} {4:6.3f}" "| {5!s:>4} {6:3d} {7!s:>3} {8!s:>3}" "{9:6.3f}".format(ar.segid, ar.resid, ar.resname, ar.name, ar.mass, at.segid, at.resid, at.resname, at.name, at.mass)) errmsg = ("Inconsistent selections, masses differ by more than" "{0:f}; mis-matching atoms" "are shown above.".format(self.tol_mass)) logger.error(errmsg) raise SelectionError(errmsg) del mass_mismatches # TODO: # - make a group comparison a class that contains the checks above # - use this class for the *select* group and the additional # *groupselections* groups each a dict with reference/mobile self._groupselections_atoms = [{ 'reference': self.reference.universe.select_atoms(*s['reference']), 'mobile': self.atomgroup.universe.select_atoms(*s['mobile']), } for s in self.groupselections] # sanity check for igroup, (sel, atoms) in enumerate( zip(self.groupselections, self._groupselections_atoms)): if len(atoms['mobile']) != len(atoms['reference']): logger.exception('SelectionError: Group Selection') raise SelectionError( "Group selection {0}: {1} | {2}: Reference and trajectory " "atom selections do not contain the same number of atoms: " "N_ref={3}, N_traj={4}".format(igroup, sel['reference'], sel['mobile'], len(atoms['reference']), len(atoms['mobile']))) # Explicitly check for "mass" because this option CAN # be used with groupselection. (get_weights() returns the mass array # for "mass") if not iterable(self.weights) and self.weights == "mass": pass else: self.weights = get_weights(self.mobile_atoms, self.weights) # cannot use arbitrary weight array (for superposition) with # groupselections because arrays will not match if (len(self.groupselections) > 0 and (iterable(self.weights) or self.weights not in ("mass", None))): raise ValueError("groupselections can only be combined with " "weights=None or weights='mass', not a weight " "array.") # initialized to note for testing the save function self.rmsd = None
def test_arrays(self): assert_equal(util.iterable(np.array([1, 2, 3])), True)