def test_map_nucleotide(): """Test the function map_nucleotide with some examples. """ pyrimidines = ['C', 'T', 'U'] purines = ['A', 'G'] # Test that the standard bases are correctly identified assert struc.map_nucleotide(residue('U')) == ('U', True) assert struc.map_nucleotide(residue('A')) == ('A', True) assert struc.map_nucleotide(residue('T')) == ('T', True) assert struc.map_nucleotide(residue('G')) == ('G', True) assert struc.map_nucleotide(residue('C')) == ('C', True) # Test that some non_standard nucleotides are mapped correctly to # pyrimidine/purine references psu_tuple = struc.map_nucleotide(residue('PSU')) assert psu_tuple[0] in pyrimidines assert psu_tuple[1] == False psu_tuple = struc.map_nucleotide(residue('3MC')) assert psu_tuple[0] in pyrimidines assert psu_tuple[1] == False i_tuple = struc.map_nucleotide(residue('I')) assert i_tuple[0] in purines assert i_tuple[1] == False m7g_tuple = struc.map_nucleotide(residue('M7G')) assert m7g_tuple[0] in purines assert m7g_tuple[1] == False with pytest.warns(struc.IncompleteStructureWarning): assert struc.map_nucleotide(residue('ALA')) == (None, False)
def test_get_molecule_indices(array, as_stack, as_bonds): """ Multiple tests to :func:`get_molecule_indices()` on a :class:`AtomArray` of random molecules. """ if as_stack: array = struc.stack([array]) if as_bonds: test_indices = struc.get_molecule_indices(array.bonds) else: test_indices = struc.get_molecule_indices(array) seen_atoms = 0 for indices in test_indices: molecule = array[..., indices] # Assert that all residue IDs in the molecule are equal # -> all atoms from the same molecule assert (molecule.res_id == molecule.res_id[0]).all() # Assert that no atom is missing from the molecule assert molecule.array_length() \ == info.residue(molecule.res_name[0]).array_length() seen_atoms += molecule.array_length() # Assert that all molecules are fond assert seen_atoms == array.array_length()
def array(): """ Create an :class:`AtomArray` containing a lot of different molecules. The atoms that belong to a single molecule are not adjacent in the :class:`AtomArray`, but a are shuffled in random positions of the :class:`AtomArray`. """ MOL_NAMES = [ "ARG", # Molecule with multiple branches "TRP", # Molecule with a cycle "GLC", # Molecule with a cycle "NA", # A single atom "ATP" # Larger molecule ] N_MOLECULES = 20 np.random.seed(0) atom_array = struc.AtomArray(0) for i, mol_name in enumerate(np.random.choice(MOL_NAMES, N_MOLECULES)): molecule = info.residue(mol_name) molecule.res_id[:] = i + 1 atom_array += molecule reordered_indices = np.random.choice(np.arange(atom_array.array_length()), atom_array.array_length(), replace=False) atom_array = atom_array[reordered_indices] return atom_array
def test_pdbx_consistency(path): """ Check if the structure parsed from a MOL file is equal to the same structure read from the *Chemical Component Dictionary* in PDBx format. In this case an SDF file is used, but it is compatible with the MOL format. """ mol_name = split(splitext(path)[0])[1] ref_atoms = info.residue(mol_name) # The CCD contains information about aromatic bond types, # but the SDF test files do not ref_atoms.bonds.remove_aromaticity() mol_file = mol.MOLFile.read(path) test_atoms = mol_file.get_structure() assert test_atoms.coord.shape == ref_atoms.coord.shape assert test_atoms.coord.flatten().tolist() \ == ref_atoms.coord.flatten().tolist() assert test_atoms.element.tolist() == ref_atoms.element.tolist() assert test_atoms.charge.tolist() == ref_atoms.charge.tolist() assert set(tuple(bond) for bond in test_atoms.bonds.as_array()) \ == set(tuple(bond) for bond in ref_atoms.bonds.as_array())
def flexGif(residue): mol = info.residue(residue) thetas = np.linspace(-30, 30, 60) thetas = np.append(thetas, np.linspace(30, 0, 30)) for i, theta in enumerate(thetas): mol_new = rotate_residue(mol, 0, theta * np.pi / 180) plot(mol_new, save_as=f"./plots/res_flex/{i}.png", show=False) thetas = np.linspace(0, 30, 60) thetas = np.append(thetas, np.linspace(30, -30, 30)) for j, theta in enumerate(thetas): mol_new = rotate_residue(mol, 1, theta * np.pi / 180) plot(mol_new, save_as=f"./plots/res_flex/{i+j}.png", show=False)
def test_find_rotatable_bonds(res_name, expected_bonds): """ Check the :func:`find_rotatable_bonds()` function based on known examples. """ molecule = info.residue(res_name) ref_bond_set = { tuple(sorted((name_i, name_j))) for name_i, name_j in expected_bonds } rotatable_bonds = struc.find_rotatable_bonds(molecule.bonds) test_bond_set = set() for i, j, _ in rotatable_bonds.as_array(): test_bond_set.add( tuple(sorted((molecule.atom_name[i], molecule.atom_name[j])))) # Compare with reference bonded atom names assert test_bond_set == ref_bond_set # All rotatable bonds must be single bonds assert np.all(rotatable_bonds.as_array()[:, 2] == struc.BondType.SINGLE)
import numpy as np import networkx as nx import matplotlib.pyplot as plt import biotite.structure as struc import biotite.structure.io as strucio import biotite.structure.info as info import biotite.structure.graphics as graphics # 'CA' is not in backbone, # as we want to include the rotation between 'CA' and 'CB' BACKBONE = ["N", "C", "O", "OXT"] LIBRARY_SIZE = 9 # Get the structure (including bonds) from the standard RCSB compound residue = info.residue("TYR") bond_list = residue.bonds ### Identify rotatable bonds ### rotatable_bonds = struc.find_rotatable_bonds(residue.bonds) # Do not rotate about backbone bonds, # as these are irrelevant for a amino rotamer library for atom_name in BACKBONE: index = np.where(residue.atom_name == atom_name)[0][0] rotatable_bonds.remove_bonds_to(index) print("Rotatable bonds in tyrosine:") for atom_i, atom_j, _ in rotatable_bonds.as_array(): print(residue.atom_name[atom_i] + " <-> " + residue.atom_name[atom_j]) ### VdW radii of each atom, required for the next step ### vdw_radii = np.zeros(residue.array_length())
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)
# 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 app.set_energy_range(100.0) # Start docking run app.start() app.join()
ELEMENT_FONT_SIZE = 10 # The scaling factor of the atom 'balls' BALL_SCALE = 20 # The higher this number, the more detailed are the rays N_RAY_STEPS = 20 # The scaling factor of the 'ray' of charged molecules RAY_SCALE = 100 # The transparency value for each 'ray ring' RAY_ALPHA = 0.03 # The color map to use to depict the charge CMAP_NAME = "bwr_r" # Get an atom array for the selected molecule molecule = info.residue(MOLECULE_NAME) # Align molecule with principal component analysis: # The component with the least variance, i.e. the axis with the lowest # number of atoms lying over each other, is aligned to the z-axis, # which points into the plane of the figure pca = PCA(n_components=3) pca.fit(molecule.coord) molecule = struc.align_vectors(molecule, pca.components_[-1], [0, 0, 1]) # Balls should be colored by partial charge charges = struc.partial_charges(molecule, ITERATION_NUMBER) # Later this variable stores values between 0 and 1 for use in color map normalized_charges = charges.copy() # Show no partial charge for atoms # that are not parametrized for the PEOE algorithm
This example displays the small molecule caffeine. """ # Code source: Patrick Kunzmann # License: BSD 3 clause import numpy as np import matplotlib.pyplot as plt from matplotlib.animation import FuncAnimation import biotite.structure as struc import biotite.structure.info as info import biotite.structure.graphics as graphics # Get an atom array for caffeine # Caffeine has the PDB reside name 'CFF' caffeine = info.residue("CFF") # For cosmetic purposes align central rings to x-y plane n1 = caffeine[caffeine.atom_name == "N1"][0] n3 = caffeine[caffeine.atom_name == "N3"][0] n7 = caffeine[caffeine.atom_name == "N7"][0] # Normal vector of ring plane normal = np.cross(n1.coord - n3.coord, n1.coord - n7.coord) # Align ring plane normal to z-axis caffeine = struc.align_vectors(caffeine, normal, np.array([0, 0, 1])) # Caffeine should be colored by element colors = np.zeros((caffeine.array_length(), 3)) colors[caffeine.element == "H"] = (0.8, 0.8, 0.8) # gray colors[caffeine.element == "C"] = (0.0, 0.8, 0.0) # green colors[caffeine.element == "N"] = (0.0, 0.0, 0.8) # blue
def assemble_peptide(sequence): res_names = [seq.ProteinSequence.convert_letter_1to3(r) for r in sequence] peptide = struc.AtomArray(length=0) for res_id, res_name, connect_angle in zip( np.arange(1, len(res_names) + 1), res_names, itertools.cycle([120, -120])): # Create backbone atom_n = struc.Atom([0.0, 0.0, 0.0], atom_name="N", element="N") atom_ca = struc.Atom([0.0, N_CA_LENGTH, 0.0], atom_name="CA", element="C") coord_c = calculate_atom_coord_by_z_rotation(atom_ca.coord, atom_n.coord, 120, CA_C_LENGTH) atom_c = struc.Atom(coord_c, atom_name="C", element="C") coord_o = calculate_atom_coord_by_z_rotation(atom_c.coord, atom_ca.coord, 120, C_O_DOUBLE_LENGTH) atom_o = struc.Atom(coord_o, atom_name="O", element="O") coord_h = calculate_atom_coord_by_z_rotation(atom_n.coord, atom_ca.coord, -120, N_H_LENGTH) atom_h = struc.Atom(coord_h, atom_name="H", element="H") backbone = struc.array([atom_n, atom_ca, atom_c, atom_o, atom_h]) backbone.res_id[:] = res_id backbone.res_name[:] = res_name # Add bonds between backbone atoms bonds = struc.BondList(backbone.array_length()) bonds.add_bond(0, 1, struc.BondType.SINGLE) # N-CA bonds.add_bond(1, 2, struc.BondType.SINGLE) # CA-C bonds.add_bond(2, 3, struc.BondType.DOUBLE) # C-O bonds.add_bond(0, 4, struc.BondType.SINGLE) # N-H backbone.bonds = bonds # Get residue from dataset residue = info.residue(res_name) # Superimpose backbone of residue # with backbone created previously _, transformation = struc.superimpose( backbone[struc.filter_backbone(backbone)], residue[struc.filter_backbone(residue)]) residue = struc.superimpose_apply(residue, transformation) # Remove backbone atoms from residue because they are already # existing in the backbone created prevoisly side_chain = residue[~np.isin( residue. atom_name, ["N", "CA", "C", "O", "OXT", "H", "H2", "H3", "HXT"])] # Assemble backbone with side chain (including HA) # and set annotation arrays residue = backbone + side_chain residue.bonds.add_bond( np.where(residue.atom_name == "CA")[0][0], np.where(residue.atom_name == "CB")[0][0], struc.BondType.SINGLE) residue.bonds.add_bond( np.where(residue.atom_name == "CA")[0][0], np.where(residue.atom_name == "HA")[0][0], struc.BondType.SINGLE) residue.chain_id[:] = "A" residue.res_id[:] = res_id residue.res_name[:] = res_name peptide += residue # Connect current residue to existing residues in the chain if res_id > 1: index_prev_ca = np.where((peptide.res_id == res_id - 1) & (peptide.atom_name == "CA"))[0][0] index_prev_c = np.where((peptide.res_id == res_id - 1) & (peptide.atom_name == "C"))[0][0] index_curr_n = np.where((peptide.res_id == res_id) & (peptide.atom_name == "N"))[0][0] index_curr_c = np.where((peptide.res_id == res_id) & (peptide.atom_name == "C"))[0][0] curr_residue_mask = peptide.res_id == res_id # Adjust geometry curr_coord_n = calculate_atom_coord_by_z_rotation( peptide.coord[index_prev_c], peptide.coord[index_prev_ca], connect_angle, C_N_LENGTH) peptide.coord[curr_residue_mask] -= peptide.coord[index_curr_n] peptide.coord[curr_residue_mask] += curr_coord_n # Adjacent residues should show in opposing directions # -> rotate residues with even residue ID by 180 degrees if res_id % 2 == 0: coord_n = peptide.coord[index_curr_n] coord_c = peptide.coord[index_curr_c] peptide.coord[curr_residue_mask] = struc.rotate_about_axis( atoms=peptide.coord[curr_residue_mask], axis=coord_c - coord_n, angle=np.deg2rad(180), support=coord_n) # Add bond between previous C and current N peptide.bonds.add_bond(index_prev_c, index_curr_n, struc.BondType.SINGLE) # Add N-terminal hydrogen atom_n = peptide[(peptide.res_id == 1) & (peptide.atom_name == "N")][0] atom_h = peptide[(peptide.res_id == 1) & (peptide.atom_name == "H")][0] coord_h2 = calculate_atom_coord_by_z_rotation(atom_n.coord, atom_h.coord, -120, N_H_LENGTH) atom_h2 = struc.Atom(coord_h2, chain_id="A", res_id=1, res_name=atom_h.res_name, atom_name="H2", element="H") peptide = struc.array([atom_h2]) + peptide peptide.bonds.add_bond(0, 1, struc.BondType.SINGLE) # H2-N # Add C-terminal hydroxyl group last_id = len(sequence) index_c = np.where((peptide.res_id == last_id) & (peptide.atom_name == "C"))[0][0] index_o = np.where((peptide.res_id == last_id) & (peptide.atom_name == "O"))[0][0] coord_oxt = calculate_atom_coord_by_z_rotation(peptide.coord[index_c], peptide.coord[index_o], connect_angle, C_O_LENGTH) coord_hxt = calculate_atom_coord_by_z_rotation(coord_oxt, peptide.coord[index_c], connect_angle, O_H_LENGTH) atom_oxt = struc.Atom(coord_oxt, chain_id="A", res_id=last_id, res_name=peptide[index_c].res_name, atom_name="OXT", element="O") atom_hxt = struc.Atom(coord_hxt, chain_id="A", res_id=last_id, res_name=peptide[index_c].res_name, atom_name="HXT", element="H") peptide = peptide + struc.array([atom_oxt, atom_hxt]) peptide.bonds.add_bond(index_c, -2, struc.BondType.SINGLE) # C-OXT peptide.bonds.add_bond(-2, -1, struc.BondType.SINGLE) # OXT-HXT return peptide
ampicillin. """ # Code source: Patrick Kunzmann # License: CC0 import biotite.structure as struc import biotite.structure.info as info import ammolite PNG_SIZE = (600, 600) ######################################################################## ampicillin = info.residue("AIC") pymol_obj = ammolite.PyMOLObject.from_structure(ampicillin) ammolite.show(PNG_SIZE) ######################################################################## ammolite.cmd.orient() ammolite.cmd.zoom(buffer=2.0) ammolite.show(PNG_SIZE) ######################################################################## pymol_obj.show("spheres") pymol_obj.color("black", ampicillin.element == "C") ammolite.cmd.set("stick_radius", 0.15)
mol_new = rotate_residue(mol, 0, theta * np.pi / 180) plot(mol_new, save_as=f"./plots/res_flex/{i}.png", show=False) thetas = np.linspace(0, 30, 60) thetas = np.append(thetas, np.linspace(30, -30, 30)) for j, theta in enumerate(thetas): mol_new = rotate_residue(mol, 1, theta * np.pi / 180) plot(mol_new, save_as=f"./plots/res_flex/{i+j}.png", show=False) acids = ['ala','arg','asn','asp','cys','gln','glu','his',\ 'leu','lys','met','phe','pyl','ser','sec','thr','trp','tyr',\ 'val'] chi = np.linspace(0, 359, 360) np.save("chi.npy", chi) for acid in acids: # --- get base residue --- residue = info.residue(acid.upper()) # --- create directory --- pth = f"./data/psi4files/{acid}" if not os.path.exists(pth): os.makedirs(pth) for i in range(len(chi)): res = rotate_residue(residue, 0, chi[i] * np.pi / 180) f = open(f"{pth}/{acid}_{chi[i]}.txt", "w") f.write(mkpsi4(res)) f.close()