def assert_equal_matrices(array, matrix1, matrix2, periodic): """ Due to numerical instability, entries in both matrices might be different, when the distance of atoms is almost equal to the cutoff distance of the matrix. This function checks, whether two atoms with unequal entries in the matrices are near the cutoff distance. """ nonlocal CUTOFF indices = np.where(matrix1 != matrix2) for index in range(len(indices[0])): if len(indices) == 2: # multi_model = False -> AtomArray m = None i = indices[0][index] j = indices[1][index] box = array.box if periodic else None distance = struc.distance(array[i], array[j], box=box) if len(indices) == 3: # multi_model = True -> AtomArrayStack m = indices[0][index] i = indices[1][index] j = indices[2][index] box = array.box[m] if periodic else None distance = struc.distance(array[m, i], array[m, j], box=box) try: assert distance == pytest.approx(CUTOFF, abs=1e-4) except AssertionError: print(f"Model {m}, Atoms {i} and {j}") raise
def test_masked_superimposition(seed): """ Take two models of the same structure and superimpose based on a single, randomly chosen atom. Since two atoms can be superimposed perfectly, the distance between the atom in both models should be 0. """ path = join(data_dir, "1l2y.mmtf") fixed = strucio.load_structure(path, model=1) mobile = strucio.load_structure(path, model=2) # Create random mask for a single atom np.random.seed(seed) mask = np.full(fixed.array_length(), False) mask[np.random.randint(fixed.array_length())] = True # The distance between the atom in both models should not be # already 0 prior to superimposition assert struc.distance(fixed[mask], mobile[mask])[0] \ != pytest.approx(0, abs=5e-4) fitted, transformation = struc.superimpose(fixed, mobile, mask) assert struc.distance(fixed[mask], fitted[mask])[0] \ == pytest.approx(0, abs=5e-4) fitted = struc.superimpose_apply(mobile, transformation) struc.distance(fixed[mask], fitted[mask])[0] \ == pytest.approx(0, abs=5e-4)
def membrane_thickness(upper_atoms, lower_atoms): """ Calculate membrane thickness. For each atom, the algorithm calculates the distance to the closest atom in the other leaflet. Parameters ---------- upper_atoms, lower_atoms : AtomArray or AtomArrayStack atoms of the lower and upper membrane leaflet to use for distance calculation Returns ------- ndarray For each frame, a set of x and y coordinates and the corresponding membrane thickness is returned """ if not isinstance(lower_atoms, AtomArrayStack): lower_atoms = stack([lower_atoms]) if not isinstance(upper_atoms, AtomArrayStack): upper_atoms = stack([upper_atoms]) thickness_upper = np.full( [len(upper_atoms), upper_atoms.array_length(), 3], 0.0) thickness_lower = np.full( [len(lower_atoms), lower_atoms.array_length(), 3], 0.0) for idx in range(upper_atoms.array_length()): atom = upper_atoms[:, idx] dist = struct.distance(atom, lower_atoms) atom_min_dist = dist.min(axis=1) thickness_upper[:, idx, 0:2] = atom.coord[:, 0, 0:2] thickness_upper[:, idx, -1] = atom_min_dist for idx in range(lower_atoms.array_length()): atom = lower_atoms[:, idx] dist = struct.distance(atom, upper_atoms) atom_min_dist = dist.min(axis=1) thickness_lower[:, idx, 0:2] = atom.coord[:, 0, 0:2] thickness_lower[:, idx, -1] = atom_min_dist thickness = np.concatenate([thickness_upper, thickness_lower], axis=1) if thickness.shape[0] == 1: return thickness[0, ...] else: return thickness
def test_index_functions(): """ The `index_xxx()` functions should give the same result as the corresponding `xxx` functions. """ stack = strucio.load_structure(join(data_dir, "1l2y.mmtf")) array = stack[0] # Test for atom array, stack and raw coordinates samples = (array, stack, struc.coord(array), struc.coord(stack)) # Generate random indices random.seed(42) indices = random.randint(array.array_length(), size=(100, 4), dtype=int) for sample in samples: if isinstance(sample, np.ndarray): atoms1 = sample[..., indices[:, 0], :] atoms2 = sample[..., indices[:, 1], :] atoms3 = sample[..., indices[:, 2], :] atoms4 = sample[..., indices[:, 3], :] else: atoms1 = sample[..., indices[:, 0]] atoms2 = sample[..., indices[:, 1]] atoms3 = sample[..., indices[:, 2]] atoms4 = sample[..., indices[:, 3]] assert np.allclose(struc.displacement(atoms1, atoms2), struc.index_displacement(sample, indices[:, :2]), atol=1e-5) assert np.allclose(struc.distance(atoms1, atoms2), struc.index_distance(sample, indices[:, :2]), atol=1e-5) assert np.allclose(struc.angle(atoms1, atoms2, atoms3), struc.index_angle(sample, indices[:, :3]), atol=1e-5) assert np.allclose(struc.dihedral(atoms1, atoms2, atoms3, atoms4), struc.index_dihedral(sample, indices[:, :4]), atol=1e-5)
def detect_disulfide_bonds(structure, distance=2.05, distance_tol=0.05, dihedral=90, dihedral_tol=10): # Array where detected disulfide bonds are stored disulfide_bonds = [] # A mask that selects only S-gamma atoms of cysteins sulfide_mask = (structure.res_name == "CYS") & \ (structure.atom_name == "SG") # sulfides in adjacency to other sulfides are detected in an # efficient manner via a cell list cell_list = struc.CellList(structure, cell_size=distance + distance_tol, selection=sulfide_mask) # Iterate over every index corresponding to an S-gamma atom for sulfide_i in np.where(sulfide_mask)[0]: # Find indices corresponding to other S-gamma atoms, # that are adjacent to the position of structure[sulfide_i] # We use the faster 'get_atoms_in_cells()' instead of # `get_atoms()`, as precise distance measurement is done # afterwards anyway potential_bond_partner_indices = cell_list.get_atoms_in_cells( coord=structure.coord[sulfide_i]) # Iterate over every index corresponding to an S-gamma atom # as bond partner for sulfide_j in potential_bond_partner_indices: if sulfide_i == sulfide_j: # A sulfide cannot create a bond with itself: continue # Create 'Atom' instances # of the potentially bonds S-gamma atoms sg1 = structure[sulfide_i] sg2 = structure[sulfide_j] # For dihedral angle measurement the corresponding # C-beta atoms are required, too cb1 = structure[(structure.chain_id == sg1.chain_id) & (structure.res_id == sg1.res_id) & (structure.atom_name == "CB")] cb2 = structure[(structure.chain_id == sg2.chain_id) & (structure.res_id == sg2.res_id) & (structure.atom_name == "CB")] # Measure distance and dihedral angle and check criteria bond_dist = struc.distance(sg1, sg2) bond_dihed = np.abs(np.rad2deg(struc.dihedral(cb1, sg1, sg2, cb2))) if bond_dist > distance - distance_tol and \ bond_dist < distance + distance_tol and \ bond_dihed > dihedral - dihedral_tol and \ bond_dihed < dihedral + dihedral_tol: # Atom meet criteria -> we found a disulfide bond # -> the indices of the bond S-gamma atoms # are put into a tuple with the lower index first bond_tuple = sorted((sulfide_i, sulfide_j)) # Add bond to list of bonds, but each bond only once if bond_tuple not in disulfide_bonds: disulfide_bonds.append(bond_tuple) return np.array(disulfide_bonds, dtype=int)
def test_index_distance_non_periodic(): """ Without PBC the result should be equal to the normal distance calculation. """ array = strucio.load_structure(join(data_dir, "3o5r.mmtf")) ref_dist = struc.distance(array.coord[np.newaxis, :, :], array.coord[:, np.newaxis, :]).flatten() length = array.array_length() dist = struc.index_distance(array, indices=np.stack([ np.repeat(np.arange(length), length), np.tile(np.arange(length), length) ], axis=1)) assert np.allclose(dist, ref_dist)
root=atom_i) accepted = False while not accepted: # A random angle between 0 and 360 degrees angle = np.random.rand() * 2 * np.pi # Rotate coord[rotated_atom_indices] = struc.rotate_about_axis( coord[rotated_atom_indices], axis, angle, support) # Check if the atoms clash with each other: # The distance between each pair of atoms must be larger # 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,
def extractInterface(dimer, c1, c2, vdwC, minC, nearC, mem): """ This function extracts interfaces from provided dimers, by chain ids. """ if os.path.exists("cache.hdf5"): os.remove("cache.hdf5") vdw_dict = {'ALA': {'N': 1.83, 'H': 0.8, 'CA': 2.265, 'CB': 2.165, 'C': 1.87, 'O': 1.55}, 'ARG': {'N': 1.83, 'H': 0.8, 'CA': 2.265, 'CB': 2.235, 'CG': 2.235, 'CD': 2.235, 'NE': 1.83, 'HE': 0.8, 'CZ': 1.87, 'NH1': 1.83, 'HH11': 0.6, 'HH12': 0.6, 'NH2': 1.83, 'HH21': 0.6, 'HH22': 0.6, 'C': 1.87, 'O': 1.55}, 'ARGN': {'N': 1.83, 'H': 0.8, 'CA': 2.265, 'CB': 2.235, 'CG': 2.235, 'CD': 2.235, 'NE': 1.83, 'HE': 0.8, 'CZ': 1.87, 'NH1': 1.83, 'HH11': 0.6, 'HH12': 0.6, 'NH2': 1.83, 'HH21': 0.6, 'C': 1.87, 'O': 1.55}, 'ASN': {'N': 1.83, 'H': 0.8, 'CA': 2.265, 'CB': 2.235, 'CG': 1.87, 'OD1': 1.55, 'ND2': 1.83, 'HD21': 0.8, 'HD22': 0.8, 'C': 1.87, 'O': 1.55, 'AD1': 1.55, 'AD2': 1.83}, 'ASP': {'N': 1.83, 'H': 0.8, 'CA': 2.265, 'CB': 2.235, 'CG': 1.87, 'OD1': 1.66, 'OD2': 1.66, 'C': 1.87, 'O': 1.55}, 'ASPH': {'N': 1.83, 'H': 0.8, 'CA': 2.265, 'CB': 2.235, 'CG': 1.87, 'OD1': 1.52, 'OD2': 1.55, 'HD': 0.8, 'C': 1.87, 'O': 1.55}, 'CYS': {'N': 1.83, 'H': 0.8, 'CA': 2.265, 'CB': 2.235, 'SG': 1.89, 'C': 1.87, 'O': 1.55}, 'GLN': {'N': 1.83, 'H': 0.8, 'CA': 2.265, 'CB': 2.235, 'CG': 2.235, 'CD': 1.87, 'OE1': 1.55, 'NE2': 1.83, 'HE21': 0.8, 'HE22': 0.8, 'C': 1.87, 'O': 1.55, 'AE1': 1.55, 'AE2': 1.83}, 'GLU': {'N': 1.83, 'H': 0.8, 'CA': 2.265, 'CB': 2.235, 'CG': 2.235, 'CD': 1.87, 'OE1': 1.66, 'OE2': 1.66, 'C': 1.87, 'O': 1.55}, 'GLUH': {'N': 1.83, 'H': 0.8, 'CA': 2.265, 'CB': 2.235, 'CG': 2.235, 'CD': 1.87, 'OE1': 1.52, 'OE2': 1.55, 'HE': 0.8, 'C': 1.87, 'O': 1.55}, 'GLY': {'N': 1.83, 'H': 0.8, 'CA': 2.235, 'C': 1.87, 'O': 1.55}, 'HIS': {'N': 1.83, 'H': 0.8, 'CA': 2.265, 'CB': 2.235, 'CG': 2.04, 'ND1': 1.72, 'HD1': 0.8, 'CD2': 2.1, 'NE2': 1.72, 'CE1': 2.1, 'C': 1.87, 'O': 1.55, 'AE1': 2.1, 'AE2': 1.72}, 'ILE': {'N': 1.83, 'H': 0.8, 'CA': 2.265, 'CB': 2.265, 'CG2': 2.165, 'CG1': 2.235, 'CD1': 2.165, 'C': 1.87, 'O': 1.55, 'CD': 2.165}, 'LEU': {'N': 1.83, 'H': 0.8, 'CA': 2.265, 'CB': 2.235, 'CG': 2.265, 'CD1': 2.165, 'CD2': 2.165, 'C': 1.87, 'O': 1.55}, 'LYS': {'N': 1.83, 'H': 0.8, 'CA': 2.265, 'CB': 2.235, 'CG': 2.235, 'CD': 2.235, 'CE': 2.235, 'NZ': 1.65, 'HZ': 0.6, 'HZ1': 0.6, 'HZ2': 0.6, 'HZ3': 0.6, 'C': 1.87, 'O': 1.55}, 'LYSN': {'N': 1.83, 'H': 0.8, 'CA': 2.265, 'CB': 2.235, 'CG': 2.235, 'CD': 2.235, 'CE': 2.235, 'NZ': 1.65, 'HZ1': 0.8, 'HZ2': 0.8, 'C': 1.87, 'O': 1.55}, 'MET': {'N': 1.83, 'H': 0.8, 'CA': 2.265, 'CB': 2.235, 'CG': 2.235, 'SD': 1.97, 'CE': 2.165, 'C': 1.87, 'O': 1.55}, 'PHE': {'N': 1.83, 'H': 0.8, 'CA': 2.265, 'CB': 2.235, 'CG': 2.04, 'CD1': 1.99, 'CD2': 1.99, 'CE1': 1.99, 'CE2': 1.99, 'CZ': 1.99, 'C': 1.87, 'O': 1.55}, 'PRO': {'N': 1.83, 'CD': 2.235, 'CA': 2.265, 'CB': 2.235, 'CG': 2.235, 'C': 1.87, 'O': 1.55}, 'SER': {'N': 1.83, 'H': 0.8, 'CA': 2.265, 'CB': 2.235, 'OG': 1.55, 'HG': 0.76, 'C': 1.87, 'O': 1.55}, 'THR': {'N': 1.83, 'H': 0.8, 'CA': 2.265, 'CB': 2.265, 'OG1': 1.55, 'HG1': 0.76, 'CG2': 2.165, 'C': 1.87, 'O': 1.55}, 'TRP': {'N': 1.83, 'H': 0.8, 'CA': 2.265, 'CB': 2.235, 'CG': 2.04, 'CD2': 2.04, 'CE2': 2.04, 'CE3': 1.99, 'CD1': 2.1, 'NE1': 1.72, 'HE1': 0.8, 'CZ2': 1.99, 'CZ3': 1.99, 'CH2': 1.99, 'C': 1.87, 'O': 1.55}, 'TYR': {'N': 1.83, 'H': 0.8, 'CA': 2.265, 'CB': 2.235, 'CG': 2.04, 'CD1': 1.99, 'CE1': 1.99, 'CD2': 1.99, 'CE2': 1.99, 'CZ': 2.04, 'OH': 1.55, 'HH': 0.76, 'C': 1.87, 'O': 1.55}, 'VAL': {'N': 1.83, 'H': 0.8, 'CA': 2.265, 'CB': 2.265, 'CG1': 2.165, 'CG2': 2.165, 'C': 1.87, 'O': 1.55}, 'HSC': {'N': 1.83, 'H': 0.8, 'CA': 2.265, 'CB': 2.235, 'CG': 2.04, 'CD2': 2.1, 'ND1': 1.72, 'HD1': 0.8, 'CE1': 2.1, 'NE2': 1.72, 'HE2': 0.8, 'C': 1.87, 'O': 1.55}, 'HSD': {'N': 1.83, 'H': 0.8, 'CA': 2.265, 'CB': 2.235, 'CG': 2.04, 'ND1': 1.72, 'CE1': 2.1, 'CD2': 2.1, 'NE2': 1.72, 'HE2': 0.8, 'C': 1.87, 'O': 1.55}, 'ACE': {'C': 1.87, 'O': 1.55, 'CH3': 2.165}} default_dict = {'N': 1.83, 'H': 0.8, 'CA': 2.265, 'CB': 2.265, 'C': 1.87, 'O': 1.55, 'CG': 2.265, 'CD': 2.235, 'NE': 1.83, 'HE': 0.8, 'C1': 1.87, 'C2': 1.87, 'C3': 1.87, 'CZ': 2.04, 'NH1': 1.83, 'H1': 0.8, 'H2': 0.8, 'H3': 0.8, 'H11': 0.8, 'H12': 0.8, 'H31': 0.8, 'H32': 0.8, 'HA' : 0.8, 'HA2' : 0.8, 'HA3' : 0.8, 'HB' : 0.8, 'HB1' : 0.8, 'HB2' : 0.8, 'HB3' : 0.8, 'HH2': 0.6, 'HH11': 0.6, 'HH12': 0.6, 'NH2': 1.83, 'HH21': 0.6, 'HH22': 0.6, 'OD1': 1.66, 'ND2': 1.83, 'HD21': 0.8, 'HD22': 0.8, 'AD1': 1.55, 'AD2': 1.83, 'OD2': 1.66, 'HD': 0.8, 'SG': 1.89, 'OE1': 1.66, 'NE2': 1.83, 'HE2': 0.8, 'HE3': 0.8, 'HE21': 0.8, 'HE22': 0.8, 'AE1': 2.1, 'AE2': 1.83, 'OE2': 1.66, 'ND1': 1.72, 'HD1': 0.8, 'HD2': 0.8, 'HD3': 0.8, 'HD11': 0.8, 'HD12': 0.8, 'HD13': 0.8, 'HD23': 0.8, 'CD2': 2.165, 'CE1': 2.1, 'CG2': 2.165, 'CG1': 2.235, 'CD1': 2.165, 'CE': 2.235, 'NZ': 1.65, 'HZ': 0.6, 'HZ1': 0.8, 'HZ2': 0.8, 'HZ3': 0.6, 'SD': 1.97, 'CE2': 2.04, 'OG': 1.55, 'HG': 0.76, 'OG1': 1.55, 'HG1': 0.76, 'HG2': 0.76, 'HG3': 0.76, 'HG11': 0.76, 'HG12': 0.76, 'HG13': 0.76, 'HG21': 0.76, 'HG22': 0.76, 'HG23': 0.76, 'CE3': 1.99, 'NE1': 1.72, 'HE1': 0.8, 'HO1': 0.8, 'HO2': 0.8, 'HO3': 0.8, 'CZ2': 1.99, 'CZ3': 1.99, 'CH2': 1.99, 'OH': 1.55, 'OXT': 1.55, 'O1': 1.55, 'O2': 1.55, 'O3': 1.55, 'HH': 0.76, 'HE2': 0.8, 'CH3': 2.165} no_digit_dict = {'N': 1.83, 'H': 0.8, 'CA': 2.265, 'CB': 2.265, 'C': 1.87, 'O': 1.55, 'CG': 2.265, 'CD': 2.235, 'NE': 1.83, 'HE': 0.8, 'CZ': 2.04, 'NH': 1.83, 'HA': 0.8, 'HB': 0.8, 'HH': 0.76, 'OD': 1.66, 'ND': 1.83, 'HD': 0.8, 'AD': 1.83, 'SG': 1.89, 'OE': 1.66, 'AE': 2.1, 'CE': 2.235, 'NZ': 1.65, 'HZ': 0.8, 'SD': 1.97, 'OG': 1.55, 'HG': 0.76, 'HO': 0.8, 'CH': 2.165, 'OH': 1.55, 'OXT': 1.55, 'D': 0.8, 'DZ': 0.8, 'HXT': 0.8, 'HAB': 0.8} chain_1 = dimer[dimer.chain_id == c1] chain_2 = dimer[dimer.chain_id == c2] chain_1_coord = chain_1.coord chain_2_coord = chain_2.coord try: chain_1_vdw = [float(vdw_dict[chain_1[i].res_name][chain_1[i].atom_name]) for i in range(len(chain_1))] except: chain_1_vdw = [] for i in range(len(chain_1)): if chain_1[i].res_name in vdw_dict and chain_1[i].atom_name in vdw_dict[chain_1[i].res_name]: chain_1_vdw.append(float(vdw_dict[chain_1[i].res_name][chain_1[i].atom_name])) elif chain_1[i].atom_name in default_dict: chain_1_vdw.append(float(default_dict[chain_1[i].atom_name])) else: chain_1_vdw.append(float(no_digit_dict[chain_1[''.join([j for j in chain_1[i].atom_name if not j.isdigit()])].atom_name])) try: chain_2_vdw = [float(vdw_dict[chain_2[i].res_name][chain_2[i].atom_name]) for i in range(len(chain_2))] except: chain_2_vdw = [] for i in range(len(chain_2)): if chain_2[i].res_name in vdw_dict and chain_2[i].atom_name in vdw_dict[chain_2[i].res_name]: chain_2_vdw.append(float(vdw_dict[chain_2[i].res_name][chain_2[i].atom_name])) elif chain_2[i].atom_name in default_dict: chain_2_vdw.append(float(default_dict[chain_2[i].atom_name])) else: chain_2_vdw.append(float(no_digit_dict[chain_2[''.join([j for j in chain_1[i].atom_name if not j.isdigit()])].atom_name])) if len(chain_1) * len(chain_2) * 8 > mem * (1024**3)/3: hdf5_file = True hdf5_store = h5py.File("cache.hdf5", "a") critdist_array = hdf5_store.create_dataset("critdist_array", (len(chain_1), len(chain_2))) n = len(chain_2) chunks = 10 chunk_size = [[i * int(n / chunks), (i + 1) * int(n / chunks)] if i + 1 != chunks else [i * int(n / chunks), n] for i in range(chunks)] for start, end in chunk_size: critdist_array[:, start:end] = np.array(chain_2_vdw[start:end]) + np.array(chain_1_vdw).reshape((-1, 1)) + vdwC realdist_array = hdf5_store.create_dataset('realdist_array', data=cdist(chain_1_coord, chain_2_coord, 'euclidean')) contacting_bool = np.zeros(realdist_array.shape, dtype=bool) n = len(realdist_array) chunks = 500 chunk_size = [[i * int(n / chunks), (i + 1) * int(n / chunks)] if i + 1 != chunks else [i * int(n / chunks), n] for i in range(chunks)] for start, end in chunk_size: contacting_bool[start:end, :] = realdist_array[start:end] <= critdist_array[start:end] contacting_1 = np.unique(chain_1[np.any(contacting_bool, axis=1)].res_id) contacting_2 = np.unique(chain_2[np.any(contacting_bool, axis=0)].res_id) else: hdf5_file = False realdist_array = cdist(chain_1_coord, chain_2_coord, 'euclidean') critdist_array = np.array(chain_2_vdw) + np.array(chain_1_vdw).reshape((-1, 1)) + vdwC contacting_1 = np.unique(chain_1[np.any(realdist_array <= critdist_array, axis=1)].res_id) contacting_2 = np.unique(chain_2[np.any(realdist_array <= critdist_array, axis=0)].res_id) if len(contacting_1) < minC or len(contacting_2) < minC: if hdf5_file is True: hdf5_store.close() os.remove("cache.hdf5") return None contacting = dimer[np.logical_or(np.logical_and(dimer.chain_id == c1, np.isin(dimer.res_id, contacting_1)), np.logical_and(dimer.chain_id == c2, np.isin(dimer.res_id, contacting_2)))] contacting_ca = contacting[contacting.atom_name == 'CA'] dimer_ca = dimer[dimer.atom_name == 'CA'] pos_nearby_ca = dimer_ca[~struc.filter_intersection(dimer_ca, contacting_ca)] nearby_array = np.zeros((len(pos_nearby_ca), len(contacting_ca))) for i in range(len(pos_nearby_ca)): nearby_array[i] = struc.distance(pos_nearby_ca[i], contacting_ca) nearby_ca = pos_nearby_ca[np.any(nearby_array <= nearC, axis = 1)] nearby_1 = nearby_ca[nearby_ca.chain_id == c1].res_id nearby_2 = nearby_ca[nearby_ca.chain_id == c2].res_id nearby = dimer[np.logical_or(np.logical_and(dimer.chain_id == c1, np.isin(dimer.res_id, nearby_1)), np.logical_and(dimer.chain_id == c2, np.isin(dimer.res_id, nearby_2)))] interface_1 = sorted(list(contacting_1) + list(nearby_1)) interface_2 = sorted(list(contacting_2) + list(nearby_2)) interface = dimer[np.logical_or(np.logical_and(dimer.chain_id == c1, np.isin(dimer.res_id, interface_1)), np.logical_and(dimer.chain_id == c2, np.isin(dimer.res_id, interface_2)))] if hdf5_file is True: hdf5_store.close() os.remove("cache.hdf5") return ", ".join(map(str, contacting_1)), ", ".join(map(str, contacting_2)), ", ".join(map(str, nearby_1)), ", ".join(map(str, nearby_2)), interface
def test_distance(): coord1 = struc.coord([0, 1, 1]) coord2 = struc.coord([0, 2, 2]) assert struc.distance(coord1, coord2) == pytest.approx(np.sqrt(2))
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)
# Style protein protein_mask = struc.filter_amino_acids(cyt_c) pymol_cyt_c.show_as("cartoon", protein_mask) pymol_cyt_c.color("lightgreen", protein_mask & (cyt_c.element == "C")) #----------------------------------------------------------------------# # Style heme group heme_mask = (cyt_c.res_name == "HEM") pymol_cyt_c.show_as("sticks", heme_mask) pymol_cyt_c.color("lightorange", heme_mask & (cyt_c.element == "C")) #----------------------------------------------------------------------# # Mark the histidine bound to heme as sticks heme_iron_coord = cyt_c[cyt_c.element == "FE"].coord[0] adjacency_mask = (struc.distance(cyt_c.coord, heme_iron_coord) < 2.0) bound_nitrogen_mask = adjacency_mask & (cyt_c.res_name == "HIS") his_mask = (cyt_c.res_id == cyt_c.res_id[bound_nitrogen_mask]) pymol_cyt_c.show("sticks", his_mask) pymol_cyt_c.color( "darkgreen", his_mask & (cyt_c.element == "C") & (cyt_c.atom_name != "CA")) #----------------------------------------------------------------------# # Zoom into binding site pymol_cyt_c.zoom(heme_mask | his_mask, buffer=1.0)
# Geometry measures # ^^^^^^^^^^^^^^^^^ # # Let's start with measuring some simple geometric characteristics, # for example atom distances of CA atoms. import biotite.structure as struc import biotite.structure.io as strucio import biotite.database.rcsb as rcsb file_path = rcsb.fetch("1l2y", "mmtf", biotite.temp_dir()) stack = strucio.load_structure(file_path) # Filter only CA atoms stack = stack[:, stack.atom_name == "CA"] # Calculate distance between first and second CA in first frame array = stack[0] print("Atom to atom:", struc.distance(array[0], array[1])) # Calculate distance between the first atom # and all other CA atoms in the array print("Array to atom:") array = stack[0] print(struc.distance(array[0], array)) # Calculate pairwise distances between the CA atoms in the first frame # and the CA atoms in the second frame print("Array to array:") print(struc.distance(stack[0], stack[1])) # Calculate the distances between all CA atoms in the stack # and the first CA atom in the first frame # The resulting array is too large, therefore only the shape is printed print("Stack to atom:") print(struc.distance(stack, stack[0, 0]).shape) # And finally distances between two adjacent CA in the first frame