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_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 test_molecule_iter(array, as_stack): """ Test whether :func:`molecule_iter()` gives the same molecules as pointed by :func:`get_molecule_indices()`. """ if as_stack: array = struc.stack([array]) ref_indices = struc.get_molecule_indices(array) test_iterator = struc.molecule_iter(array) for i, molecule in enumerate(test_iterator): assert molecule == array[..., ref_indices[i]]
def test_remove_pbc_selections(multi_model): """ This test makes no assertions, it only test whether an exception occurs, when the `selection` parameter is given in `remove_pbc()`. """ array = load_structure(join(data_dir("structure"), "3o5r.mmtf")) if multi_model: array = struc.stack([array, array]) struc.remove_pbc(array) struc.remove_pbc(array, array.chain_id[0]) struc.remove_pbc(array, struc.filter_amino_acids(array)) struc.remove_pbc( array, [struc.filter_amino_acids(array), (array.res_name == "FK5")]) # Expect error when selectinf an atom multiple times with pytest.raises(ValueError): struc.remove_pbc( array, [struc.filter_amino_acids(array), (array.atom_name == "CA")])
def test_get_molecule_masks(array, as_stack, as_bonds): """ Test whether the masks returned by :func:`get_molecule_masks()` point to the same atoms as the indices returned by :func:`get_molecule_indices()`. """ if as_stack: array = struc.stack([array]) if as_bonds: ref_indices = struc.get_molecule_indices(array.bonds) test_masks = struc.get_molecule_masks(array.bonds) else: ref_indices = struc.get_molecule_indices(array) test_masks = struc.get_molecule_masks(array) for i in range(len(test_masks)): # Assert that the mask is 'True' for all indices # and that these 'True' values are the only ones in the mask assert (test_masks[i, ref_indices[i]] == True).all() assert np.count_nonzero(test_masks[i]) == len(ref_indices[i])
def test_read_iter_structure(format, start, stop, step): """ Compare aggregated yields of :func:`read_iter_structure()` with the return value of :func:`get_structure()` from a corresponding :class:`TrajectoryFile` object. """ if format == "netcdf" and step is not None: # Currently, there is an inconsistency in in MDTraj's # NetCDFTrajectoryFile class: # In this class the number of frames in the output arrays # is dependent on the 'stride' parameter return template = strucio.load_structure(join(data_dir("structure"), "1l2y.mmtf")) if format == "trr": traj_file_cls = trr.TRRFile if format == "xtc": traj_file_cls = xtc.XTCFile if format == "tng": traj_file_cls = tng.TNGFile if format == "dcd": traj_file_cls = dcd.DCDFile if format == "netcdf": traj_file_cls = netcdf.NetCDFFile file_name = join(data_dir("structure"), f"1l2y.{format}") traj_file = traj_file_cls.read(file_name, start, stop, step) ref_traj = traj_file.get_structure(template) test_traj = struc.stack([ frame for frame in traj_file_cls.read_iter_structure( file_name, template, start, stop, step) ]) assert test_traj == ref_traj
def stack(array): return struc.stack([array, array.copy(), array.copy()])
# (multiple models in NMR structures, MD trajectories). # For the cases :class:`AtomArrayStack` objects are used, which # represent a list of atom arrays. # Since the atoms are the same for each frame, but only the coordinates # change, the annotation arrays in stacks are still the same length *n* # :class:`ndarray` objects as in atom arrays. # However, a stack stores the coordinates in a *(m,n,3)*-shaped # :class:`ndarray`, where *m* is the number of frames. # A stack is constructed with :func:`stack()` analogous to the code # snipped above. # It is crucial that all arrays that should be stacked # have the same annotation arrays, otherwise an exception is raised. # For simplicity reasons, we create a stack containing two identical # models, derived from the previous example. stack = struc.stack([array, array.copy()]) print(stack) ######################################################################## # Loading structures from file # ---------------------------- # # Usually structures are not built from scratch in *Biotite*, # but they are read from a file. # Probably the most popular strcuture file format is the *PDB* format. # For our purpose, we will work on a protein structure as small as # possible, namely the miniprotein *TC5b* (PDB: ``1L2Y```). # The structure of this 20-residue protein (304 atoms) has been # elucidated via NMR. # Thus, the corresponding PDB file consists of multiple (namely 38) # models, each showing another conformation.