def test_rotate(input_atoms, as_list, axis, random_seed, centered): """ Rotate and rotate back and check if the coordinates are still the same. """ np.random.seed(random_seed) angles = np.zeros(3) angles[axis] = np.random.rand() * 2*np.pi neg_angles = -angles if as_list: angles = angles.tolist() neg_angles = neg_angles.tolist() func = struc.rotate_centered if centered else struc.rotate rotated = func(input_atoms, angles) restored = func(rotated, neg_angles) assert type(restored) == type(input_atoms) assert struc.coord(restored).shape == struc.coord(input_atoms).shape print(np.max(np.abs(struc.coord(restored) - struc.coord(input_atoms)))) assert np.allclose( struc.coord(restored), struc.coord(input_atoms), atol=1e-5 ) if centered and struc.coord(input_atoms).ndim > 1: assert np.allclose( struc.centroid(restored), struc.centroid(input_atoms), atol=1e-5 )
def membrane_leaflet_identification(atoms, lipid_head="P"): """ Simple algorithm to identify membrane leaflets in a topology Parameters --------- atoms : AtomArray or AtomArrayStack lipid_head : str, optional Name of the lipid headgroup Returns ------- tuple of ndarray Two masks for AtomArray identifying headgroups of the upper and lower leaflet """ if isinstance(atoms, AtomArrayStack): atoms = atoms[0] lipid_heads = atoms.atom_name == lipid_head z_center = struct.centroid(atoms[lipid_heads])[2] upper = (atoms.coord[:, 2] < z_center) & lipid_heads lower = np.invert(upper) & lipid_heads return upper, lower
def test_rotate_360(input_atoms, x, y, z, centered): """ Rotate by 360 degrees and expect that the coordinates have not changed. """ func = struc.rotate_centered if centered else struc.rotate rotated = func(input_atoms, [x, y, z]) assert type(rotated) == type(input_atoms) assert struc.coord(rotated).shape == struc.coord(input_atoms).shape assert np.allclose( struc.coord(rotated), struc.coord(input_atoms), atol=1e-5 ) if centered and struc.coord(input_atoms).ndim > 1: assert np.allclose( struc.centroid(rotated), struc.centroid(input_atoms), atol=1e-5 )
def test_residue_iter(array): centroid = [ struc.centroid(res).tolist() for res in struc.residue_iter(array) ] ref_centroid = struc.apply_residue_wise(array, array.coord, np.average, axis=0) assert centroid == ref_centroid.tolist()
def test_remove_pbc_unsegmented(): """ `remove_pbc()` should not alter unsegmented structures, when the structure is entirely in the box. Exclude the solvent, due to high distances between each atom. """ ref_array = load_structure(join(data_dir("structure"), "3o5r.mmtf")) # Center structure in box centroid = struc.centroid(ref_array) box_center = np.diag(ref_array.box) / 2 ref_array = struc.translate(ref_array, box_center - centroid) # Remove solvent ref_array = ref_array[~struc.filter_solvent(ref_array)] array = struc.remove_pbc(ref_array) assert ref_array.equal_annotation_categories(array) assert np.allclose(ref_array.coord, array.coord)
def test_centroid(): coord = struc.coord([[1, 1, 1], [0, -1, -1], [-1, 0, 0]]) assert struc.centroid(coord).tolist() == [0, 0, 0]
def test_docking(flexible): """ Test :class:`VinaApp` for the case of docking biotin to streptavidin. The output binding pose should be very similar to the pose in the PDB structure. """ # A structure of a straptavidin-biotin complex mmtf_file = mmtf.MMTFFile.read(join(data_dir("application"), "2rtg.mmtf")) structure = mmtf.get_structure(mmtf_file, model=1, extra_fields=["charge"], include_bonds=True) structure = structure[structure.chain_id == "B"] receptor = structure[struc.filter_amino_acids(structure)] ref_ligand = structure[structure.res_name == "BTN"] ref_ligand_coord = ref_ligand.coord ligand = info.residue("BTN") # Remove hydrogen atom that is missing in ref_ligand ligand = ligand[ligand.atom_name != "HO2"] if flexible: # Two residues within the binding pocket: ASN23, SER88 flexible_mask = np.isin(receptor.res_id, (23, 88)) else: flexible_mask = None app = VinaApp(ligand, receptor, struc.centroid(ref_ligand), [20, 20, 20], flexible=flexible_mask) app.set_seed(0) app.start() app.join() test_ligand_coord = app.get_ligand_coord() test_receptor_coord = app.get_receptor_coord() energies = app.get_energies() # One energy value per model assert len(test_ligand_coord) == len(energies) assert len(test_receptor_coord) == len(energies) assert np.all(energies < 0) # Select best binding pose test_ligand_coord = test_ligand_coord[0] not_nan_mask = ~np.isnan(test_ligand_coord).any(axis=-1) ref_ligand_coord = ref_ligand_coord[not_nan_mask] test_ligand_coord = test_ligand_coord[not_nan_mask] # Check if it least one atom is preserved assert test_ligand_coord.shape[1] > 0 rmsd = struc.rmsd(ref_ligand_coord, test_ligand_coord) # The deviation of the best pose from the real conformation # should be less than 1 Å assert rmsd < 1.0 if flexible: # Select best binding pose test_receptor_coord = test_receptor_coord[0] not_nan_mask = ~np.isnan(test_receptor_coord).any(axis=-1) ref_receptor_coord = receptor[not_nan_mask] test_receptor_coord = test_receptor_coord[not_nan_mask] # Check if it least one atom is preserved assert test_receptor_coord.shape[1] > 0 # The flexible residues should have a maximum deviation of 1 Å # from the original conformation assert np.max(struc.distance(test_receptor_coord, ref_receptor_coord)) < 1.0 else: ref_receptor_coord = receptor.coord for model_coord in test_receptor_coord: assert np.array_equal(model_coord, ref_receptor_coord)
# Get the receptor structure # and the original 'correct' conformation of the ligand mmtf_file = mmtf.MMTFFile.read(rcsb.fetch("2RTG", "mmtf")) structure = mmtf.get_structure( # Include formal charge for accurate partial charge calculation mmtf_file, model=1, include_bonds=True, extra_fields=["charge"]) # The asymmetric unit describes a streptavidin homodimer # However, we are only interested in a single monomer structure = structure[structure.chain_id == "B"] receptor = structure[struc.filter_amino_acids(structure)] ref_ligand = structure[structure.res_name == "BTN"] ref_ligand_center = struc.centroid(ref_ligand) # Independently, get the ligand without optimized conformation # from the chemical components dictionary ligand = info.residue("BTN") # Search for a binding mode in a 20 Å radius # of the original ligand position app = autodock.VinaApp(ligand, receptor, ref_ligand_center, [20, 20, 20]) # 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
base, n3.coord - c6.coord, np.array([1, 0, 0]) ) for base in (purine, pyrimidine) ] # Coords are changed -> update 'Atom' objects n1, n3, c4, c5 = [pyrimidine[pyrimidine.atom_name == name][0] for name in ("N1", "N3", "C4", "C5")] # Pyrimidine base plane normal vector is aligned to z-axis # Furthermore, distance between bases is set purine, pyrimidine = [ struc.align_vectors( base, np.cross(n3.coord - n1.coord, c5.coord - n1.coord), np.array([0, 0, 1]), origin_position = struc.centroid(purine + pyrimidine), # 10 Å separation between pairs target_position = np.array([0, 10*i, 0]) ) for base in (purine, pyrimidine) ] pairs[i] = (purine, pyrimidine) # Plot base pairs # Merge bases into a single atom array atoms = pairs[0][0] + pairs[0][1] + pairs[1][0] + pairs[1][1] # Color by element colors = np.zeros((atoms.array_length(), 3)) colors[atoms.element == "H"] = (0.8, 0.8, 0.8) # gray colors[atoms.element == "C"] = (0.2, 0.2, 0.2) # darkgray colors[atoms.element == "N"] = (0.0, 0.0, 0.8) # blue colors[atoms.element == "O"] = (0.8, 0.0, 0.0) # red