All Downloads are FREE. Search and download functionalities are using the official Maven repository.

pl.poznan.put.pdb.analysis.PdbResidue Maven / Gradle / Ivy

package pl.poznan.put.pdb.analysis;

import org.apache.commons.collections4.SetUtils;
import org.apache.commons.math3.geometry.euclidean.threed.Plane;
import pl.poznan.put.atom.AtomName;
import pl.poznan.put.pdb.ChainNumberICode;
import pl.poznan.put.pdb.ImmutablePdbNamedResidueIdentifier;
import pl.poznan.put.pdb.PdbAtomLine;
import pl.poznan.put.pdb.PdbNamedResidueIdentifier;
import pl.poznan.put.pdb.PdbResidueIdentifier;
import pl.poznan.put.rna.Nucleotide;

import java.util.Collection;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;

/** A residue (nucleotide or amino acid). */
public interface PdbResidue extends ChainNumberICode {
  /** @return The identifier of a residue. */
  PdbResidueIdentifier identifier();

  /**
   * @return The usual name of the residue. For example, a pseudouridine will be seen as uracil
   *     here.
   */
  String standardResidueName();

  /** @return The name of the residue as read from PDB or mmCIF file. */
  String modifiedResidueName();

  /** @return The list of atoms. */
  List atoms();

  /** @return A text representation of this residue in PDB format. */
  default String toPdb() {
    return atoms().stream().map(String::valueOf).collect(Collectors.joining("\n"));
  }

  /** @return A text representation of this residue in mmCIF format. */
  default String toCif() {
    return atoms().stream().map(PdbAtomLine::toCif).collect(Collectors.joining("\n"));
  }

  /** @return True if the list of atoms is empty. */
  default boolean isMissing() {
    return atoms().isEmpty();
  }

  /**
   * Detects the type of residue by its name and atom content.
   *
   * @return An instance with details about what this reside represents.
   */
  default ResidueInformationProvider residueInformationProvider() {
    return ResidueTypeDetector.detectResidueType(modifiedResidueName(), atomNames());
  }

  /** @return A one letter name which is lowercase for modified residues and uppercase otherwise. */
  default char oneLetterName() {
    return isModified()
        ? Character.toLowerCase(residueInformationProvider().oneLetterName())
        : residueInformationProvider().oneLetterName();
  }

  /**
   * Checks if this residue is connected with another one (see {@link
   * MoleculeType#areConnected(PdbResidue, PdbResidue)}).
   *
   * @param other The other residue.
   * @return True if this residue and the other one are connected.
   */
  default boolean isConnectedTo(final PdbResidue other) {
    return residueInformationProvider().moleculeType().areConnected(this, other);
  }

  @Override
  default String chainIdentifier() {
    return identifier().chainIdentifier();
  }

  @Override
  default int residueNumber() {
    return identifier().residueNumber();
  }

  @Override
  default String insertionCode() {
    return identifier().insertionCode();
  }

  /**
   * Finds an atom of a given name.
   *
   * @param atomName An atom name.
   * @return An instance of atom (with coordinates) of the given type.
   */
  default PdbAtomLine findAtom(final AtomName atomName) {
    return atoms().stream()
        .filter(atom -> atom.detectAtomName() == atomName)
        .findFirst()
        .orElseThrow(() -> new IllegalArgumentException("Failed to find: " + atomName));
  }

  /**
   * Calculates the plane equation of the nucleobase in this residue. This method works only for
   * nucleotides and will throw an {@code IllegalArgumentException} for amino acids. The plane for
   * purines (A or G) is based on N9, C2 and C6 atoms. The plane for pyrimidines (C, U or T) is
   * based on N1, N3 and C5 atoms.
   *
   * @return The plane of the nucleobase.
   */
  default Plane nucleobasePlane() {
    if (residueInformationProvider() instanceof Nucleotide) {
      switch ((Nucleotide) residueInformationProvider()) {
        case ADENINE:
        case GUANINE:
          return new Plane(
              findAtom(AtomName.N9).toVector3D(),
              findAtom(AtomName.C2).toVector3D(),
              findAtom(AtomName.C6).toVector3D(),
              1.0e-3);
        case CYTOSINE:
        case URACIL:
        case THYMINE:
          return new Plane(
              findAtom(AtomName.N1).toVector3D(),
              findAtom(AtomName.N3).toVector3D(),
              findAtom(AtomName.C5).toVector3D(),
              1.0e-3);
      }
    }

    throw new IllegalArgumentException(
        "Cannot compute base plane for not a nucleotide: " + identifier());
  }

  /** @return The instance of named identifier. */
  default PdbNamedResidueIdentifier namedResidueIdentifer() {
    return ImmutablePdbNamedResidueIdentifier.of(
        identifier().chainIdentifier(),
        identifier().residueNumber(),
        identifier().insertionCode(),
        isModified() ? Character.toLowerCase(oneLetterName()) : oneLetterName());
  }

  /** @return The set of all atom names available in this residue. */
  default Set atomNames() {
    return atoms().stream().map(PdbAtomLine::detectAtomName).collect(Collectors.toSet());
  }

  /**
   * Checks whether this residue has atom of the given name.
   *
   * @param atomName The atom name.
   * @return True if this residue has an atom of the given name.
   */
  default boolean hasAtom(final AtomName atomName) {
    return atomNames().contains(atomName);
  }

  /** @return True if there is any hydrogen atom available in this residue. */
  default boolean hasAnyHydrogen() {
    return atomNames().stream().anyMatch(atomName -> !atomName.isHeavy());
  }

  /**
   * Compares the set of actual atoms in this residue with the set of expected atoms derived from
   * the detected type of residue.
   *
   * @return True if all expected heavy atoms (non-hydrogen) for this residue type are present in
   *     this residue.
   */
  default boolean hasAllHeavyAtoms() {
    final Set heavyAtoms =
        atomNames().stream().filter(AtomName::isHeavy).collect(Collectors.toSet());
    final Set expectedHeavyAtoms =
        residueInformationProvider().moleculeComponents().stream()
            .map(ResidueComponent::requiredAtoms)
            .flatMap(Collection::stream)
            .filter(AtomName::isHeavy)
            .collect(Collectors.toSet());
    return SetUtils.isEqualSet(heavyAtoms, expectedHeavyAtoms);
  }

  /**
   * @return True if the standard and modified residue names differ or if there are some atoms in
   *     the residue, but they do not match the expected set of atoms.
   */
  default boolean isModified() {
    return !Objects.equals(standardResidueName(), modifiedResidueName())
        || (!isMissing() && !hasAllHeavyAtoms());
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy