# than the sum of their VdW radii, if they are not bonded to # each other accepted = True distances = struc.distance(coord[:, np.newaxis], coord[np.newaxis, :]) clashed = distances < vdw_radii_mean for clash_atom1, clash_atom2 in zip(*np.where(clashed)): if clash_atom1 == clash_atom2: # Ignore distance of an atom to itself continue if (clash_atom1, clash_atom2) not in bond_list: # Nonbonded atoms clash # -> structure is not accepted accepted = False rotamer_coord[i] = coord rotamers = struc.from_template(residue, rotamer_coord) ### Superimpose backbone onto first model for better visualization ### rotamers, _ = struc.superimpose(rotamers[0], rotamers, atom_mask=struc.filter_backbone(rotamers)) ### Visualize rotamers ### colors = np.zeros((residue.array_length(), 3)) colors[residue.element == "H"] = (0.8, 0.8, 0.8) # gray colors[residue.element == "C"] = (0.0, 0.8, 0.0) # green colors[residue.element == "N"] = (0.0, 0.0, 0.8) # blue colors[residue.element == "O"] = (0.8, 0.0, 0.0) # red # For consistency, each subplot has the same box size coord = rotamers.coord
# For reproducibility app.set_seed(0) # This is the maximum number: # Vina may find less interesting binding modes # and thus output less models app.set_max_number_of_models(100) # Effectively no limit app.set_energy_range(100.0) # Start docking run app.start() app.join() docked_coord = app.get_ligand_coord() energies = app.get_energies() # Create an AtomArrayStack for all docked binding modes docked_ligand = struc.from_template(ligand, docked_coord) # As Vina discards all nonpolar hydrogen atoms, their respective # coordinates are NaN -> remove these atoms docked_ligand = docked_ligand[..., ~np.isnan(docked_ligand.coord[0]).any(axis=-1)] # For comparison of the docked pose with the experimentally determined # reference conformation, the atom order of both must be exactly the # same # Therefore, all atoms, that are additional in one of both models, # e.g. carboxy or nonpolar hydrogen atoms, are removed... docked_ligand = docked_ligand[ ..., np.isin(docked_ligand.atom_name, ref_ligand.atom_name)] docked_ligand = docked_ligand[..., info.standardize_order(docked_ligand)] # ...and the atom order is standardized ref_ligand = ref_ligand[np.isin(ref_ligand.atom_name, docked_ligand.atom_name)]
vectors = vectors[:-6] # Extract vectors for given mode and reshape to (n,3) array mode_vectors = vectors[MODE].reshape((-1, 3)) # Rescale, so that the largest vector has the length 'MAX_AMPLITUDE' vector_lenghts = np.sqrt(np.sum(mode_vectors**2, axis=-1)) scale = MAX_AMPLITUDE / np.max(vector_lenghts) mode_vectors *= scale # Stepwise application of eigenvectors as smooth sine oscillation time = np.linspace(0, 2 * np.pi, FRAMES, endpoint=False) deviation = np.sin(time)[:, newaxis, newaxis] * mode_vectors # Apply oscillation of CA atom to all atoms in the corresponding residue oscillation = np.zeros((FRAMES, len(protein_chain), 3)) residue_starts = struc.get_residue_starts( protein_chain, # The last array element will be the length of the atom array, # i.e. no valid index add_exclusive_stop=True) for i in range(len(residue_starts) - 1): res_start = residue_starts[i] res_stop = residue_starts[i + 1] oscillation[:, res_start:res_stop, :] \ = protein_chain.coord[res_start:res_stop, :] + deviation[:, i:i+1, :] # An atom array stack containing all frames oscillating_structure = struc.from_template(protein_chain, oscillation) # Save as PDB for rendering a video with PyMOL #strucio.save_structure("glycosylase_oscillation.pdb", oscillating_structure) # biotite_static_image = glycosylase_oscillation.gif
def to_structure(self, state=None, altloc="first", extra_fields=None, include_bonds=False): """ Convert this object into an :class:`AtomArray` or :class:`AtomArrayStack`. The returned :class:`AtomArray` contains the optional annotation categories ``b_factor``, ``occupancy`` and ``charge``. Parameters ---------- state : int, optional If this parameter is given, the function will return an :class:`AtomArray` corresponding to the given state of the *PyMOL* object. If this parameter is omitted, an :class:`AtomArrayStack` containing all states will be returned, even if the *PyMOL* object contains only one state. altloc : {'first', 'occupancy', 'all'} This parameter defines how *altloc* IDs are handled: - ``'first'`` - Use atoms that have the first *altloc* ID appearing in a residue. - ``'occupancy'`` - Use atoms that have the *altloc* ID with the highest occupancy for a residue. - ``'all'`` - Use all atoms. Note that this leads to duplicate atoms. When this option is chosen, the ``altloc_id`` annotation array is added to the returned structure. include_bonds : bool, optional If set to true, an associated :class:`BondList` will be created for the returned structure. Returns ------- structure : AtomArray or AtomArrayStack The converted structure. Whether an :class:`AtomArray` or :class:`AtomArrayStack` is returned depends on the `state` parameter. """ if state is None: model = self._cmd.get_model(self._name, state=1) template = convert_to_atom_array(model, include_bonds) expected_length = None coord = [] for i in range(self._cmd.count_states(self._name)): state_coord = self._cmd.get_coordset(self._name, state=i + 1) if expected_length is None: expected_length = len(state_coord) elif len(state_coord) != expected_length: raise ValueError( "The models have different numbers of atoms") coord.append(state_coord) coord = np.stack(coord) structure = struc.from_template(template, coord) else: model = self._cmd.get_model(self._name, state=state) structure = convert_to_atom_array(model, include_bonds) # Filter altloc IDs and return if altloc == "occupancy": structure = structure[ ..., struc.filter_highest_occupancy_altloc( structure, structure.altloc_id, structure.occupancy)] structure.del_annotation("altloc_id") return structure elif altloc == "first": structure = structure[ ..., struc.filter_first_altloc(structure, structure.altloc_id)] structure.del_annotation("altloc_id") return structure elif altloc == "all": return structure else: raise ValueError(f"'{altloc}' is not a valid 'altloc' option")