Source code for chilife.IntrinsicLabel

import numpy as np
from .protein_utils import FreeAtom
import MDAnalysis as mda
from .MolSys import MolecularSystemBase


[docs] class IntrinsicLabel: """ A helper object to assign spin density to part of a protein that is already present, e.g. metal ligands. IntrinsicLabels can be used with the :func:`~chilife.chilife.distance_distribution` function Parameters ---------- res : str 3-character identifier of desired residue, e.g. NC2 (Native Cu(II)). atom_selection : MDAnalysis.AtomGroup, chiLife.MolecularSystemBase Group of atoms constituting the intrinsic label. These selections can consist of multiple residues, which can be useful in the case of ions with multiple coordinating residues spin_atoms : str, list, tuple, array, dict, MDAnalysis.AtomGroup, chiLife MolecularSystemBase Atoms of the intrinsic label that host the unpaired electron density. Can be a single atom name, a list/tuple/array of atom names or a dictionary mapping atom names to their relative populations. ``spin_atoms `` can also be an ``MDAnalysis.AtomGroup`` object derived from the same MDAnalysis.Universe as the ``atom_selection`` keyword argument. Use of an ``AtomGroup`` is particularly useful when there is spin density is distributed on several atoms with the same name on different residues within the ``IntrinsicLabel``. """ def __init__(self, res, atom_selection, spin_atoms=None, name='IntrinsicLabel'): self._selection = atom_selection self._coords = atom_selection.positions.copy()[None, :, :] self.coords = self._coords self.atom_names = atom_selection.names.copy() self.atom_types = atom_selection.types self.name = name self.label = res self.res = res self.chain = "".join(set(atom_selection.segids)) self.weights = np.array([1]) if isinstance(spin_atoms, (list, tuple, np.ndarray)): self.spin_atoms = np.asarray(spin_atoms) self.spin_weights = np.ones(len(spin_atoms)) / len(spin_atoms) elif isinstance(spin_atoms, dict): self.spin_atoms = np.asarray([key for key in spin_atoms]) self.spin_weights = np.asarray([val for val in spin_atoms.values()]) elif isinstance(spin_atoms, str): self.spin_atoms = np.asarray([spin_atoms]) self.spin_weights = np.asarray([1]) elif isinstance(spin_atoms, (mda.AtomGroup, MolecularSystemBase)): self.spin_atoms = spin_atoms.names.copy() self.spin_weights = np.ones(len(spin_atoms)) / len(spin_atoms) else: raise RuntimeError("spin_atoms must contain a string, a list/tuple/array of strings, a dict") self.site = atom_selection.select_atoms(f'name {self.spin_atoms[np.argmax(self.spin_weights)]}').resnums[0] sa_mask = np.isin(self.atom_names, self.spin_atoms,) self.spin_idx = np.argwhere(sa_mask) self.spin_idx.shape = -1 self.atoms = [FreeAtom(atom.name, atom.type, idx, atom.resname, atom.resnum, atom.position) for idx, atom in enumerate(self._selection.atoms)] @property def spin_coords(self): """get the spin coordinates of the rotamer ensemble""" return np.squeeze(self._coords[:, self.spin_idx, :]) @property def spin_centers(self): """get the spin center of the rotamers in the ensemble""" if len(self.spin_idx) > 0: spin_centers = np.average(self._coords[:, self.spin_idx, :], weights=self.spin_weights, axis=1) else: spin_centers = np.array([]) return np.atleast_2d(np.squeeze(spin_centers)) @property def spin_centroid(self): """Average location of all the label's `spin_coords` weighted based off of the rotamer weights""" return np.average(self.spin_centers, weights=self.weights, axis=0)