def __init__(self, mobile, reference, select='all', filename=None, prefix='rmsfit_', weights=None, tol_mass=0.1, strict=False, force=True, in_memory=False, **kwargs): """Parameters ---------- mobile : Universe Universe containing trajectory to be fitted to reference reference : Universe Universe containing trajectory frame to be used as reference select : str (optional) Set as default to all, is used for Universe.select_atoms to choose subdomain to be fitted against filename : str (optional) Provide a filename for results to be written to prefix : str (optional) Provide a string to prepend to filename for results to be written to weights : {"mass", ``None``} or array_like (optional) choose weights. With ``"mass"`` uses masses of `reference` as weights; with ``None`` weigh each atom equally. If a float array of the same length as the selection is provided, use each element of the `array_like` as a weight for the corresponding atom in the selection. tol_mass : float (optional) Tolerance given to `get_matching_atoms` to find appropriate atoms strict : bool (optional) Force `get_matching_atoms` to fail if atoms can't be found using exact methods force : bool (optional) Force overwrite of filename for rmsd-fitting verbose : bool (optional) Set logger to show more information start : int (optional) First frame of trajectory to analyse, Default: 0 stop : int (optional) Last frame of trajectory to analyse, Default: -1 step : int (optional) Step between frames to analyse, Default: 1 in_memory : bool (optional) *Permanently* switch `mobile` to an in-memory trajectory so that alignment can be done in-place, which can improve performance substantially in some cases. In this case, no file is written out (`filename` and `prefix` are ignored) and only the coordinates of `mobile` are *changed in memory*. Attributes ---------- reference_atoms : AtomGroup Atoms of the reference structure to be aligned against mobile_atoms : AtomGroup Atoms inside each trajectory frame to be rmsd_aligned rmsd : Array Array of the rmsd values of the least rmsd between the mobile_atoms and reference_atoms after superposition and minimimization of rmsd filename : str String reflecting the filename of the file where mobile_atoms positions will be written to upon running RMSD alignment Notes ----- - If set to ``verbose=False``, it is recommended to wrap the statement in a ``try ... finally`` to guarantee restoring of the log level in the case of an exception. - The ``in_memory`` option changes the `mobile` universe to an in-memory representation (see :mod:`MDAnalysis.coordinates.memory`) for the remainder of the Python session. If ``mobile.trajectory`` is already a :class:`MemoryReader` then it is *always* treated as if ``in_memory`` had been set to ``True``. .. versionchanged:: 0.16.0 new general ``weights`` kwarg replace ``mass_weights`` .. deprecated:: 0.16.0 Instead of ``mass_weighted=True`` use new ``weights='mass'`` .. versionchanged:: 0.17.0 removed deprecated `mass_weighted` keyword """ select = rms.process_selection(select) self.ref_atoms = reference.select_atoms(*select['reference']) self.mobile_atoms = mobile.select_atoms(*select['mobile']) if in_memory or isinstance(mobile.trajectory, MemoryReader): mobile.transfer_to_memory() filename = None logger.info("Moved mobile trajectory to in-memory representation") else: if filename is None: path, fn = os.path.split(mobile.trajectory.filename) filename = os.path.join(path, prefix + fn) logger.info('filename of rms_align with no filename given' ': {0}'.format(filename)) if os.path.exists(filename) and not force: raise IOError( 'Filename already exists in path and force is not set' ' to True') # do this after setting the memory reader to have a reference to the # right reader. super(AlignTraj, self).__init__(mobile.trajectory, **kwargs) if not self._verbose: logging.disable(logging.WARN) # store reference to mobile atoms self.mobile = mobile.atoms self.filename = filename natoms = self.mobile.n_atoms self.ref_atoms, self.mobile_atoms = get_matching_atoms( self.ref_atoms, self.mobile_atoms, tol_mass=tol_mass, strict=strict) # with self.filename == None (in_memory), the NullWriter is chosen # (which just ignores input) and so only the in_memory trajectory is # retained self._writer = mda.Writer(self.filename, natoms) self._weights = get_weights(self.ref_atoms, weights) logger.info("RMS-fitting on {0:d} atoms.".format(len(self.ref_atoms)))
def __init__(self, mobile, reference, select='all', filename=None, prefix='rmsfit_', weights=None, tol_mass=0.1, strict=False, force=True, in_memory=False, **kwargs): """Parameters ---------- mobile : Universe Universe containing trajectory to be fitted to reference reference : Universe Universe containing trajectory frame to be used as reference select : str (optional) Set as default to all, is used for Universe.select_atoms to choose subdomain to be fitted against filename : str (optional) Provide a filename for results to be written to prefix : str (optional) Provide a string to prepend to filename for results to be written to weights : {"mass", ``None``} or array_like (optional) choose weights. With ``"mass"`` uses masses of `reference` as weights; with ``None`` weigh each atom equally. If a float array of the same length as the selection is provided, use each element of the `array_like` as a weight for the corresponding atom in the selection. tol_mass : float (optional) Tolerance given to `get_matching_atoms` to find appropriate atoms strict : bool (optional) Force `get_matching_atoms` to fail if atoms can't be found using exact methods force : bool (optional) Force overwrite of filename for rmsd-fitting start : int (optional) First frame of trajectory to analyse, Default: 0 stop : int (optional) Last frame of trajectory to analyse, Default: -1 step : int (optional) Step between frames to analyse, Default: 1 in_memory : bool (optional) *Permanently* switch `mobile` to an in-memory trajectory so that alignment can be done in-place, which can improve performance substantially in some cases. In this case, no file is written out (`filename` and `prefix` are ignored) and only the coordinates of `mobile` are *changed in memory*. verbose : bool (optional) Set logger to show more information and show detailed progress of the calculation if set to ``True``; the default is ``False``. Attributes ---------- reference_atoms : AtomGroup Atoms of the reference structure to be aligned against mobile_atoms : AtomGroup Atoms inside each trajectory frame to be rmsd_aligned rmsd : Array Array of the rmsd values of the least rmsd between the mobile_atoms and reference_atoms after superposition and minimimization of rmsd filename : str String reflecting the filename of the file where mobile_atoms positions will be written to upon running RMSD alignment Notes ----- - If set to ``verbose=False``, it is recommended to wrap the statement in a ``try ... finally`` to guarantee restoring of the log level in the case of an exception. - The ``in_memory`` option changes the `mobile` universe to an in-memory representation (see :mod:`MDAnalysis.coordinates.memory`) for the remainder of the Python session. If ``mobile.trajectory`` is already a :class:`MemoryReader` then it is *always* treated as if ``in_memory`` had been set to ``True``. .. deprecated:: 0.19.1 Default ``filename`` directory will change in 1.0 to the current directory. .. versionchanged:: 0.16.0 new general ``weights`` kwarg replace ``mass_weights`` .. deprecated:: 0.16.0 Instead of ``mass_weighted=True`` use new ``weights='mass'`` .. versionchanged:: 0.17.0 removed deprecated `mass_weighted` keyword """ select = rms.process_selection(select) self.ref_atoms = reference.select_atoms(*select['reference']) self.mobile_atoms = mobile.select_atoms(*select['mobile']) if in_memory or isinstance(mobile.trajectory, MemoryReader): mobile.transfer_to_memory() filename = None logger.info("Moved mobile trajectory to in-memory representation") else: if filename is None: # DEPRECATED in 0.19.1 # Change in 1.0 # # fn = os.path.split(mobile.trajectory.filename)[1] # filename = prefix + fn path, fn = os.path.split(mobile.trajectory.filename) filename = os.path.join(path, prefix + fn) logger.info('filename of rms_align with no filename given' ': {0}'.format(filename)) if os.path.exists(filename) and not force: raise IOError( 'Filename already exists in path and force is not set' ' to True') # do this after setting the memory reader to have a reference to the # right reader. super(AlignTraj, self).__init__(mobile.trajectory, **kwargs) if not self._verbose: logging.disable(logging.WARN) # store reference to mobile atoms self.mobile = mobile.atoms self.filename = filename natoms = self.mobile.n_atoms self.ref_atoms, self.mobile_atoms = get_matching_atoms( self.ref_atoms, self.mobile_atoms, tol_mass=tol_mass, strict=strict) # with self.filename == None (in_memory), the NullWriter is chosen # (which just ignores input) and so only the in_memory trajectory is # retained self._writer = mda.Writer(self.filename, natoms) self._weights = get_weights(self.ref_atoms, weights) logger.info("RMS-fitting on {0:d} atoms.".format(len(self.ref_atoms)))
def alignto(mobile, reference, select="all", weights=None, subselection=None, tol_mass=0.1, strict=False): """Perform a spatial superposition by minimizing the RMSD. Spatially align the group of atoms `mobile` to `reference` by doing a RMSD fit on `select` atoms. The superposition is done in the following way: 1. A rotation matrix is computed that minimizes the RMSD between the coordinates of `mobile.select_atoms(sel1)` and `reference.select_atoms(sel2)`; before the rotation, `mobile` is translated so that its center of geometry (or center of mass) coincides with the one of `reference`. (See below for explanation of how *sel1* and *sel2* are derived from `select`.) 2. All atoms in :class:`~MDAnalysis.core.universe.Universe` that contain `mobile` are shifted and rotated. (See below for how to change this behavior through the `subselection` keyword.) The `mobile` and `reference` atom groups can be constructed so that they already match atom by atom. In this case, `select` should be set to "all" (or ``None``) so that no further selections are applied to `mobile` and `reference`, therefore preserving the exact atom ordering (see :ref:`ordered-selections-label`). .. Warning:: The atom order for `mobile` and `reference` is *only* preserved when `select` is either "all" or ``None``. In any other case, a new selection will be made that will sort the resulting AtomGroup by index and therefore destroy the correspondence between the two groups. **It is safest not to mix ordered AtomGroups with selection strings.** Parameters ---------- mobile : Universe or AtomGroup structure to be aligned, a :class:`~MDAnalysis.core.groups.AtomGroup` or a whole :class:`~MDAnalysis.core.universe.Universe` reference : Universe or AtomGroup reference structure, a :class:`~MDAnalysis.core.groups.AtomGroup` or a whole :class:`~MDAnalysis.core.universe.Universe` 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 `mobile` and `reference`; or 2. a dictionary ``{'mobile': sel1, 'reference': sel2}`` where *sel1* and *sel2* are valid selection strings that are applied to `mobile` 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`). 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 `mobile` is provided, use each element of the `array_like` as a weight for the corresponding atom in `mobile`. tol_mass: float (optional) Reject match if the atomic masses for matched atoms differ by more than `tol_mass`, default [0.1] strict: bool (optional) ``True`` Will raise :exc:`SelectionError` if a single atom does not match between the two selections. ``False`` [default] Will try to prepare a matching selection by dropping residues with non-matching atoms. See :func:`get_matching_atoms` for details. subselection : str or AtomGroup or None (optional) Apply the transformation only to this selection. ``None`` [default] Apply to ``mobile.universe.atoms`` (i.e., all atoms in the context of the selection from `mobile` such as the rest of a protein, ligands and the surrounding water) *selection-string* Apply to ``mobile.select_atoms(selection-string)`` :class:`~MDAnalysis.core.groups.AtomGroup` Apply to the arbitrary group of atoms Returns ------- old_rmsd : float RMSD before spatial alignment new_rmsd : float RMSD after spatial alignment See Also -------- AlignTraj: More efficient method for RMSD-fitting trajectories. .. _ClustalW: http://www.clustal.org/ .. _STAMP: http://www.compbio.dundee.ac.uk/manuals/stamp.4.2/ .. versionchanged:: 0.8 Added check that the two groups describe the same atoms including the new *tol_mass* keyword. .. versionchanged:: 0.10.0 Uses :func:`get_matching_atoms` to work with incomplete selections and new `strict` keyword. The new default is to be lenient whereas the old behavior was the equivalent of ``strict = True``. .. versionchanged:: 0.16.0 new general 'weights' kwarg replace `mass_weighted`, deprecated `mass_weighted` .. deprecated:: 0.16.0 Instead of ``mass_weighted=True`` use new ``weights='mass'`` .. versionchanged:: 0.17.0 Deprecated keyword `mass_weighted` was removed. """ if select in ('all', None): # keep the EXACT order in the input AtomGroups; select_atoms('all') # orders them by index, which can lead to wrong results if the user # has crafted mobile and reference to match atom by atom mobile_atoms = mobile.atoms ref_atoms = reference.atoms else: select = rms.process_selection(select) mobile_atoms = mobile.select_atoms(*select['mobile']) ref_atoms = reference.select_atoms(*select['reference']) ref_atoms, mobile_atoms = get_matching_atoms(ref_atoms, mobile_atoms, tol_mass=tol_mass, strict=strict) weights = get_weights(ref_atoms, weights) mobile_com = mobile_atoms.center(weights) ref_com = ref_atoms.center(weights) ref_coordinates = ref_atoms.positions - ref_com mobile_coordinates = mobile_atoms.positions - mobile_com old_rmsd = rms.rmsd(mobile_coordinates, ref_coordinates, weights) if subselection is None: # mobile_atoms is Universe mobile_atoms = mobile.universe.atoms elif isinstance(subselection, string_types): # select mobile_atoms from string mobile_atoms = mobile.select_atoms(subselection) else: try: # treat subselection as AtomGroup mobile_atoms = subselection.atoms except AttributeError: raise TypeError("subselection must be a selection string, an" " AtomGroup or Universe or None") # _fit_to DOES subtract center of mass, will provide proper min_rmsd mobile_atoms, new_rmsd = _fit_to(mobile_coordinates, ref_coordinates, mobile_atoms, mobile_com, ref_com, weights=weights) return old_rmsd, new_rmsd
def alignto(mobile, reference, select="all", weights=None, subselection=None, tol_mass=0.1, strict=False): """Perform a spatial superposition by minimizing the RMSD. Spatially align the group of atoms `mobile` to `reference` by doing a RMSD fit on `select` atoms. The superposition is done in the following way: 1. A rotation matrix is computed that minimizes the RMSD between the coordinates of `mobile.select_atoms(sel1)` and `reference.select_atoms(sel2)`; before the rotation, `mobile` is translated so that its center of geometry (or center of mass) coincides with the one of `reference`. (See below for explanation of how *sel1* and *sel2* are derived from `select`.) 2. All atoms in :class:`~MDAnalysis.core.universe.Universe` that contain `mobile` are shifted and rotated. (See below for how to change this behavior through the `subselection` keyword.) The `mobile` and `reference` atom groups can be constructed so that they already match atom by atom. In this case, `select` should be set to "all" (or ``None``) so that no further selections are applied to `mobile` and `reference`, therefore preserving the exact atom ordering (see :ref:`ordered-selections-label`). .. Warning:: The atom order for `mobile` and `reference` is *only* preserved when `select` is either "all" or ``None``. In any other case, a new selection will be made that will sort the resulting AtomGroup by index and therefore destroy the correspondence between the two groups. **It is safest not to mix ordered AtomGroups with selection strings.** Parameters ---------- mobile : Universe or AtomGroup structure to be aligned, a :class:`~MDAnalysis.core.groups.AtomGroup` or a whole :class:`~MDAnalysis.core.universe.Universe` reference : Universe or AtomGroup reference structure, a :class:`~MDAnalysis.core.groups.AtomGroup` or a whole :class:`~MDAnalysis.core.universe.Universe` 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 `mobile` and `reference`; or 2. a dictionary ``{'mobile': sel1, 'reference': sel2}`` where *sel1* and *sel2* are valid selection strings that are applied to `mobile` 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`). 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 `mobile` is provided, use each element of the `array_like` as a weight for the corresponding atom in `mobile`. tol_mass: float (optional) Reject match if the atomic masses for matched atoms differ by more than `tol_mass`, default [0.1] strict: bool (optional) ``True`` Will raise :exc:`SelectionError` if a single atom does not match between the two selections. ``False`` [default] Will try to prepare a matching selection by dropping residues with non-matching atoms. See :func:`get_matching_atoms` for details. subselection : str or AtomGroup or None (optional) Apply the transformation only to this selection. ``None`` [default] Apply to ``mobile.universe.atoms`` (i.e., all atoms in the context of the selection from `mobile` such as the rest of a protein, ligands and the surrounding water) *selection-string* Apply to ``mobile.select_atoms(selection-string)`` :class:`~MDAnalysis.core.groups.AtomGroup` Apply to the arbitrary group of atoms Returns ------- old_rmsd : float RMSD before spatial alignment new_rmsd : float RMSD after spatial alignment See Also -------- AlignTraj: More efficient method for RMSD-fitting trajectories. .. _ClustalW: http://www.clustal.org/ .. _STAMP: http://www.compbio.dundee.ac.uk/manuals/stamp.4.2/ .. versionchanged:: 0.8 Added check that the two groups describe the same atoms including the new *tol_mass* keyword. .. versionchanged:: 0.10.0 Uses :func:`get_matching_atoms` to work with incomplete selections and new `strict` keyword. The new default is to be lenient whereas the old behavior was the equivalent of ``strict = True``. .. versionchanged:: 0.16.0 new general 'weights' kwarg replace `mass_weighted`, deprecated `mass_weighted` .. deprecated:: 0.16.0 Instead of ``mass_weighted=True`` use new ``weights='mass'`` .. versionchanged:: 0.17.0 Deprecated keyword `mass_weighted` was removed. """ if select in ('all', None): # keep the EXACT order in the input AtomGroups; select_atoms('all') # orders them by index, which can lead to wrong results if the user # has crafted mobile and reference to match atom by atom mobile_atoms = mobile.atoms ref_atoms = reference.atoms else: select = rms.process_selection(select) mobile_atoms = mobile.select_atoms(*select['mobile']) ref_atoms = reference.select_atoms(*select['reference']) ref_atoms, mobile_atoms = get_matching_atoms(ref_atoms, mobile_atoms, tol_mass=tol_mass, strict=strict) weights = get_weights(ref_atoms, weights) mobile_com = mobile_atoms.center(weights) ref_com = ref_atoms.center(weights) ref_coordinates = ref_atoms.positions - ref_com mobile_coordinates = mobile_atoms.positions - mobile_com old_rmsd = rms.rmsd(mobile_coordinates, ref_coordinates, weights) if subselection is None: # mobile_atoms is Universe mobile_atoms = mobile.universe.atoms elif isinstance(subselection, string_types): # select mobile_atoms from string mobile_atoms = mobile.select_atoms(subselection) else: try: # treat subselection as AtomGroup mobile_atoms = subselection.atoms except AttributeError: raise TypeError("subselection must be a selection string, an" " AtomGroup or Universe or None") # _fit_to DOES subtract center of mass, will provide proper min_rmsd mobile_atoms, new_rmsd = _fit_to(mobile_coordinates, ref_coordinates, mobile_atoms, mobile_com, ref_com, weights=weights) return old_rmsd, new_rmsd